commit f55475a23f84562ec3e1dfaf42e4b134e4de8667 Author: Christian Cunat-Brulé <32840852+DARKNAGAN@users.noreply.github.com> Date: Mon Jul 23 10:52:48 2018 +0200 CCB Premier Import diff --git a/.metadata/.lock b/.metadata/.lock new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.log b/.metadata/.log new file mode 100644 index 0000000..c433ccc --- /dev/null +++ b/.metadata/.log @@ -0,0 +1,28 @@ +!SESSION 2018-05-18 10:48:43.985 ----------------------------------------------- +eclipse.buildId=4.7.3.M20180330-0640 +java.version=1.8.0_171 +java.vendor=Oracle Corporation +BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=fr_FR +Framework arguments: -product org.eclipse.epp.package.java.product +Command-line arguments: -os win32 -ws win32 -arch x86_64 -product org.eclipse.epp.package.java.product + +!ENTRY org.eclipse.egit.ui 2 0 2018-05-18 10:49:48.484 +!MESSAGE Warning: The environment variable HOME is not set. The following directory will be used to store the Git +user global configuration and to define the default location to store repositories: 'C:\Users\chris'. If this is +not correct please set the HOME environment variable and restart Eclipse. Otherwise Git for Windows and +EGit might behave differently since they see different configuration options. +This warning can be switched off on the Team > Git > Confirmations and Warnings preference page. +!SESSION 2018-05-20 13:44:07.411 ----------------------------------------------- +eclipse.buildId=4.7.3.M20180330-0640 +java.version=1.8.0_171 +java.vendor=Oracle Corporation +BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=fr_FR +Framework arguments: -product org.eclipse.epp.package.java.product +Command-line arguments: -os win32 -ws win32 -arch x86_64 -product org.eclipse.epp.package.java.product + +!ENTRY org.eclipse.egit.ui 2 0 2018-05-20 13:44:31.319 +!MESSAGE Warning: The environment variable HOME is not set. The following directory will be used to store the Git +user global configuration and to define the default location to store repositories: 'C:\Users\chris'. If this is +not correct please set the HOME environment variable and restart Eclipse. Otherwise Git for Windows and +EGit might behave differently since they see different configuration options. +This warning can be switched off on the Team > Git > Confirmations and Warnings preference page. diff --git a/.metadata/.mylyn/.taskListIndex/segments_1 b/.metadata/.mylyn/.taskListIndex/segments_1 new file mode 100644 index 0000000..ddbcfae Binary files /dev/null and b/.metadata/.mylyn/.taskListIndex/segments_1 differ diff --git a/.metadata/.mylyn/.taskListIndex/write.lock b/.metadata/.mylyn/.taskListIndex/write.lock new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.mylyn/.tasks.xml.zip b/.metadata/.mylyn/.tasks.xml.zip new file mode 100644 index 0000000..0ad43b1 Binary files /dev/null and b/.metadata/.mylyn/.tasks.xml.zip differ diff --git a/.metadata/.mylyn/repositories.xml.zip b/.metadata/.mylyn/repositories.xml.zip new file mode 100644 index 0000000..85e4b90 Binary files /dev/null and b/.metadata/.mylyn/repositories.xml.zip differ diff --git a/.metadata/.mylyn/tasks.xml.zip b/.metadata/.mylyn/tasks.xml.zip new file mode 100644 index 0000000..de1316e Binary files /dev/null and b/.metadata/.mylyn/tasks.xml.zip differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.history/7c/00bb913a235c0018157a9540c84d8272 b/.metadata/.plugins/org.eclipse.core.resources/.history/7c/00bb913a235c0018157a9540c84d8272 new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.resources/.history/7c/00bb913a235c0018157a9540c84d8272 @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/.indexes/af/history.index b/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/.indexes/af/history.index new file mode 100644 index 0000000..95e1768 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/.indexes/af/history.index differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/org.eclipse.jdt.core/state.dat b/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/org.eclipse.jdt.core/state.dat new file mode 100644 index 0000000..efd55f3 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.projects/Test1/org.eclipse.jdt.core/state.dat differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version new file mode 100644 index 0000000..25cb955 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index new file mode 100644 index 0000000..c194027 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version new file mode 100644 index 0000000..6b2aaa7 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/2.tree b/.metadata/.plugins/org.eclipse.core.resources/.root/2.tree new file mode 100644 index 0000000..1303193 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.root/2.tree differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources new file mode 100644 index 0000000..f260333 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..dffc6b5 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +version=1 diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.epp.logging.aeri.ide.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.epp.logging.aeri.ide.prefs new file mode 100644 index 0000000..c186df8 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.epp.logging.aeri.ide.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +resetSendMode=KEEP +resetSendModeOn=0 +sendMode=NOTIFY diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.core.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs new file mode 100644 index 0000000..e480949 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.launching.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.launching.PREF_VM_XML=\r\n\r\n\r\n\r\n\r\n\r\n diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..63d4e81 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,7 @@ +content_assist_proposals_background=255,255,255 +content_assist_proposals_foreground=0,0,0 +eclipse.preferences.version=1 +org.eclipse.jdt.ui.formatterprofiles.version=13 +spelling_locale_initialized=true +useAnnotationsPrefPage=true +useQuickDiffPrefPage=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs new file mode 100644 index 0000000..67b1d96 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.m2e.discovery.pref.projects= diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs new file mode 100644 index 0000000..43e97e4 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.context.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +mylyn.attention.migrated=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs new file mode 100644 index 0000000..8d462a6 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.monitor.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.mylyn.monitor.activity.tracking.enabled.checked=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs new file mode 100644 index 0000000..2b60c21 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.mylyn.tasks.ui.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +migrated.task.repositories.secure.store=true +org.eclipse.mylyn.tasks.ui.filters.nonmatching=true +org.eclipse.mylyn.tasks.ui.filters.nonmatching.encouraged=true +org.eclipse.mylyn.tasks.ui.welcome.message=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.team.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.team.ui.prefs new file mode 100644 index 0000000..56cd496 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.team.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.team.ui.first_time=false diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs new file mode 100644 index 0000000..61f3bb8 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +overviewRuler_migration=migrated_3.1 diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs new file mode 100644 index 0000000..02c9934 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs @@ -0,0 +1,5 @@ +PROBLEMS_FILTERS_MIGRATE=true +eclipse.preferences.version=1 +platformState=1526632350390 +quickStart=false +tipsAndTricks=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs new file mode 100644 index 0000000..aa3dc02 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs @@ -0,0 +1,3 @@ +//org.eclipse.ui.commands/state/org.eclipse.ui.navigator.resources.nested.changeProjectPresentation/org.eclipse.ui.commands.radioState=false +PLUGINS_NOT_ACTIVATED_ON_STARTUP=;org.eclipse.m2e.discovery; +eclipse.preferences.version=1 diff --git a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi new file mode 100644 index 0000000..e60be6b --- /dev/null +++ b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -0,0 +1,2293 @@ + + + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration + ModelMigrationProcessor.001 + + + + + + + + topLevel + + + Minimized + MinimizedByZoom + + + persp.actionSet:org.eclipse.mylyn.doc.actionSet + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation + persp.actionSet:org.eclipse.ui.cheatsheets.actionSet + persp.actionSet:org.eclipse.search.searchActionSet + persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo + persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet + persp.actionSet:org.eclipse.ui.actionSet.keyBindings + persp.actionSet:org.eclipse.ui.actionSet.openFiles + persp.actionSet:org.eclipse.debug.ui.launchActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaElementCreationActionSet + persp.actionSet:org.eclipse.ui.NavigateActionSet + persp.viewSC:org.eclipse.jdt.ui.PackageExplorer + persp.viewSC:org.eclipse.jdt.ui.TypeHierarchy + persp.viewSC:org.eclipse.jdt.ui.SourceView + persp.viewSC:org.eclipse.jdt.ui.JavadocView + persp.viewSC:org.eclipse.search.ui.views.SearchView + persp.viewSC:org.eclipse.ui.console.ConsoleView + persp.viewSC:org.eclipse.ui.views.ContentOutline + persp.viewSC:org.eclipse.ui.views.ProblemView + persp.viewSC:org.eclipse.ui.views.ResourceNavigator + persp.viewSC:org.eclipse.ui.views.TaskList + persp.viewSC:org.eclipse.ui.views.ProgressView + persp.viewSC:org.eclipse.ui.navigator.ProjectExplorer + persp.viewSC:org.eclipse.ui.texteditor.TemplatesView + persp.viewSC:org.eclipse.pde.runtime.LogView + persp.newWizSC:org.eclipse.jdt.ui.wizards.JavaProjectWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewPackageCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewClassCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewEnumCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard + persp.newWizSC:org.eclipse.ui.wizards.new.folder + persp.newWizSC:org.eclipse.ui.wizards.new.file + persp.newWizSC:org.eclipse.ui.editors.wizards.UntitledTextFileWizard + persp.perspSC:org.eclipse.jdt.ui.JavaBrowsingPerspective + persp.perspSC:org.eclipse.debug.ui.DebugPerspective + persp.viewSC:org.eclipse.mylyn.tasks.ui.views.tasks + persp.newWizSC:org.eclipse.mylyn.tasks.ui.wizards.new.repository.task + persp.showIn:org.eclipse.jdt.ui.PackageExplorer + persp.showIn:org.eclipse.team.ui.GenericHistoryView + persp.showIn:org.eclipse.ui.views.ResourceNavigator + persp.showIn:org.eclipse.ui.navigator.ProjectExplorer + persp.actionSet:org.eclipse.debug.ui.breakpointActionSet + persp.actionSet:org.eclipse.jdt.debug.ui.JDTDebugActionSet + persp.actionSet:org.eclipse.eclemma.ui.CoverageActionSet + persp.showIn:org.eclipse.eclemma.ui.CoverageView + persp.showIn:org.eclipse.egit.ui.RepositoriesView + persp.newWizSC:org.eclipse.jdt.junit.wizards.NewTestCaseCreationWizard + persp.actionSet:org.eclipse.jdt.junit.JUnitActionSet + persp.viewSC:org.eclipse.ant.ui.views.AntView + + + + org.eclipse.e4.primaryNavigationStack + + + + + + + + + + + + + + + + + + + org.eclipse.e4.secondaryNavigationStack + + + + + + + + org.eclipse.e4.secondaryDataStack + + + + + + + + + + + + + + Maximized + active + + + + + + + + + View + categoryTag:Help + + + + + + View + categoryTag:General + active + activeOnClose + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Help + + + + org.eclipse.e4.primaryDataStack + EditorStack + + + + + + + View + categoryTag:Java + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:General + + + + + + View + categoryTag:Mylyn + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Git + + + + + View + categoryTag:Java + + + + + View + categoryTag:Ant + + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + Draggable + + + Draggable + + + Draggable + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + toolbarSeparator + + + + Draggable + + + stretch + SHOW_RESTORE_MENU + + + Draggable + HIDEABLE + SHOW_RESTORE_MENU + + + + + stretch + + + Draggable + + + Draggable + + + + + TrimStack + Draggable + + + + + + + + + platform:win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + platform:win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor + + + + + View + categoryTag:Ant + + + + + View + categoryTag:Gradle + + + + + View + categoryTag:Gradle + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Java + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:General + + + + + View + categoryTag:Help + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Maven + + + + + View + categoryTag:Maven + + + + + View + categoryTag:Mylyn + + + + + View + categoryTag:Mylyn + + + + + View + categoryTag:Mylyn + + + + + View + categoryTag:Mylyn + + + + + View + categoryTag:Oomph + + + + + View + categoryTag:Code Recommenders + + + + + View + categoryTag:Code Recommenders + + + + + View + categoryTag:Code Recommenders + + + + + View + categoryTag:Code Recommenders + + + + + View + categoryTag:Code Recommenders + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:Team + + + + + View + categoryTag:Team + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:Help + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:XML + + + + + View + categoryTag:XML + + + + + + + + + + glue + move_after:PerspectiveSpacer + SHOW_RESTORE_MENU + + + move_after:Spacer Glue + HIDEABLE + SHOW_RESTORE_MENU + + + glue + move_after:SearchField + SHOW_RESTORE_MENU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/http-cache.lucene60/segments_1 b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/http-cache.lucene60/segments_1 new file mode 100644 index 0000000..fb6cd63 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/http-cache.lucene60/segments_1 differ diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/http-cache.lucene60/write.lock b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/http-cache.lucene60/write.lock new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfe b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfe new file mode 100644 index 0000000..33b2727 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfe differ diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfs b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfs new file mode 100644 index 0000000..9d51f6b Binary files /dev/null and b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.cfs differ diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.si b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.si new file mode 100644 index 0000000..55a2a46 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/_0.si differ diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/segments_1 b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/segments_1 new file mode 100644 index 0000000..2e436ad Binary files /dev/null and b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/segments_1 differ diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/write.lock b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/local-history.lucene60/write.lock new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json new file mode 100644 index 0000000..b775225 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.epp.logging.aeri.ide/org.eclipse.epp.logging.aeri.ide.server/server-config.json @@ -0,0 +1,72 @@ +{ + "version": "v1", + "title": "Eclipse", + "timestamp": 1526633390403, + "ttl": 10080, + "helpUrl": "https://dev.eclipse.org/recommenders/community/aeri/v2/help/", + "feedbackUrl": "https://www.codetrails.com/error-analytics/", + "aboutUrl": "https://wiki.eclipse.org/EPP/Logging", + "submitUrl": "https://dev.eclipse.org/recommenders/community/confess/0.6/reports/", + "maxReportSize": 262144, + "problemsUrl": "https://www.eclipse.org/downloads/download.php?r\u003d1\u0026file\u003d/technology/epp/logging/problems.zip", + "problemsTtl": 20160, + "interestUrl": "https://dev.eclipse.org/recommenders/community/confess/v2/interest", + "connectTimeout": 10, + "socketTimeout": 10, + "acceptedProducts": [ + "org.eclipse.*", + "org.fordiac.*" + ], + "acceptedPlugins": [ + "org.apache.log4j.*", + "org.eclipse.*", + "org.fordiac.*" + ], + "acceptedPackages": [ + "ch.qos.*", + "com.cforcoding.*", + "com.google.*", + "com.gradleware.tooling.*", + "com.mountainminds.eclemma.*", + "com.naef.*", + "com.sun.*", + "java.*", + "javafx.*", + "javax.*", + "org.apache.*", + "org.eclipse.*", + "org.fordiac.*", + "org.gradle.*", + "org.jacoco.*", + "org.osgi.*", + "org.slf4j.*", + "sun.*" + ], + "requiredPackages": [ + "com.cforcoding.*", + "com.gradleware.tooling.*", + "com.mountainminds.eclemma.*", + "com.naef.*", + "org.eclipse.*", + "org.fordiac.*", + "org.gradle.*", + "org.jacoco.*" + ], + "acceptOtherPackages": false, + "acceptUiFreezes": true, + "ignoredStatuses": [ + ":java.io.IOException:There is not enough space on the disk", + ":java.net.*:", + "org.eclipse.core.filesystem::Could not delete*", + "org.eclipse.core.filesystem::Could not move*", + "org.eclipse.core.resources:org.eclipse.core.internal.resources.ResourceException:Resource is out of sync with the file system*", + "org.eclipse.core.runtime::Invalid input url*", + "org.eclipse.epp.mpc.ui:java.io.IOException:", + "org.eclipse.equinox.p2.*::", + "org.eclipse.jface:java.io.IOException:Unable to resolve plug-in*", + "org.eclipse.oomph.setup.core:$org.apache.http.ConnectionClosedException:", + "org.eclipse.pde.core::The current target platform contains errors*", + "org.eclipse.ui::Conflicting handlers for*" + ], + "problemsZipLastDownloadTimestamp": 0 +} \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.jdt.core/100574398.index b/.metadata/.plugins/org.eclipse.jdt.core/100574398.index new file mode 100644 index 0000000..23573ff Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/100574398.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/1177904427.index b/.metadata/.plugins/org.eclipse.jdt.core/1177904427.index new file mode 100644 index 0000000..2c41c29 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/1177904427.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/1204375214.index b/.metadata/.plugins/org.eclipse.jdt.core/1204375214.index new file mode 100644 index 0000000..dc5d890 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/1204375214.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/1205824980.index b/.metadata/.plugins/org.eclipse.jdt.core/1205824980.index new file mode 100644 index 0000000..8b73a0c Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/1205824980.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/2043626465.index b/.metadata/.plugins/org.eclipse.jdt.core/2043626465.index new file mode 100644 index 0000000..ce9a98d Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/2043626465.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/2088728460.index b/.metadata/.plugins/org.eclipse.jdt.core/2088728460.index new file mode 100644 index 0000000..d8e60b8 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/2088728460.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/2569137926.index b/.metadata/.plugins/org.eclipse.jdt.core/2569137926.index new file mode 100644 index 0000000..3191fe3 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/2569137926.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/2872033403.index b/.metadata/.plugins/org.eclipse.jdt.core/2872033403.index new file mode 100644 index 0000000..53aed5a Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/2872033403.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/3273689814.index b/.metadata/.plugins/org.eclipse.jdt.core/3273689814.index new file mode 100644 index 0000000..9add4c7 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/3273689814.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/3363692690.index b/.metadata/.plugins/org.eclipse.jdt.core/3363692690.index new file mode 100644 index 0000000..13fdca0 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/3363692690.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/3487609244.index b/.metadata/.plugins/org.eclipse.jdt.core/3487609244.index new file mode 100644 index 0000000..b843f49 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/3487609244.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/3609888128.index b/.metadata/.plugins/org.eclipse.jdt.core/3609888128.index new file mode 100644 index 0000000..178e869 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/3609888128.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/4121661813.index b/.metadata/.plugins/org.eclipse.jdt.core/4121661813.index new file mode 100644 index 0000000..8b73a0c Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/4121661813.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/710797068.index b/.metadata/.plugins/org.eclipse.jdt.core/710797068.index new file mode 100644 index 0000000..98c0932 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/710797068.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/806062888.index b/.metadata/.plugins/org.eclipse.jdt.core/806062888.index new file mode 100644 index 0000000..c9ac66a Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/806062888.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/823384711.index b/.metadata/.plugins/org.eclipse.jdt.core/823384711.index new file mode 100644 index 0000000..bdfb2f5 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/823384711.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/890887124.index b/.metadata/.plugins/org.eclipse.jdt.core/890887124.index new file mode 100644 index 0000000..7e0dd4e Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/890887124.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/916900781.index b/.metadata/.plugins/org.eclipse.jdt.core/916900781.index new file mode 100644 index 0000000..911d26b Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/916900781.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache new file mode 100644 index 0000000..593f470 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache new file mode 100644 index 0000000..05031d5 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps new file mode 100644 index 0000000..3e6f48d Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/index.db b/.metadata/.plugins/org.eclipse.jdt.core/index.db new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.plugins/org.eclipse.jdt.core/indexNamesMap.txt b/.metadata/.plugins/org.eclipse.jdt.core/indexNamesMap.txt new file mode 100644 index 0000000..0295c3b --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.core/indexNamesMap.txt @@ -0,0 +1 @@ +INDEX VERSION 1.130 diff --git a/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt b/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt new file mode 100644 index 0000000..8586397 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt @@ -0,0 +1 @@ +java \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache new file mode 100644 index 0000000..05031d5 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt new file mode 100644 index 0000000..4c967db --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt @@ -0,0 +1,19 @@ +INDEX VERSION 1.130+C:\Users\chris\Documents\WorkspaceJava\.metadata\.plugins\org.eclipse.jdt.core +2872033403.index +3363692690.index +890887124.index +3487609244.index +2043626465.index +2088728460.index +100574398.index +3273689814.index +806062888.index +1204375214.index +1177904427.index +2569137926.index +710797068.index +3609888128.index +1205824980.index +4121661813.index +916900781.index +823384711.index diff --git a/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat b/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat new file mode 100644 index 0000000..77ff78d Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat differ diff --git a/.metadata/.plugins/org.eclipse.jdt.launching/.install.xml b/.metadata/.plugins/org.eclipse.jdt.launching/.install.xml new file mode 100644 index 0000000..77e43ac --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.launching/.install.xml @@ -0,0 +1,4 @@ + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.launching/libraryInfos.xml b/.metadata/.plugins/org.eclipse.jdt.launching/libraryInfos.xml new file mode 100644 index 0000000..85b0e90 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.launching/libraryInfos.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml new file mode 100644 index 0000000..a4ee3cb --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml new file mode 100644 index 0000000..9e390f5 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml new file mode 100644 index 0000000..5f85d51 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -0,0 +1,10 @@ + +
+
+ + + + + +
+
diff --git a/.metadata/.plugins/org.eclipse.m2e.logback.configuration/0.log b/.metadata/.plugins/org.eclipse.m2e.logback.configuration/0.log new file mode 100644 index 0000000..bfc2e26 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.m2e.logback.configuration/0.log @@ -0,0 +1,2 @@ +2018-05-18 10:49:47,485 [Worker-0] INFO c.g.t.t.d.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. +2018-05-20 13:44:31,050 [Worker-5] INFO c.g.t.t.d.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update. diff --git a/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.8.3.20180227-2137.xml b/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.8.3.20180227-2137.xml new file mode 100644 index 0000000..e33758c --- /dev/null +++ b/.metadata/.plugins/org.eclipse.m2e.logback.configuration/logback.1.8.3.20180227-2137.xml @@ -0,0 +1,43 @@ + + + + %date [%thread] %-5level %logger{35} - %msg%n + + + OFF + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 100MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup b/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup new file mode 100644 index 0000000..1f73e14 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup @@ -0,0 +1,6 @@ + + diff --git a/.metadata/.plugins/org.eclipse.recommenders.news.impl/downloads/http%3A%2F%2Fwww.eclipse.org%2Fhome%2Feclipsenews.rss b/.metadata/.plugins/org.eclipse.recommenders.news.impl/downloads/http%3A%2F%2Fwww.eclipse.org%2Fhome%2Feclipsenews.rss new file mode 100644 index 0000000..e69de29 diff --git a/.metadata/.plugins/org.eclipse.recommenders.news.impl/downloads/http%3A%2F%2Fwww.eclipse.org%2Frecommenders%2Ffeeds%2Fide.rss b/.metadata/.plugins/org.eclipse.recommenders.news.impl/downloads/http%3A%2F%2Fwww.eclipse.org%2Frecommenders%2Ffeeds%2Fide.rss new file mode 100644 index 0000000..b6219a4 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.recommenders.news.impl/downloads/http%3A%2F%2Fwww.eclipse.org%2Frecommenders%2Ffeeds%2Fide.rss @@ -0,0 +1,13 @@ + + + + Code Recommenders In-IDE News + https://www.eclipse.org/recommenders/ + The latest news about Code Recommenders, delivered to your Eclipse IDE + + Insert Knowledge Here - A Guide to Intelligent Code Completion Using Eclipse Code Recommenders + https://medium.com/codetrails/insert-knowledge-here-a2f71c2862d2?utm_source=rss-eclipse&utm_medium=eclipse&utm_campaign=cc + Tue, 10 Oct 2017 16:00:00 GMT + + + diff --git a/.metadata/.plugins/org.eclipse.ui.intro/introstate b/.metadata/.plugins/org.eclipse.ui.intro/introstate new file mode 100644 index 0000000..236d56c --- /dev/null +++ b/.metadata/.plugins/org.eclipse.ui.intro/introstate @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml new file mode 100644 index 0000000..851c296 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -0,0 +1,15 @@ + +
+
+ + + + + + + + + + +
+
diff --git a/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 0000000..e5baf38 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.metadata/version.ini b/.metadata/version.ini new file mode 100644 index 0000000..1a6362f --- /dev/null +++ b/.metadata/version.ini @@ -0,0 +1,3 @@ +#Sun May 20 13:44:12 CEST 2018 +org.eclipse.core.runtime=2 +org.eclipse.platform=4.7.3.v20180330-0640 diff --git a/01-SOURCES/Asteroid.java b/01-SOURCES/Asteroid.java new file mode 100644 index 0000000..8e8c3c8 --- /dev/null +++ b/01-SOURCES/Asteroid.java @@ -0,0 +1,37 @@ +/***************************************************** + * Beginning Java Game Programming, 2nd Edition + * by Jonathan S. Harbour + * Asteroid class - For polygonal asteroid shapes + *****************************************************/ + +import java.awt.Polygon; +import java.awt.Rectangle; + +/********************************************************* + * Asteroid class derives from BaseVectorShape + **********************************************************/ +public class Asteroid extends BaseVectorShape { + //define the asteroid polygon shape + private int[] astx = {-20,-13, 0,20,22, 20, 12, 2,-10,-22,-16}; + private int[] asty = { 20, 23,17,20,16,-20,-22,-14,-17,-20, -5}; + + //rotation speed + protected double rotVel; + public double getRotationVelocity() { return rotVel; } + public void setRotationVelocity(double v) { rotVel = v; } + + //bounding rectangle + public Rectangle getBounds() { + Rectangle r; + r = new Rectangle((int)getX() - 20, (int) getY() - 20, 40, 40); + return r; + } + + //default constructor + Asteroid() { + setShape(new Polygon(astx, asty, astx.length)); + setAlive(true); + setRotationVelocity(0.0); + } + +} diff --git a/01-SOURCES/Asteroids.java b/01-SOURCES/Asteroids.java new file mode 100644 index 0000000..50ef19e --- /dev/null +++ b/01-SOURCES/Asteroids.java @@ -0,0 +1,440 @@ +/***************************************************** + * Beginning Java Game Programming, 2nd Edition + * by Jonathan S. Harbour + * Chapter 3 - ASTEROIDS GAME + *****************************************************/ + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.util.*; + +/***************************************************** + * Primary class for the game inherits from Applet + *****************************************************/ +public class Asteroids extends Applet implements Runnable, KeyListener { + + //the main thread becomes the game loop + Thread gameloop; + + //use this as a double buffer + BufferedImage backbuffer; + + //the main drawing object for the back buffer + Graphics2D g2d; + + //toggle for drawing bounding boxes + boolean showBounds = false; + + //create the asteroid array + int ASTEROIDS = 20; + Asteroid[] ast = new Asteroid[ASTEROIDS]; + + //create the bullet array + int BULLETS = 10; + Bullet[] bullet = new Bullet[BULLETS]; + int currentBullet = 0; + + //the player's ship + Ship ship = new Ship(); + + //create the identity transform (0,0) + AffineTransform identity = new AffineTransform(); + + //create a random number generator + Random rand = new Random(); + + /***************************************************** + * applet init event + *****************************************************/ + public void init() { + //create the back buffer for smooth graphics + backbuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB); + g2d = backbuffer.createGraphics(); + + //set up the ship + ship.setX(320); + ship.setY(240); + + //set up the bullets + for (int n = 0; n getSize().width + 10) + ship.setX(-10); + + //update ship's Y position + ship.incY(ship.getVelY()); + + //wrap around top/bottom + if (ship.getY() < -10) + ship.setY(getSize().height + 10); + else if (ship.getY() > getSize().height + 10) + ship.setY(-10); + } + + /***************************************************** + * Update the bullets based on velocity + *****************************************************/ + public void updateBullets() { + + //move each of the bullets + for (int n = 0; n < BULLETS; n++) { + + //is this bullet being used? + if (bullet[n].isAlive()) { + + //update bullet's x position + bullet[n].incX(bullet[n].getVelX()); + + //bullet disappears at left/right edge + if (bullet[n].getX() < 0 || + bullet[n].getX() > getSize().width) + { + bullet[n].setAlive(false); + } + + //update bullet's y position + bullet[n].incY(bullet[n].getVelY()); + + //bullet disappears at top/bottom edge + if (bullet[n].getY() < 0 || + bullet[n].getY() > getSize().height) + { + bullet[n].setAlive(false); + } + } + } + } + + /***************************************************** + * Update the asteroids based on velocity + *****************************************************/ + public void updateAsteroids() { + + //move and rotate the asteroids + for (int n = 0; n < ASTEROIDS; n++) { + + //is this asteroid being used? + if (ast[n].isAlive()) { + + //update the asteroid's X value + ast[n].incX(ast[n].getVelX()); + + //warp the asteroid at screen edges + if (ast[n].getX() < -20) + ast[n].setX(getSize().width + 20); + else if (ast[n].getX() > getSize().width + 20) + ast[n].setX(-20); + + //update the asteroid's Y value + ast[n].incY(ast[n].getVelY()); + + //warp the asteroid at screen edges + if (ast[n].getY() < -20) + ast[n].setY(getSize().height + 20); + else if (ast[n].getY() > getSize().height + 20) + ast[n].setY(-20); + + //update the asteroid's rotation + ast[n].incMoveAngle(ast[n].getRotationVelocity()); + + //keep the angle within 0-359 degrees + if (ast[n].getMoveAngle() < 0) + ast[n].setMoveAngle(360 - ast[n].getRotationVelocity()); + else if (ast[n].getMoveAngle() > 359) + ast[n].setMoveAngle(ast[n].getRotationVelocity()); + } + } + } + + /***************************************************** + * Test asteroids for collisions with ship or bullets + *****************************************************/ + public void checkCollisions() { + + //iterate through the asteroids array + for (int m = 0; m 360) ship.setFaceAngle(5); + break; + + case KeyEvent.VK_UP: + //up arrow adds thrust to ship (1/10 normal speed) + ship.setMoveAngle(ship.getFaceAngle() - 90); + ship.incVelX(calcAngleMoveX(ship.getMoveAngle()) * 0.1); + ship.incVelY(calcAngleMoveY(ship.getMoveAngle()) * 0.1); + break; + + //Ctrl, Enter, or Space can be used to fire weapon + case KeyEvent.VK_CONTROL: + case KeyEvent.VK_ENTER: + case KeyEvent.VK_SPACE: + //fire a bullet + currentBullet++; + if (currentBullet > BULLETS - 1) currentBullet = 0; + bullet[currentBullet].setAlive(true); + + //point bullet in same direction ship is facing + bullet[currentBullet].setX(ship.getX()); + bullet[currentBullet].setY(ship.getY()); + bullet[currentBullet].setMoveAngle(ship.getFaceAngle() - 90); + + //fire bullet at angle of the ship + double angle = bullet[currentBullet].getMoveAngle(); + double svx = ship.getVelX(); + double svy = ship.getVelY(); + bullet[currentBullet].setVelX(svx + calcAngleMoveX(angle) * 2); + bullet[currentBullet].setVelY(svy + calcAngleMoveY(angle) * 2); + break; + + } + } + + /***************************************************** + * calculate X movement value based on direction angle + *****************************************************/ + public double calcAngleMoveX(double angle) { + return (double) (Math.cos(angle * Math.PI / 180)); + } + + /***************************************************** + * calculate Y movement value based on direction angle + *****************************************************/ + public double calcAngleMoveY(double angle) { + return (double) (Math.sin(angle * Math.PI / 180)); + } + +} diff --git a/01-SOURCES/AsteroidsACompleter.java b/01-SOURCES/AsteroidsACompleter.java new file mode 100644 index 0000000..aec00d0 --- /dev/null +++ b/01-SOURCES/AsteroidsACompleter.java @@ -0,0 +1,422 @@ +/***************************************************** + * Beginning Java Game Programming, 2nd Edition + * by Jonathan S. Harbour + * Chapter 3 - ASTEROIDS GAME + *****************************************************/ + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.util.*; + +/***************************************************** + * Primary class for the game inherits from Applet + *****************************************************/ +public class AsteroidsACompleter extends Applet implements Runnable, KeyListener { + + //the main thread becomes the game loop + Thread gameloop; + + //use this as a double buffer + BufferedImage backbuffer; + + //the main drawing object for the back buffer + Graphics2D g2d; + + //toggle for drawing bounding boxes + boolean showBounds = false; + + //create the asteroid array + int ASTEROIDS = 20; + Asteroid[] ast = new Asteroid[ASTEROIDS]; + + //create the bullet array + int BULLETS = 10; + Bullet[] bullet = new Bullet[BULLETS]; + int currentBullet = 0; + + //the player's ship + Ship ship = new Ship(); + + //create the identity transform (0,0) + AffineTransform identity = new AffineTransform(); + + //create a random number generator + Random rand = new Random(); + + /***************************************************** + * applet init event + *****************************************************/ + public void init() { + //create the back buffer for smooth graphics + backbuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB); + g2d = backbuffer.createGraphics(); + + //set up the ship + ship.setX(320); + ship.setY(240); + + //set up the bullets + for (int n = 0; n getSize().width + 10) + ship.setX(-10); + + //update ship's Y position + ship.incY(ship.getVelY()); + + //wrap around top/bottom + if (ship.getY() < -10) + ship.setY(getSize().height + 10); + else if (ship.getY() > getSize().height + 10) + ship.setY(-10); + } + + /***************************************************** + * Update the bullets based on velocity + *****************************************************/ + public void updateBullets() { + + //move each of the bullets + for (int n = 0; n < BULLETS; n++) { + + //is this bullet being used? + if (bullet[n].isAlive()) { + + //update bullet's x position + bullet[n].incX(bullet[n].getVelX()); + + //bullet disappears at left/right edge + if (bullet[n].getX() < 0 || + bullet[n].getX() > getSize().width) + { + bullet[n].setAlive(false); + } + + //update bullet's y position + bullet[n].incY(bullet[n].getVelY()); + + //bullet disappears at top/bottom edge + if (bullet[n].getY() < 0 || + bullet[n].getY() > getSize().height) + { + bullet[n].setAlive(false); + } + } + } + } + + /***************************************************** + * Update the asteroids based on velocity + *****************************************************/ + public void updateAsteroids() { + + //move and rotate the asteroids + for (int n = 0; n < ASTEROIDS; n++) { + + //is this asteroid being used? + if (ast[n].isAlive()) { + + //update the asteroid's X value + ast[n].incX(ast[n].getVelX()); + + //warp the asteroid at screen edges + if (ast[n].getX() < -20) + ast[n].setX(getSize().width + 20); + else if (ast[n].getX() > getSize().width + 20) + ast[n].setX(-20); + + //update the asteroid's Y value + ast[n].incY(ast[n].getVelY()); + + //warp the asteroid at screen edges + if (ast[n].getY() < -20) + ast[n].setY(getSize().height + 20); + else if (ast[n].getY() > getSize().height + 20) + ast[n].setY(-20); + + //update the asteroid's rotation + ast[n].incMoveAngle(ast[n].getRotationVelocity()); + + //keep the angle within 0-359 degrees + if (ast[n].getMoveAngle() < 0) + ast[n].setMoveAngle(360 - ast[n].getRotationVelocity()); + else if (ast[n].getMoveAngle() > 359) + ast[n].setMoveAngle(ast[n].getRotationVelocity()); + } + } + } + + /***************************************************** + * Test asteroids for collisions with ship or bullets + *****************************************************/ + public void checkCollisions() { + + //iterate through the asteroids array + for (int m = 0; m erreur de compilation + + //----------------------------------------------------- + // Conversions implicites + //----------------------------------------------------- + int k = 1000 ; + long lo ; + lo = k ; + + //----------------------------------------------------- + // Conversions explicites (cast) + //----------------------------------------------------- + i = (int) 2.5 ; + System.out.println ("valeur de (int) 2.5 : " + i); + + //----------------------------------------------------- + // Gestion des erreurs en Java + //----------------------------------------------------- + + // Diviser par zéro + i = i / 0 ; + + // Saisir un caractčre alors qu'on attend un nombre + + } +} diff --git a/02-SOURCES/Assurance.java b/02-SOURCES/Assurance.java new file mode 100644 index 0000000..f41eec3 --- /dev/null +++ b/02-SOURCES/Assurance.java @@ -0,0 +1,28 @@ +public class Assurance { + + public static void main(String[] args) { + // Declarer une reference a une voiture + Voiture scenic ; + + + // Utiliser l'operateur new + // - allouer de la place memoire dans le tas pour tous les attributs de cet objet + // - initialiser les attributs ŕ des valeurs par défauut + // - retourner l'adresse de la zone allouee + scenic = new Voiture (); + + // Affecter des valeurs aux attributs de cet objet + scenic.annee = 1998 ; + + // Afficher les valeurs des attributs de cet objet + System.out.println(" Annee scenic : " + scenic.annee); + + // Declarer une reference vers une 2eme voiture + Voiture audi ; + + // Creer un objet, modifier les attributs et les afficher + audi = new Voiture (); + audi.annee = 2008 ; + System.out.println(" Annee audi : " + audi.annee); + } +} diff --git a/02-SOURCES/CabinetAssurance.java b/02-SOURCES/CabinetAssurance.java new file mode 100644 index 0000000..e627af4 --- /dev/null +++ b/02-SOURCES/CabinetAssurance.java @@ -0,0 +1,26 @@ + +public class CabinetAssurance { + + final int NB_MAX = 10 ; + + Voiture [] voitures ; + int nbVoitures ; + + CabinetAssurance () { + voitures = new Voiture [NB_MAX]; + } + + void ajouterVoiture (Voiture v1) { + voitures[nbVoitures] = v1 ; + nbVoitures++ ; + } + public static void main(String[] args) { + CabinetAssurance a = new CabinetAssurance(); + + Voiture durand = new Voiture (); + durand.annee = 1999 ; + + a.ajouterVoiture(durand); + } + +} diff --git a/02-SOURCES/Ferme.java b/02-SOURCES/Ferme.java new file mode 100644 index 0000000..2627ce9 --- /dev/null +++ b/02-SOURCES/Ferme.java @@ -0,0 +1,11 @@ + +public class Ferme { + public static void main(String[] args) { + Panier cabas = new Panier (); + System.out.println (cabas.nbEmplacements); + + PanierAC pJC = new PanierAC (2, 100, 12.5); + PanierAC pBob = new PanierAC (3, 50, 3.5); + pJC.prix = 37 ; + } +} diff --git a/02-SOURCES/Joueur.java b/02-SOURCES/Joueur.java new file mode 100644 index 0000000..a70bdcc --- /dev/null +++ b/02-SOURCES/Joueur.java @@ -0,0 +1,25 @@ + +public class Joueur { + final int SCORE_MAX ; + static int nbJoueur ; + int numero ; + + Joueur () { + SCORE_MAX = 10 ; + nbJoueur ++ ; + numero = nbJoueur ; + } + + public static void main(String[] args) { + + Joueur j1 = new Joueur (); + System.out.println (nbJoueur); + System.out.println ("no j1 : " + j1.numero); + + Joueur j2 = new Joueur (); + System.out.println (nbJoueur); + System.out.println ("no j2 : " + j2.numero); + + } + +} diff --git a/02-SOURCES/Panier.java b/02-SOURCES/Panier.java new file mode 100644 index 0000000..adbfa05 --- /dev/null +++ b/02-SOURCES/Panier.java @@ -0,0 +1,24 @@ + +public class Panier { + + int poids ; + int taille ; + double prix ; + int nbEmplacements ; + + Panier () { + // Par défaut initialisation + // ŕ 2 emplacements dans le panier + nbEmplacements = 2 ; + int i ; + i = 10 ; + } + + public static void main (String args []) { + Panier p1 = new Panier (); + System.out.println (p1.nbEmplacements); + System.out.println (p1.taille); + p1.nbEmplacements = 3 ; + System.out.println (p1.nbEmplacements); + } +} diff --git a/02-SOURCES/PanierAC.java b/02-SOURCES/PanierAC.java new file mode 100644 index 0000000..cc4f076 --- /dev/null +++ b/02-SOURCES/PanierAC.java @@ -0,0 +1,26 @@ +// Version de la classe Panier avec un constructeur +// avec des paramčtres + +public class PanierAC { + + int poids ; + int taille ; + double prix ; + int nbEmplacements ; + + PanierAC (int p, int t, double pr) { + // Par défaut initialisation + // ŕ 2 emplacements dans le panier + nbEmplacements = 2 ; + poids = p ; + taille = t ; + prix = pr ; + System.out.println ("Dans le constructeur prix vaut " + prix); + } + + public static void main (String args []) { + PanierAC p1 = new PanierAC (2, 100, 12.5); + PanierAC p2 = new PanierAC (2, 100, 7.8 ); + + } +} diff --git a/02-SOURCES/Personne.java b/02-SOURCES/Personne.java new file mode 100644 index 0000000..bb3dfcb --- /dev/null +++ b/02-SOURCES/Personne.java @@ -0,0 +1,4 @@ + +public class Personne { + int anneeNaissance ; +} diff --git a/02-SOURCES/Test.java b/02-SOURCES/Test.java new file mode 100644 index 0000000..42e4dec --- /dev/null +++ b/02-SOURCES/Test.java @@ -0,0 +1,9 @@ + +public class Test { + + // Ok + static final int CONST = 1; + public static void main(String[] args) { + System.out.println(CONST); + } +} diff --git a/02-SOURCES/Voiture.java b/02-SOURCES/Voiture.java new file mode 100644 index 0000000..03945c1 --- /dev/null +++ b/02-SOURCES/Voiture.java @@ -0,0 +1,6 @@ + +public class Voiture { + int annee ; + int puissance ; + double valeur ; +} diff --git a/02-SOURCES/VoitureAttribut.java b/02-SOURCES/VoitureAttribut.java new file mode 100644 index 0000000..b57f103 --- /dev/null +++ b/02-SOURCES/VoitureAttribut.java @@ -0,0 +1,6 @@ + +public class VoitureAttribut { + int annee ; + int puissance ; + double valeur ; +} diff --git a/02-SOURCES/VoitureConstructeur.java b/02-SOURCES/VoitureConstructeur.java new file mode 100644 index 0000000..cc4e375 --- /dev/null +++ b/02-SOURCES/VoitureConstructeur.java @@ -0,0 +1,32 @@ + +public class VoitureConstructeur { + int annee ; + int puissance ; + double valeur ; + + /* + * Si on ne définit aucun constructeur, + * le compilateur en définit un par défaut + * qui ne prend aucun paramčtre + * (et qui ne fait rien). + * Si on définit un constructeur avec des paramčtres, + * le compilateur ne définit plus de constructeur + * par défaut sans paramčtres + * => c'est ŕ vous de le définir si vous voulez l'utiliser + */ + public static void main (String args[]) { + VoitureConstructeur v ; + v = new VoitureConstructeur (2007, 7); + VoitureConstructeur v2 = new VoitureConstructeur (); // ERREUR CAR LE CONSTRUCTEUR PAR DEFAUT N'A PAS ETE CREE + } + + VoitureConstructeur () { + + } + + VoitureConstructeur (int an, double valeur) { + annee = an ; + this.valeur = valeur ; // CAR L'ATTRIBUT A LE MEME NOM QU'UN PARAMETRE +} + +} diff --git a/02-SOURCES/VoitureProprietaire.java b/02-SOURCES/VoitureProprietaire.java new file mode 100644 index 0000000..33e4bdf --- /dev/null +++ b/02-SOURCES/VoitureProprietaire.java @@ -0,0 +1,33 @@ + +public class VoitureProprietaire { + int annee ; + int puissance ; + double valeur ; + Personne proprietaire ; + + public static void main(String args[]) { + + // Déclaration d'une référence vers + // une instance de la classe VoitureProprietaire + VoitureProprietaire v ; + + // Créer une instance de la classe VoitureProprietaire + // et mémoriser son adresse dans la variable v + v = new VoitureProprietaire(); + + + + + + + // Déclarer et créer une instance de la classe Personne + Personne p = new Personne (); + + // Modifier l'année de naissance + p.anneeNaissance = 1965 ; + + // Mémoriser cette personne comme proprietaire de la voiture + v.proprietaire = p ; + System.out.println (v.proprietaire.anneeNaissance); + } +} diff --git a/02-SOURCES/VoitureProprietaireComplet.java b/02-SOURCES/VoitureProprietaireComplet.java new file mode 100644 index 0000000..5e20640 --- /dev/null +++ b/02-SOURCES/VoitureProprietaireComplet.java @@ -0,0 +1,61 @@ +// Compléter la classe Personne en ajoutant un attribut numéroSecu +// Compléter le main de la classe VoitureProprietaire +// en créant une 2čme voiture. +// Le propriétaire de cette voiture sera le męme pour les 2 voitures. +// Faire un schéma de la mémoire boite et fleches. + +public class VoitureProprietaireComplet { + int annee ; + int puissance ; + double valeur ; + PersonneSecu proprietaire ; + + public static void main(String[] args) { + // Déclaration d'une référence vers + // une instance de la classe VoitureProprietaire + VoitureProprietaireComplet v ; + + // Créer une instance de la classe VoitureProprietaire + // et mémoriser son adresse dans la variable v + v = new VoitureProprietaireComplet(); + + // Déclarer et créer une instance de la classe Personne + PersonneSecu p = new PersonneSecu (); + + // Modifier l'année de naissance + p.anneeNaissance = 1965 ; + + // Mémoriser cette personne comme proprietaire de la voiture + // pointée par v + v.proprietaire = p ; + + // Créer une 2čme voiture + VoitureProprietaireComplet v2 = new VoitureProprietaireComplet(); + v2.proprietaire = p ; + + // Vérifier que les 2 propriétaires sont les męmes + System.out.println (v.proprietaire.anneeNaissance); + System.out.println (v2.proprietaire.anneeNaissance); + } +} + +class PersonneSecu { + int anneeNaissance ; + int numeroSecu ; +} + + + + + + + + + + + + + + + + diff --git a/02-SOURCES/VoitureProprietaireNP.java b/02-SOURCES/VoitureProprietaireNP.java new file mode 100644 index 0000000..21b3938 --- /dev/null +++ b/02-SOURCES/VoitureProprietaireNP.java @@ -0,0 +1,28 @@ + +public class VoitureProprietaireNP { + int annee ; + int puissance ; + double valeur ; + Personne proprietaire ; + + public static void main(String args[]) { + + // Déclaration d'une référence vers + // une instance de la classe VoitureProprietaire + VoitureProprietaireNP v ; + + // Créer une instance de la classe VoitureProprietaire + // et mémoriser son adresse dans la variable v + v = new VoitureProprietaireNP(); + + // Déclarer et créer une instance de la classe Personne + Personne p = new Personne (); + + // Modifier l'année de naissance + p.anneeNaissance = 1965 ; + + // Mémoriser cette personne comme proprietaire de la voiture + v.proprietaire = p ; + + } +} diff --git a/03-SOURCES/Cercle.java b/03-SOURCES/Cercle.java new file mode 100644 index 0000000..fe650d4 --- /dev/null +++ b/03-SOURCES/Cercle.java @@ -0,0 +1,30 @@ +class Cercle +{ class Centre // définition interne a Cercle + { public Centre (int x, int y) + { this.x = x ; this.y = y ; + } + public void affiche() + { System.out.println (x + ", " + y) ; + } + class CentreCentre { + + } + private int x, y ; + } + public Cercle (int x, int y, double r) + { c = new Centre (x, y) ; + this.r = r ; + } + public void affiche () + { System.out.print ("cercle de rayon " + r + " de centre ") ; + c.affiche() ; + } + public void deplace (int dx, int dy) + { c.x += dx ; c.y += dy ; // ici, on a bien acces ŕ x et y + } + private Centre c ; + private double r ; +} + + + diff --git a/03-SOURCES/ExPaquetageExistant.java b/03-SOURCES/ExPaquetageExistant.java new file mode 100644 index 0000000..aff678c --- /dev/null +++ b/03-SOURCES/ExPaquetageExistant.java @@ -0,0 +1,15 @@ +import java.io.*; + +class ExPaquetageExistant { + public static void main (String arg[]) { + System.out.println ("Affichage du dossier "); + File f = new File ("."); + String dossier [] = f.list (); + + // enhanced for loop + for (String s : dossier) { + System.out.println ("-- " + s); + } + System.out.println ("2 puissance 3 = " + Math.pow (2,3)); + } +} diff --git a/03-SOURCES/ExSousPaquetages.java b/03-SOURCES/ExSousPaquetages.java new file mode 100644 index 0000000..842bad3 --- /dev/null +++ b/03-SOURCES/ExSousPaquetages.java @@ -0,0 +1,7 @@ +import java.awt.event.*; +import java.awt.*; + + +public class ExSousPaquetages { + ActionEvent e ; +} diff --git a/03-SOURCES/Point.java b/03-SOURCES/Point.java new file mode 100644 index 0000000..860f9d1 --- /dev/null +++ b/03-SOURCES/Point.java @@ -0,0 +1,41 @@ + +public class Point { + + private double x ; + private double y ; + + Point (double x, double y) { + this.x = x ; + this.y = y ; + } + + public boolean coincide (Point pt) { + return ((pt.x == x) && (pt.y==y)); + } + + public static void main (String args []) { + Point p1 = new Point (1, 3); + Point p2 = new Point (2, 9); + Point p3 = new Point (1, 3); + + System.out.println ("p1 et p2 " + p1.coincide(p2)); // équivalent ŕ p2.coincide (p1) + System.out.println ("p1 et p3 " + p1.coincide(p3)); + + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } +} diff --git a/03-SOURCES/Tableaux.java b/03-SOURCES/Tableaux.java new file mode 100644 index 0000000..884c731 --- /dev/null +++ b/03-SOURCES/Tableaux.java @@ -0,0 +1,27 @@ + +public class Tableaux { + + //-------------------------------------------------------- + public static void main(String[] args) { + int tab [] = {1, 2, 3, 4}; + int nb = 4 ; + + // Afficher avant incrémentation + for (int val : tab) + System.out.print (val + "\t"); + + // Incrémenter les cases du tableau + incremente (tab, nb, 2); + + // Afficher aprčs incrémentation + System.out.println(); + for (int val : tab) + System.out.print (val + "\t"); + } + + //-------------------------------------------------------- + public static void incremente (int t [], int n, int inc) { + for (int i = 0; i < n; i++) + t[i] += inc ; + } +} diff --git a/03-SOURCES/TesteCercle.java b/03-SOURCES/TesteCercle.java new file mode 100644 index 0000000..d2da659 --- /dev/null +++ b/03-SOURCES/TesteCercle.java @@ -0,0 +1,9 @@ + +public class TesteCercle { + public static void main (String args[]) + { Cercle c1 = new Cercle(1, 3, 2.5) ; + c1.affiche() ; + c1.deplace (4, -2) ; + c1.affiche() ; + } + } diff --git a/03-SOURCES/gestionStocks/C1.java b/03-SOURCES/gestionStocks/C1.java new file mode 100644 index 0000000..3ba790a --- /dev/null +++ b/03-SOURCES/gestionStocks/C1.java @@ -0,0 +1,22 @@ +package gestionStocks; + +public class C1 { + + private int a ; + private float b ; + + public int getA() { + return a; + } + public void setA(int a) { + if (a > 0) + this.a = a; + } + public float getB() { + return b; + } + public void setB(float b) { + this.b = b; + } + +} diff --git a/03-SOURCES/gestionStocks/Stock.java b/03-SOURCES/gestionStocks/Stock.java new file mode 100644 index 0000000..5697dbf --- /dev/null +++ b/03-SOURCES/gestionStocks/Stock.java @@ -0,0 +1,18 @@ +package gestionStocks; +import p1.* ; +import p1.Article; +import p2.* ; + + +public class Stock { + + Article a ; + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/03-SOURCES/ingredients/MaClasse.java b/03-SOURCES/ingredients/MaClasse.java new file mode 100644 index 0000000..c78308c --- /dev/null +++ b/03-SOURCES/ingredients/MaClasse.java @@ -0,0 +1,13 @@ +package ingredients ; + +public class MaClasse { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/03-SOURCES/modele/eleves/Promotion.java b/03-SOURCES/modele/eleves/Promotion.java new file mode 100644 index 0000000..eef2b2e --- /dev/null +++ b/03-SOURCES/modele/eleves/Promotion.java @@ -0,0 +1,12 @@ +package modele.eleves ; +public class Promotion { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/03-SOURCES/p1/Article.java b/03-SOURCES/p1/Article.java new file mode 100644 index 0000000..02b1311 --- /dev/null +++ b/03-SOURCES/p1/Article.java @@ -0,0 +1,13 @@ +package p1; + +public class Article { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/03-SOURCES/p2/Article.java b/03-SOURCES/p2/Article.java new file mode 100644 index 0000000..1658d2e --- /dev/null +++ b/03-SOURCES/p2/Article.java @@ -0,0 +1,13 @@ +package p2; + +public class Article { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/03-SOURCES/paquetage1/Classe1.java b/03-SOURCES/paquetage1/Classe1.java new file mode 100644 index 0000000..e7064e3 --- /dev/null +++ b/03-SOURCES/paquetage1/Classe1.java @@ -0,0 +1,13 @@ +package paquetage1; + +public class Classe1 { + + // Normalement on commence plutot par les attributs public ! + private int d11 ; + public int d12 ; + int d13; + + private void m11 () {}; + public void m12 () {} ; + void m13 () {}; +} diff --git a/03-SOURCES/paquetage1/Classe2.java b/03-SOURCES/paquetage1/Classe2.java new file mode 100644 index 0000000..46db765 --- /dev/null +++ b/03-SOURCES/paquetage1/Classe2.java @@ -0,0 +1,8 @@ +package paquetage1; + +public class Classe2 { + + public void m21 () {}; + public void m22 () {}; + +} diff --git a/03-SOURCES/paquetage2/Classe3.java b/03-SOURCES/paquetage2/Classe3.java new file mode 100644 index 0000000..b2e1d39 --- /dev/null +++ b/03-SOURCES/paquetage2/Classe3.java @@ -0,0 +1,9 @@ +package paquetage2; + +public class Classe3 { + void m31() {}; + void m32() {}; + void m33() {}; + void m34() {}; + +} diff --git a/03-SOURCES/paquetage2/Classe4.java b/03-SOURCES/paquetage2/Classe4.java new file mode 100644 index 0000000..6c10341 --- /dev/null +++ b/03-SOURCES/paquetage2/Classe4.java @@ -0,0 +1,6 @@ +package paquetage2; + +public class Classe4 { + void m41() {}; + void m42() {}; +} diff --git a/03-SOURCES/promotion/Notes.java b/03-SOURCES/promotion/Notes.java new file mode 100644 index 0000000..7d725f5 --- /dev/null +++ b/03-SOURCES/promotion/Notes.java @@ -0,0 +1,13 @@ +package promotion; + +public class Notes { + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/04-SOURCES/A.java b/04-SOURCES/A.java new file mode 100644 index 0000000..5b87084 --- /dev/null +++ b/04-SOURCES/A.java @@ -0,0 +1,4 @@ + +public class A { + public int a ; +} diff --git a/04-SOURCES/B.java b/04-SOURCES/B.java new file mode 100644 index 0000000..385b351 --- /dev/null +++ b/04-SOURCES/B.java @@ -0,0 +1,19 @@ + +public class B extends A { + public int b ; + + public static void main(String[] args) { + A objetA = new A(); + B objetB = new B(); + + System.out.println(objetA.a); + + // ERREUR DE COMPILATION : + // "b cannot be resolved or is not a field + //System.out.println(objetA.b); + + System.out.println(objetB.b); + System.out.println(objetB.a); + } + +} diff --git a/04-SOURCES/Cercle.java b/04-SOURCES/Cercle.java new file mode 100644 index 0000000..abb4a3e --- /dev/null +++ b/04-SOURCES/Cercle.java @@ -0,0 +1,12 @@ +class Cercle extends Forme { + int rayon ; + + void afficher () { + System.out.println ("x = " + x + " , y = " + y + ", couleur = " + couleur + + " rayon = " + rayon); + } + + void agrandir (int facteur) { + rayon = rayon * facteur ; + } +} \ No newline at end of file diff --git a/04-SOURCES/Dessin.java b/04-SOURCES/Dessin.java new file mode 100644 index 0000000..73bf591 --- /dev/null +++ b/04-SOURCES/Dessin.java @@ -0,0 +1,33 @@ +class Dessin { + public static void main (String args[]) { + // Créer une forme et un cercle + Forme f = new Forme(); + f.x = 10 ; + f.y = 20 ; + + Cercle c = new Cercle (); + c.x = 30 ; + c.y = 50 ; + c.rayon = 100 ; + + // Afficher leurs attributs avec un println + System.out.println("Affichage avec println : "); + System.out.println("Forme : " + f.x + " - " + f.y); + System.out.println("Cercle : " + c.x + " - " + c.y + "-" + c.rayon); + + // Appeler la méthode afficher sur une instance de la classe Forme + System.out.println("Affichage avec l'appel de la méthode afficher() : "); + f.afficher(); + + // Appeler la méthode afficher sur une instance de la classe Cercle : + // Cette méthode est HERITEE ! + // On peut appeler cette méthode sur des instances des sous-classes + // de la classe Forme + c.afficher(); + } +} + + + + + diff --git a/04-SOURCES/DessinResolutionAppel.java b/04-SOURCES/DessinResolutionAppel.java new file mode 100644 index 0000000..e7ad829 --- /dev/null +++ b/04-SOURCES/DessinResolutionAppel.java @@ -0,0 +1,8 @@ + +public class DessinResolutionAppel { + public static void main (String args[]) { + Forme f ; + f = new Cercle(); + f.afficher(); + } +} diff --git a/04-SOURCES/Forme.java b/04-SOURCES/Forme.java new file mode 100644 index 0000000..62c33ce --- /dev/null +++ b/04-SOURCES/Forme.java @@ -0,0 +1,11 @@ +public class Forme { + int x ; + int y ; + int couleur ; + + void afficher () { + System.out.println ("x = " + x + " , y = " + y + ", couleur = " + couleur); + } +} + + diff --git a/04-SOURCES/TestInstanceOf.java b/04-SOURCES/TestInstanceOf.java new file mode 100644 index 0000000..293eb59 --- /dev/null +++ b/04-SOURCES/TestInstanceOf.java @@ -0,0 +1,35 @@ +public class TestInstanceOf { + + public static void main(String[] args) { + Forme f ; + f = new Forme (); + if (f instanceof Forme) + System.out.println ("f instance de Forme ") ; + if (f instanceof Cercle) + System.out.println ("f instance de Cercle ") ; + + Forme c ; + c = new Cercle () ; // Permis grace au polymorphisme ! + if (c instanceof Forme) + System.out.println ("c instance de Forme") ; + if (c instanceof Cercle) + System.out.println ("c instance de Cercle ") ; + + // Appel d'une méthode spécifique de la sous-classe + if (c instanceof Cercle) { + // on fait un downcasting pour rassurer le compilateur + ((Cercle) c).getRayon (); + + } + + + + + + + + + + } + +} \ No newline at end of file diff --git a/05-Diapositives/Diapositive2.JPG b/05-Diapositives/Diapositive2.JPG new file mode 100644 index 0000000..887189d Binary files /dev/null and b/05-Diapositives/Diapositive2.JPG differ diff --git a/05-Diapositives/Diapositive3.JPG b/05-Diapositives/Diapositive3.JPG new file mode 100644 index 0000000..31d6bf5 Binary files /dev/null and b/05-Diapositives/Diapositive3.JPG differ diff --git a/05-Diapositives/Diapositive4.JPG b/05-Diapositives/Diapositive4.JPG new file mode 100644 index 0000000..a87dd9e Binary files /dev/null and b/05-Diapositives/Diapositive4.JPG differ diff --git a/11-SOURCES/Box.java b/11-SOURCES/Box.java new file mode 100644 index 0000000..1f26fc7 --- /dev/null +++ b/11-SOURCES/Box.java @@ -0,0 +1,12 @@ +public class Box { + private Object valeur; + public void set(Object valeur) { + this.valeur = valeur; } + public Object get() { return valeur; } + + public static void main (String args []) { + Box box_integer = new Box (); + box_integer.set(new Integer (12)); + // Integer i = box_integer.get(); // Erreur de compilation : Type mismatch: cannot convert from Object to Integer + } +} diff --git a/11-SOURCES/generique/Box.java b/11-SOURCES/generique/Box.java new file mode 100644 index 0000000..781a893 --- /dev/null +++ b/11-SOURCES/generique/Box.java @@ -0,0 +1,30 @@ +package generique; + +public class Box { + private T t; + + public void set(T t) { this.t = t; } + public T get() { return t; } + + public static void main (String args []) { + // Déclaration et new avec le type + Box integerBox = new Box(); + integerBox.set(new Integer (12)); + // integerBox.set("Bonjour"); // ERREUR DE COMPILATION : + // The method set(Integer) in the type Box is not applicable + // for the arguments (String) + Integer i = integerBox.get(); // PAS LA PEINE DE FAIRE UN CAST + + // Ou alors avec le “diamant” et une inférence de type + Box integerBox2 = new Box<>(); + integerBox2.set(new Integer (33)); + Integer j = integerBox2.get(); + + // Le type “brut” : Box + Box rawBox = new Box(); + rawBox.set(new Integer (35)); + // Integer k = rawBox.get(); ERREUR DE COMPILATION : + // Type mismatch: cannot convert from Object to Integer + int k = (Integer) rawBox.get(); + } +} diff --git a/11-SOURCES/generique/Couple.java b/11-SOURCES/generique/Couple.java new file mode 100644 index 0000000..318e6ec --- /dev/null +++ b/11-SOURCES/generique/Couple.java @@ -0,0 +1,15 @@ +package generique; + +class Couple { + private T x, y ; // les 2 elements du couple seront du męme type + + public Couple (T premier, T second){ + x = premier ; y = second ; + } + public T getPremier () { + return x ; + } + public void affiche () { + System.out.println ("Couple : 1ere val : " + x + " - 2e val : " + y ) ; + } +} diff --git a/11-SOURCES/generique/CoupleDiff.java b/11-SOURCES/generique/CoupleDiff.java new file mode 100644 index 0000000..858bf92 --- /dev/null +++ b/11-SOURCES/generique/CoupleDiff.java @@ -0,0 +1,36 @@ +package generique; + +class CoupleDiff { + private T x; // le premier element du couple + private U y; // le second element du couple + + public CoupleDiff(T premier, U second) { + x = premier; + y = second; + } + + public T getPremier() { + return x; + } + + public void affiche() { + System.out.println ("Couple diff 1ere val : " + x + " - 2e val : " + y ) ; + } + + public static void main(String args[]) { + Integer oi1 = 3; + Double od1 = 2.5; + CoupleDiff ch1 = new CoupleDiff(oi1, + od1); + ch1.affiche(); + + Integer oi2 = 4; + CoupleDiff ch2 = new CoupleDiff(oi1, oi2); + ch2.affiche(); + + Integer n = ch1.getPremier(); + System.out.println("premier element du couple ch1 = " + n); + } + +} + diff --git a/11-SOURCES/generique/CoupleMain.java b/11-SOURCES/generique/CoupleMain.java new file mode 100644 index 0000000..28e11c0 --- /dev/null +++ b/11-SOURCES/generique/CoupleMain.java @@ -0,0 +1,17 @@ +package generique; + +public class CoupleMain +{ + public static void main (String args[]) + { + Integer oi1 = new Integer (3) ; + Integer oi2 = new Integer (5) ; + Couple ci = new Couple (oi1, oi2) ; + ci.affiche () ; + + Couple cd = new Couple (2.0, 12.0) ; + cd.affiche() ; + Double p = cd.getPremier () ; + System.out.println ("premier element du couple cd = " + p ) ; + } +} \ No newline at end of file diff --git a/11-SOURCES/generique/Pair.java b/11-SOURCES/generique/Pair.java new file mode 100644 index 0000000..20de1d3 --- /dev/null +++ b/11-SOURCES/generique/Pair.java @@ -0,0 +1,17 @@ +package generique; + +public class Pair { + // Gérer une paire clé - valeur (ayant chacune un type générique) + private K key; + private V value; + + public Pair(K key, V value) { + this.key = key; + this.value = value; + } + + public void setKey(K key) { this.key = key; } + public void setValue(V value) { this.value = value; } + public K getKey() { return key; } + public V getValue() { return value; } +} \ No newline at end of file diff --git a/11-SOURCES/generique/PairMain.java b/11-SOURCES/generique/PairMain.java new file mode 100644 index 0000000..7a85b21 --- /dev/null +++ b/11-SOURCES/generique/PairMain.java @@ -0,0 +1,22 @@ +package generique; + +public class PairMain { + // Comparer deux paires clés - valeur (ayant chacune un type générique) + public static boolean compare(Pair p1, Pair p2) { + return p1.getKey().equals(p2.getKey()) && + p1.getValue().equals(p2.getValue()); + } + + public static void main(String args[]) { + Pair p1 = new Pair<>(1, "apple"); + Pair p2 = new Pair<>(2, "pear"); + boolean same = PairMain.compare(p1, p2); + System.out.println("same = " + same); + + // On peut aussi ne pas mettre le type qui sera inféré par le compilateur : + Pair p3 = new Pair<>(1, "apple"); + Pair p4 = new Pair<>(1, "apple"); + boolean same2 = PairMain.compare(p3, p4); + System.out.println("same2 = " + same2); + } +} diff --git a/BoiteJava/.classpath b/BoiteJava/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/BoiteJava/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/BoiteJava/.project b/BoiteJava/.project new file mode 100644 index 0000000..577ce09 --- /dev/null +++ b/BoiteJava/.project @@ -0,0 +1,17 @@ + + + BoiteJava + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/BoiteJava/.settings/org.eclipse.jdt.core.prefs b/BoiteJava/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/BoiteJava/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/BoiteJava/bin/Fenetre$1.class b/BoiteJava/bin/Fenetre$1.class new file mode 100644 index 0000000..610ddbd Binary files /dev/null and b/BoiteJava/bin/Fenetre$1.class differ diff --git a/BoiteJava/bin/Fenetre.class b/BoiteJava/bin/Fenetre.class new file mode 100644 index 0000000..fe9a8f1 Binary files /dev/null and b/BoiteJava/bin/Fenetre.class differ diff --git a/BoiteJava/bin/ZDialog$1.class b/BoiteJava/bin/ZDialog$1.class new file mode 100644 index 0000000..b76f6fc Binary files /dev/null and b/BoiteJava/bin/ZDialog$1.class differ diff --git a/BoiteJava/bin/ZDialog$2.class b/BoiteJava/bin/ZDialog$2.class new file mode 100644 index 0000000..3f7eb1c Binary files /dev/null and b/BoiteJava/bin/ZDialog$2.class differ diff --git a/BoiteJava/bin/ZDialog.class b/BoiteJava/bin/ZDialog.class new file mode 100644 index 0000000..f1fbdee Binary files /dev/null and b/BoiteJava/bin/ZDialog.class differ diff --git a/BoiteJava/bin/ZDialogInfo.class b/BoiteJava/bin/ZDialogInfo.class new file mode 100644 index 0000000..ca98936 Binary files /dev/null and b/BoiteJava/bin/ZDialogInfo.class differ diff --git a/BoiteJava/src/Fenetre.java b/BoiteJava/src/Fenetre.java new file mode 100644 index 0000000..71b48c8 --- /dev/null +++ b/BoiteJava/src/Fenetre.java @@ -0,0 +1,32 @@ +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +public class Fenetre extends JFrame { + private JButton bouton = new JButton("Appel ŕ la ZDialog"); + + public Fenetre(){ + this.setTitle("Ma JFrame"); + this.setSize(300, 100); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLocationRelativeTo(null); + this.getContentPane().setLayout(new FlowLayout()); + this.getContentPane().add(bouton); + bouton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent arg0) { + ZDialog zd = new ZDialog(null, "Coucou les ZérOs", true); + ZDialogInfo zInfo = zd.showZDialog(); + JOptionPane jop = new JOptionPane(); + jop.showMessageDialog(null, zInfo.toString(), "Informations personnage", JOptionPane.INFORMATION_MESSAGE); + } + }); + this.setVisible(true); + } + + public static void main(String[] main){ + Fenetre fen = new Fenetre(); + } +} \ No newline at end of file diff --git a/BoiteJava/src/ZDialog.java b/BoiteJava/src/ZDialog.java new file mode 100644 index 0000000..38560a3 --- /dev/null +++ b/BoiteJava/src/ZDialog.java @@ -0,0 +1,164 @@ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.ButtonGroup; +import javax.swing.JTextField; + +public class ZDialog extends JDialog { + private ZDialogInfo zInfo = new ZDialogInfo(); + private boolean sendData; + private JLabel nomLabel, sexeLabel, cheveuxLabel, ageLabel, tailleLabel,taille2Label, icon; + private JRadioButton tranche1, tranche2, tranche3, tranche4; + private JComboBox sexe, cheveux; + private JTextField nom, taille; + + public ZDialog(JFrame parent, String title, boolean modal){ + super(parent, title, modal); + this.setSize(550, 270); + this.setLocationRelativeTo(null); + this.setResizable(false); + this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + this.initComponent(); + } + + public ZDialogInfo showZDialog(){ + this.sendData = false; + this.setVisible(true); + return this.zInfo; + } + + private void initComponent(){ + //Icône + icon = new JLabel(new ImageIcon("images/icone.jpg")); + JPanel panIcon = new JPanel(); + panIcon.setBackground(Color.white); + panIcon.setLayout(new BorderLayout()); + panIcon.add(icon); + + //Le nom + JPanel panNom = new JPanel(); + panNom.setBackground(Color.white); + panNom.setPreferredSize(new Dimension(220, 60)); + nom = new JTextField(); + nom.setPreferredSize(new Dimension(100, 25)); + panNom.setBorder(BorderFactory.createTitledBorder("Nom du personnage")); + nomLabel = new JLabel("Saisir un nom :"); + panNom.add(nomLabel); + panNom.add(nom); + + //Le sexe + JPanel panSexe = new JPanel(); + panSexe.setBackground(Color.white); + panSexe.setPreferredSize(new Dimension(220, 60)); + panSexe.setBorder(BorderFactory.createTitledBorder("Sexe du personnage")); + sexe = new JComboBox(); + sexe.addItem("Masculin"); + sexe.addItem("Féminin"); + sexe.addItem("Indéterminé"); + sexeLabel = new JLabel("Sexe : "); + panSexe.add(sexeLabel); + panSexe.add(sexe); + + //L'âge + JPanel panAge = new JPanel(); + panAge.setBackground(Color.white); + panAge.setBorder(BorderFactory.createTitledBorder("Age du personnage")); + panAge.setPreferredSize(new Dimension(440, 60)); + tranche1 = new JRadioButton("15 - 25 ans"); + tranche1.setSelected(true); + tranche2 = new JRadioButton("26 - 35 ans"); + tranche3 = new JRadioButton("36 - 50 ans"); + tranche4 = new JRadioButton("+ de 50 ans"); + ButtonGroup bg = new ButtonGroup(); + bg.add(tranche1); + bg.add(tranche2); + bg.add(tranche3); + bg.add(tranche4); + panAge.add(tranche1); + panAge.add(tranche2); + panAge.add(tranche3); + panAge.add(tranche4); + + //La taille + JPanel panTaille = new JPanel(); + panTaille.setBackground(Color.white); + panTaille.setPreferredSize(new Dimension(220, 60)); + panTaille.setBorder(BorderFactory.createTitledBorder("Taille du personnage")); + tailleLabel = new JLabel("Taille : "); + taille2Label = new JLabel(" cm"); + taille = new JTextField("180"); + taille.setPreferredSize(new Dimension(90, 25)); + panTaille.add(tailleLabel); + panTaille.add(taille); + panTaille.add(taille2Label); + + //La couleur des cheveux + JPanel panCheveux = new JPanel(); + panCheveux.setBackground(Color.white); + panCheveux.setPreferredSize(new Dimension(220, 60)); + panCheveux.setBorder(BorderFactory.createTitledBorder("Couleur de cheveux du personnage")); + cheveux = new JComboBox(); + cheveux.addItem("Blond"); + cheveux.addItem("Brun"); + cheveux.addItem("Roux"); + cheveux.addItem("Blanc"); + cheveuxLabel = new JLabel("Cheveux"); + panCheveux.add(cheveuxLabel); + panCheveux.add(cheveux); + + JPanel content = new JPanel(); + content.setBackground(Color.white); + content.add(panNom); + content.add(panSexe); + content.add(panAge); + content.add(panTaille); + content.add(panCheveux); + + JPanel control = new JPanel(); + JButton okBouton = new JButton("OK"); + + okBouton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent arg0) { + zInfo = new ZDialogInfo(nom.getText(), (String)sexe.getSelectedItem(), getAge(), (String)cheveux.getSelectedItem() ,getTaille()); + setVisible(false); + } + + public String getAge(){ + return (tranche1.isSelected()) ? tranche1.getText() : + (tranche2.isSelected()) ? tranche2.getText() : + (tranche3.isSelected()) ? tranche3.getText() : + (tranche4.isSelected()) ? tranche4.getText() : + tranche1.getText(); + } + + public String getTaille(){ + return (taille.getText().equals("")) ? "180" : taille.getText(); + } + }); + + JButton cancelBouton = new JButton("Annuler"); + cancelBouton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent arg0) { + setVisible(false); + } + }); + + control.add(okBouton); + control.add(cancelBouton); + + this.getContentPane().add(panIcon, BorderLayout.WEST); + this.getContentPane().add(content, BorderLayout.CENTER); + this.getContentPane().add(control, BorderLayout.SOUTH); + } +} \ No newline at end of file diff --git a/BoiteJava/src/ZDialogInfo.java b/BoiteJava/src/ZDialogInfo.java new file mode 100644 index 0000000..a68ef69 --- /dev/null +++ b/BoiteJava/src/ZDialogInfo.java @@ -0,0 +1,28 @@ +public class ZDialogInfo { + private String nom, sexe, age, cheveux, taille; + + public ZDialogInfo(){} + public ZDialogInfo(String nom, String sexe, String age, String cheveux, String taille){ + this.nom = nom; + this.sexe = sexe; + this.age = age; + this.cheveux = cheveux; + this.taille = taille; + } + + public String toString(){ + String str; + if(this.nom != null && this.sexe != null && this.taille != null && this.age != null && this.cheveux != null){ + str = "Description de l'objet InfoZDialog"; + str += "Nom : " + this.nom + "\n"; + str += "Sexe : " + this.sexe + "\n"; + str += "Age : " + this.age + "\n"; + str += "Cheveux : " + this.cheveux + "\n"; + str += "Taille : " + this.taille + "\n"; + } + else{ + str = "Aucune information !"; + } + return str; + } +} \ No newline at end of file diff --git a/ComboBox/.classpath b/ComboBox/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/ComboBox/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/ComboBox/.project b/ComboBox/.project new file mode 100644 index 0000000..5f47955 --- /dev/null +++ b/ComboBox/.project @@ -0,0 +1,17 @@ + + + ComboBox + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/ComboBox/.settings/org.eclipse.jdt.core.prefs b/ComboBox/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/ComboBox/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/ComboBox/bin/Fenetre$Bouton2Listener.class b/ComboBox/bin/Fenetre$Bouton2Listener.class new file mode 100644 index 0000000..dfc976d Binary files /dev/null and b/ComboBox/bin/Fenetre$Bouton2Listener.class differ diff --git a/ComboBox/bin/Fenetre$BoutonListener.class b/ComboBox/bin/Fenetre$BoutonListener.class new file mode 100644 index 0000000..3fc27f5 Binary files /dev/null and b/ComboBox/bin/Fenetre$BoutonListener.class differ diff --git a/ComboBox/bin/Fenetre$FormeListener.class b/ComboBox/bin/Fenetre$FormeListener.class new file mode 100644 index 0000000..5ec175a Binary files /dev/null and b/ComboBox/bin/Fenetre$FormeListener.class differ diff --git a/ComboBox/bin/Fenetre$PlayAnimation.class b/ComboBox/bin/Fenetre$PlayAnimation.class new file mode 100644 index 0000000..59f9c7f Binary files /dev/null and b/ComboBox/bin/Fenetre$PlayAnimation.class differ diff --git a/ComboBox/bin/Fenetre.class b/ComboBox/bin/Fenetre.class new file mode 100644 index 0000000..a0c11b4 Binary files /dev/null and b/ComboBox/bin/Fenetre.class differ diff --git a/ComboBox/bin/Panneau.class b/ComboBox/bin/Panneau.class new file mode 100644 index 0000000..4e227d5 Binary files /dev/null and b/ComboBox/bin/Panneau.class differ diff --git a/ComboBox/src/Fenetre.java b/ComboBox/src/Fenetre.java new file mode 100644 index 0000000..fd34bfd --- /dev/null +++ b/ComboBox/src/Fenetre.java @@ -0,0 +1,112 @@ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class Fenetre extends JFrame{ + private Panneau pan = new Panneau(); + private JButton bouton = new JButton("Go"); + private JButton bouton2 = new JButton("Stop"); + private JPanel container = new JPanel(); + private JLabel label = new JLabel("Choix de la forme"); + private int compteur = 0; + private boolean animated = true; + private boolean backX, backY; + private int x, y; + private Thread t; + private JComboBox combo = new JComboBox(); + + public Fenetre(){ + this.setTitle("Animation"); + this.setSize(300, 300); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLocationRelativeTo(null); + + container.setBackground(Color.white); + container.setLayout(new BorderLayout()); + container.add(pan, BorderLayout.CENTER); + + bouton.addActionListener(new BoutonListener()); + bouton2.addActionListener(new Bouton2Listener()); + bouton2.setEnabled(false); + JPanel south = new JPanel(); + south.add(bouton); + south.add(bouton2); + container.add(south, BorderLayout.SOUTH); + + combo.addItem("ROND"); + combo.addItem("CARRE"); + combo.addItem("TRIANGLE"); + combo.addItem("ETOILE"); + combo.addActionListener(new FormeListener()); + + JPanel top = new JPanel(); + top.add(label); + top.add(combo); + container.add(top, BorderLayout.NORTH); + this.setContentPane(container); + this.setVisible(true); + } + + private void go(){ + x = pan.getPosX(); + y = pan.getPosY(); + while(this.animated){ + if(x < 1) backX = false; + if(x > pan.getWidth() - 50) backX = true; + if(y < 1) backY = false; + if(y > pan.getHeight() - 50) backY = true; + if(!backX) pan.setPosX(++x); + else pan.setPosX(--x); + if(!backY) pan.setPosY(++y); + else pan.setPosY(--y); + pan.repaint(); + try { + Thread.sleep(3); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + //Classe écoutant notre bouton + public class BoutonListener implements ActionListener{ + public void actionPerformed(ActionEvent arg0) { + animated = true; + t = new Thread(new PlayAnimation()); + t.start(); + bouton.setEnabled(false); + bouton2.setEnabled(true); + } + } + + class Bouton2Listener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + animated = false; + bouton.setEnabled(true); + bouton2.setEnabled(false); + } + } + + class PlayAnimation implements Runnable{ + public void run() { + go(); + } + } + + class FormeListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + //La méthode retourne un Object puisque nous passons des Object dans une liste + //Il faut donc utiliser la méthode toString() pour retourner un String (ou utiliser un cast) + pan.setForme(combo.getSelectedItem().toString()); + } + } +} \ No newline at end of file diff --git a/ComboBox/src/Panneau.java b/ComboBox/src/Panneau.java new file mode 100644 index 0000000..4ede574 --- /dev/null +++ b/ComboBox/src/Panneau.java @@ -0,0 +1,90 @@ +import java.awt.Color; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; + +import javax.swing.JPanel; + +public class Panneau extends JPanel { + private int posX = -50; + private int posY = -50; + private String forme = "ROND"; + + public void paintComponent(Graphics g){ + //On choisit une couleur de fond pour le rectangle + g.setColor(Color.white); + //On le dessine de sorte qu'il occupe toute la surface + g.fillRect(0, 0, this.getWidth(), this.getHeight()); + //On redéfinit une couleur pour le rond + g.setColor(Color.red); + //On délčgue la méthode de dessin ŕ la méthode draw() + draw(g); + } + + private void draw(Graphics g){ + if(this.forme.equals("ROND")){ + g.fillOval(posX, posY, 50, 50); + } + if(this.forme.equals("CARRE")){ + g.fillRect(posX, posY, 50, 50); + } + if(this.forme.equals("TRIANGLE")){ + //Calcul des sommets + //Le sommet 1 se situe ŕ la moitié du côté supérieur du carré + int s1X = posX + 25; + int s1Y = posY; + //Le sommet 2 se situe en bas ŕ droite + int s2X = posX + 50; + int s2Y = posY + 50; + //Le sommet 3 se situe en bas ŕ gauche + int s3X = posX; + int s3Y = posY + 50; + //Nous créons deux tableaux de coordonnées + int[] ptsX = {s1X, s2X, s3X}; + int[] ptsY = {s1Y, s2Y, s3Y}; + //Nous utilisons la méthode fillPolygon() + g.fillPolygon(ptsX, ptsY, 3); + } + if(this.forme.equals("ETOILE")){ + //Pour l'étoile, on se contente de tracer des lignes dans le carré + //correspondant ŕ peu prčs ŕ une étoile... + //Mais ce code peut ętre amélioré ! + int s1X = posX + 25; + int s1Y = posY; + int s2X = posX + 50; + int s2Y = posY + 50; + g.drawLine(s1X, s1Y, s2X, s2Y); + int s3X = posX; + int s3Y = posY + 17; + g.drawLine(s2X, s2Y, s3X, s3Y); + int s4X = posX + 50; + int s4Y = posY + 17; + g.drawLine(s3X, s3Y, s4X, s4Y); + int s5X = posX; + int s5Y = posY + 50; + g.drawLine(s4X, s4Y, s5X, s5Y); + g.drawLine(s5X, s5Y, s1X, s1Y); + } + } + + public void setForme(String form){ + this.forme = form; + } + + public int getPosX() { + return posX; + } + + public void setPosX(int posX) { + this.posX = posX; + } + + public int getPosY() { + return posY; + } + + public void setPosY(int posY) { + this.posY = posY; + } +} \ No newline at end of file diff --git a/Convertisseur/.classpath b/Convertisseur/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/Convertisseur/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Convertisseur/.project b/Convertisseur/.project new file mode 100644 index 0000000..be187d4 --- /dev/null +++ b/Convertisseur/.project @@ -0,0 +1,17 @@ + + + Convertisseur + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Convertisseur/.settings/org.eclipse.jdt.core.prefs b/Convertisseur/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Convertisseur/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Convertisseur/bin/Convertisseur.class b/Convertisseur/bin/Convertisseur.class new file mode 100644 index 0000000..8badc79 Binary files /dev/null and b/Convertisseur/bin/Convertisseur.class differ diff --git a/Convertisseur/src/Convertisseur.java b/Convertisseur/src/Convertisseur.java new file mode 100644 index 0000000..230332b --- /dev/null +++ b/Convertisseur/src/Convertisseur.java @@ -0,0 +1,133 @@ +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + +public class Convertisseur extends JFrame implements ActionListener { + + private JLabel lbTitre = new JLabel("Convertisseur"); + private JButton btEuro = new JButton("Euro"); + private JButton btFr = new JButton("Franc"); + private JButton btAc = new JButton("Annuler"); + private JButton btApropos = new JButton("Ŕ propos"); + private JButton btTaux = new JButton("Taux"); + private JButton btQuitter = new JButton("Quitter"); + + private JTextField txtMontant = new JTextField(); + + private String devise; + private float taux; + + public Convertisseur() { + + this.devise = "Franc"; + this.taux = (float)6.56; + + this.setBounds(200, 200, 450, 300); + this.setTitle("Convertisseur"); + this.setResizable(false); + this.setLayout(null); + this.lbTitre.setBounds(175, 20, 200, 20); + this.add(this.lbTitre); + + ImageIcon icon = new ImageIcon("src/convertisseur.png"); + JLabel lbIcon = new JLabel(icon); + lbIcon.setBounds(10, 10, 70, 70); + this.add(lbIcon); + + this.btEuro.setBounds(50, 100, 100, 20); + this.add(this.btEuro); + this.txtMontant.setBounds(170, 100, 100, 20); + this.add(this.txtMontant); + this.btFr.setBounds(290, 100, 100, 20); + this.add(this.btFr); + + this.btApropos.setBounds(50, 170, 100, 20); + this.add(this.btApropos); + this.btAc.setBounds(170, 170, 100, 20); + this.add(this.btAc); + this.btTaux.setBounds(290, 170, 100, 20); + this.add(this.btTaux); + this.btQuitter.setBounds(170, 220, 100, 20); + this.add(this.btQuitter); + + this.btAc.addActionListener(this); + this.btEuro.addActionListener(this); + this.btFr.addActionListener(this); + this.btApropos.addActionListener(this); + this.btTaux.addActionListener(this); + this.btQuitter.addActionListener(this); + + this.setVisible(true); + + } + + public static void main(String[] args) { + Convertisseur monC = new Convertisseur(); + } + + @Override + public void actionPerformed(ActionEvent e) { + if(e.getSource() == this.btAc) { + this.txtMontant.setText(""); + } else if (e.getSource() == this.btEuro) { + if (this.txtMontant.getText().equals("")) { + JOptionPane.showMessageDialog(this,"Veuillez saisir un montant", "Erreur", JOptionPane.ERROR_MESSAGE); + } else { + float mt=0; + try { + mt = Float.parseFloat(this.txtMontant.getText()); + mt=(float) (mt/this.taux); + this.txtMontant.setText(""+mt); + } + catch(NumberFormatException exp) { + JOptionPane.showMessageDialog(this,"Veuillez vérifier votre montant", "Erreur", JOptionPane.ERROR_MESSAGE); + } + } + } else if (e.getSource() == this.btFr) { + if (this.txtMontant.getText().equals("")) { + JOptionPane.showMessageDialog(this,"Veuillez saisir un montant", "Erreur", JOptionPane.ERROR_MESSAGE); + } else { + float mt=0; + try { + mt = Float.parseFloat(this.txtMontant.getText()); + mt=(float) (mt*this.taux); + this.txtMontant.setText(""+mt); + } + catch(NumberFormatException exp) { + JOptionPane.showMessageDialog(this,"Veuillez vérifier votre montant", "Erreur", JOptionPane.ERROR_MESSAGE); + } + } + } else if (e.getSource() == this.btApropos) { + JOptionPane.showMessageDialog(this,"Logiciel réalisé le 15/12/2016\n" + + "\n Alexandre DA COSTA" + + "\n Utilisant le framework Swing", + "Information", JOptionPane.INFORMATION_MESSAGE); + } else if (e.getSource() == this.btQuitter) { + int r = JOptionPane.showConfirmDialog(this, "Voulez-vous quitter ?", "Quitter", JOptionPane.OK_CANCEL_OPTION); + if (r == 0) { + this.dispose(); + } + } else if (e.getSource() == this.btTaux) { + try { + this.devise = JOptionPane.showInputDialog(this, "Saisir le nom de la devise"); + this.taux = Float.parseFloat(JOptionPane.showInputDialog(this, "Saisir le taux de la devise")); + this.btFr.setText(this.devise); + } catch (NumberFormatException exp) { + this.taux = (float) 6.56; + this.devise = "Franc"; + this.btFr.setText(this.devise); + } catch (NullPointerException exp) { + this.taux = (float) 6.56; + this.devise = "Franc"; + this.btFr.setText(this.devise); + } + } + } + +} \ No newline at end of file diff --git a/EX-01/.classpath b/EX-01/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/EX-01/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/EX-01/.project b/EX-01/.project new file mode 100644 index 0000000..a2228be --- /dev/null +++ b/EX-01/.project @@ -0,0 +1,17 @@ + + + EX-01 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/EX-01/.settings/org.eclipse.jdt.core.prefs b/EX-01/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/EX-01/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/EX-01/bin/AfficherSaisir.class b/EX-01/bin/AfficherSaisir.class new file mode 100644 index 0000000..e4ff622 Binary files /dev/null and b/EX-01/bin/AfficherSaisir.class differ diff --git a/EX-01/bin/ApplicationGridLayout.class b/EX-01/bin/ApplicationGridLayout.class new file mode 100644 index 0000000..ef44ab1 Binary files /dev/null and b/EX-01/bin/ApplicationGridLayout.class differ diff --git a/EX-01/bin/ApplicationLayoutCombinaison.class b/EX-01/bin/ApplicationLayoutCombinaison.class new file mode 100644 index 0000000..ac5ce12 Binary files /dev/null and b/EX-01/bin/ApplicationLayoutCombinaison.class differ diff --git a/EX-01/bin/CalcButton.class b/EX-01/bin/CalcButton.class new file mode 100644 index 0000000..7f82d26 Binary files /dev/null and b/EX-01/bin/CalcButton.class differ diff --git a/EX-01/bin/Calculatrice.class b/EX-01/bin/Calculatrice.class new file mode 100644 index 0000000..6399079 Binary files /dev/null and b/EX-01/bin/Calculatrice.class differ diff --git a/EX-01/bin/Calculette.class b/EX-01/bin/Calculette.class new file mode 100644 index 0000000..04b8174 Binary files /dev/null and b/EX-01/bin/Calculette.class differ diff --git a/EX-01/bin/Stock.class b/EX-01/bin/Stock.class new file mode 100644 index 0000000..b561c38 Binary files /dev/null and b/EX-01/bin/Stock.class differ diff --git a/EX-01/src/AfficherSaisir.java b/EX-01/src/AfficherSaisir.java new file mode 100644 index 0000000..7f55660 --- /dev/null +++ b/EX-01/src/AfficherSaisir.java @@ -0,0 +1,23 @@ +import java.lang.Math; +import java.util.Scanner; +import java.text.*; +public class AfficherSaisir { + +public static void main(String[] args){ + System.out.println("Bonjour") ; + + //Afficher ensuite le nombre pi (Math.PI) de maničre formatée avec 3 chiffres aprčs la virgule. + DecimalFormat df = new DecimalFormat ( ) ; + df.setMaximumFractionDigits ( 3 ) ; //arrondi ŕ 3 chiffres apres la virgules + System.out.println(df.format(Math.PI)); + + //Saisir un nombre n ; afficher un nombre aléatoire entre 0 et n + @SuppressWarnings("resource") +Scanner sc = new Scanner(System.in); + System.out.println("Veuillez saisir un chiffre :"); + String str = sc.nextLine(); + System.out.println("Vous avez saisi : " + str); + + System.out.println (Math.random()); + } +} \ No newline at end of file diff --git a/EX-01/src/ApplicationGridLayout.java b/EX-01/src/ApplicationGridLayout.java new file mode 100644 index 0000000..71079c9 --- /dev/null +++ b/EX-01/src/ApplicationGridLayout.java @@ -0,0 +1,11 @@ +import java.awt.GridLayout; +import javax.swing.*; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +public class ApplicationGridLayout { + public static void main(String[] args) { + + } +} diff --git a/EX-01/src/ApplicationLayoutCombinaison.java b/EX-01/src/ApplicationLayoutCombinaison.java new file mode 100644 index 0000000..a18b3b6 --- /dev/null +++ b/EX-01/src/ApplicationLayoutCombinaison.java @@ -0,0 +1,56 @@ +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.*; + +public class ApplicationLayoutCombinaison { + + public static void main(String[] args) { + // Créer la fenetre de l'application + JFrame mainFrame = new JFrame("Combinaison de plusieurs layout"); + + // Créer un panel global + JPanel panelGlobal = new JPanel(); + + // Changer le layout du panel global + panelGlobal.setLayout(new BorderLayout()); + + // Ajouter des boutons + panelGlobal.add(new JButton("East"), BorderLayout.NORTH); + panelGlobal.add(new JButton("West"), BorderLayout.SOUTH); + + // Créer le panel du milieu + JLabel panelLabel = new JLabel(); + panelLabel.add(new JLabel("Bouton 1")); + panelGlobal.add(panelLabel, BorderLayout.NORTH); + + JPanel panelBoutons = new JPanel(); + panelBoutons.setLayout(new GridLayout(2, 2)); + panelBoutons.add(new JButton("Bouton 1")); + panelBoutons.add(new JButton("Bouton 2")); + panelBoutons.add(new JButton("Bouton 3")); + panelBoutons.add(new JButton("Bouton 4")); + panelBoutons.add(new JButton("Bouton 1")); + panelBoutons.add(new JButton("Bouton 2")); + panelBoutons.add(new JButton("Bouton 3")); + panelBoutons.add(new JButton("Bouton 4")); + panelBoutons.add(new JButton("Bouton 1")); + panelBoutons.add(new JButton("Bouton 2")); + panelBoutons.add(new JButton("Bouton 3")); + panelBoutons.add(new JButton("Bouton 4")); + panelGlobal.add(panelBoutons, BorderLayout.SOUTH); + + + //Ajout du panel a la fenetre + mainFrame.add(panelGlobal); + + //'Compactage' de la fenetre + mainFrame.pack(); + + //On quitte l'application quand la fenetre est fermee + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + //Affichage de la fen?tre + mainFrame.setVisible(true); + } +} diff --git a/EX-01/src/Calculatrice.java b/EX-01/src/Calculatrice.java new file mode 100644 index 0000000..8a5439a --- /dev/null +++ b/EX-01/src/Calculatrice.java @@ -0,0 +1,332 @@ +import java.awt.*; +import java.applet.Applet; +import java.lang.Integer; +import javax.swing.JLabel; +import javax.swing.JPanel; + + +public class Calculatrice extends Applet +{ +//déclaration des variables pour les boutons + +TextField txtDisp; +public final int NONE = 0; +public final int ADD = 1; +public final int SUB = 2; +public final int MUL = 3; +public final int DIV = 4; +public final int NEG = 5; +public final int SQRT = 6; +public final int EQ = 7; +public final int C = 8; +public final int AC = 9; +public final int DECSEP = -1; + +String msDecimal; +int mnOp = NONE; +boolean mbNewNumber = true; +boolean mbDecimal = false; +double mdReg = 0.0; + + +/* +Initialisation de l'applet +Mise en page des boutons : couleur, et police de caractčre +*/ + +public void init() +{ +CalcButton btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9; +CalcButton btnDecSep, btnNeg, btnSqrt, btnPlus, btnMinus; +CalcButton btnTimes, btnDiv, btnEqual, btnClear, btnAllClear; + +setLayout(null); +setFont(new Font("Helvetica", Font.PLAIN, 14)); +//setBackground(new Color(0xCC, 0xCC, 0xCC)); + +btn0 = new CalcButton("0",NONE, 0); +add(btn0); +btn0.reshape(64, 164, 40, 24); +btn0.setForeground(Color.blue); + +btn1 = new CalcButton("1",NONE, 1); +add(btn1); +btn1.reshape(64, 132, 40, 24); +btn1.setForeground(Color.blue); + +btn2 = new CalcButton("2",NONE, 2); +add(btn2); +btn2.reshape(120, 132, 40, 24); +btn2.setForeground(Color.blue); + +btn3 = new CalcButton("3",NONE, 3); +add(btn3); +btn3.reshape(176, 132, 40, 24); +btn3.setForeground(Color.blue); + +btn4 = new CalcButton("4",NONE , 4); +add(btn4); +btn4.reshape(64, 100, 40, 24); +btn4.setForeground(Color.blue); + +btn5 = new CalcButton("5",NONE , 5); +add(btn5); +btn5.reshape(120, 100, 40, 24); +btn5.setForeground(Color.blue); + +btn6 = new CalcButton("6",NONE, 6); +add(btn6); +btn6.reshape(176, 100, 40, 24); +btn6.setForeground(Color.blue); + +btn7 = new CalcButton("7",NONE, 7); +add(btn7); +btn7.reshape(64, 68, 40, 24); +btn7.setForeground(Color.blue); + +btn8 = new CalcButton("8",NONE, 8); +add(btn8); +btn8.reshape(120, 68, 40, 24); +btn8.setForeground(Color.blue); + +btn9 = new CalcButton("9",NONE, 9); +add(btn9); +btn9.reshape(176, 68, 40, 24); +btn9.setForeground(Color.blue); + +btnDecSep = new CalcButton("·",NONE, DECSEP); +add(btnDecSep); +btnDecSep.reshape(176, 164, 40, 24); +btnDecSep.setForeground(Color.blue); + + +btnNeg = new CalcButton("+/-", NEG, 0); +add(btnNeg); +btnNeg.reshape(120, 164, 40, 24); +btnNeg.setForeground(Color.blue); + + +btnSqrt = new CalcButton("Sqrt", SQRT, 0); +add(btnSqrt); +btnSqrt.reshape(8, 80, 40, 24); +btnSqrt.setForeground(Color.red); + + +btnPlus = new CalcButton("+", ADD, 0); +add(btnPlus); +btnPlus.reshape(232, 112, 40, 56); +btnPlus.setForeground(Color.red); + + +btnMinus = new CalcButton("-", SUB, 0); +add(btnMinus); +btnMinus.reshape(288, 112, 40, 24); +btnMinus.setForeground(Color.red); + + +btnTimes = new CalcButton("×", MUL, 0); +add(btnTimes); +btnTimes.reshape(232, 80, 40, 24); +btnTimes.setForeground(Color.red); + + +btnDiv = new CalcButton("÷", DIV, 0); +add(btnDiv); +btnDiv.reshape(288, 80, 40, 24); +btnDiv.setForeground(Color.red); + + +btnEqual = new CalcButton("=", EQ, 0); +add(btnEqual); +btnEqual.reshape(288, 144, 40, 24); +btnEqual.setForeground(Color.red); + + +btnClear = new CalcButton("C", C, 0); +add(btnClear); +btnClear.reshape(8, 112, 40, 24); +btnClear.setForeground(Color.red); + + +btnAllClear = new CalcButton("AC", AC, 0); +add(btnAllClear); +btnAllClear.reshape(8, 144, 40, 24); +btnAllClear.setForeground(Color.red); + + +txtDisp = new TextField("0", 80); +txtDisp.setEditable(false); +add(txtDisp); +txtDisp.reshape(64, 8, 268, 27); +String sOneTenth = (new Double(0.1)).toString(); +msDecimal = sOneTenth.substring(sOneTenth.length()-2).substring(0, 1); +} + +/* +Création de la frame +*/ + +public static void main(String args[]) +{ +Frame frm = new Frame("Ma premiere calculatrice en java"); +Calculatrice ex1 = new Calculatrice(); +ex1.init(); +frm.add("Center", ex1); +frm.pack(); +frm.resize(350, 350); +frm.show(); +} + +/* +Fonction pour les nombres décimaux +*/ + +public void append(int nValue) +{ +String sDigit; + +if(nValue == DECSEP) +if(!mbDecimal) +{ +if(mbNewNumber) +{ +txtDisp.setText("0"); +mbNewNumber = false; +} +mbDecimal = true; +sDigit = msDecimal; +} +else +return; +else +sDigit = (new Integer(nValue)).toString(); +if(mbNewNumber) +{ +txtDisp.setText(sDigit); +mbNewNumber = false; +} +else +txtDisp.setText(txtDisp.getText() + sDigit); +repaint(); +} + +/* +Initialisation des opérations +*/ +public void doOp(int nNewOp) +{ +double dDisp; +dDisp = (new Double(txtDisp.getText())).doubleValue(); +switch(nNewOp) +{ +case NEG: +case SQRT: +case C: +case AC: + +switch(nNewOp) +{ +case NEG: +txtDisp.setText((new Double(-dDisp)).toString()); +break; + +case SQRT: +txtDisp.setText((new Double(Math.sqrt(dDisp))).toString()); +mbNewNumber = true; +mbDecimal = false; +break; +case C: +mbNewNumber = true; +mbDecimal = false; +txtDisp.setText("0"); +break; +case AC: +mnOp = NONE; +mbNewNumber = true; +mbDecimal = false; +mdReg = 0.0; +txtDisp.setText("0"); +break; +} +break; +case ADD: +case SUB: +case MUL: +case DIV: +case EQ: + +switch(mnOp) +{ +case ADD: +mdReg = mdReg + dDisp; +break; + +case SUB: +mdReg = mdReg - dDisp; +break; +case MUL: +mdReg = mdReg * dDisp; +break; +case DIV: +mdReg = mdReg / dDisp; +break; +case EQ: +case NONE: +mdReg = dDisp; +break; +} +mnOp = nNewOp; +mbNewNumber = true; +mbDecimal = false; +txtDisp.setText((new Double(mdReg)).toString()); +break; +} +} +/* +Initialisation de la calculatrice +*/ + +public Calculatrice() { +try { +jbInit(); +} +catch (Exception e) { +e.printStackTrace(); +} +} + +private void jbInit() throws Exception { +this.setBackground(SystemColor.text); +this.setSize(new Dimension(300, 316)); +} +} + +/* +Initialisation des boutons +*/ + +class CalcButton extends Button +{ +int mnOp; +int mnValue; + +CalcButton(String sText, int nOp, int nValue) +{ +super(sText); +mnOp = nOp; +mnValue = nValue; +} + +public boolean action(Event evt, Object arg) +{ +Calculatrice par = (Calculatrice)getParent(); + +if(mnOp == par.NONE) +par.append(mnValue); +else +{ +par.doOp(mnOp); +} +return true; +} +} \ No newline at end of file diff --git a/EX-01/src/Calculette.java b/EX-01/src/Calculette.java new file mode 100644 index 0000000..9d56a57 --- /dev/null +++ b/EX-01/src/Calculette.java @@ -0,0 +1,31 @@ +import javax.swing.*; +public class Calculette { + public static void main(String[] args) { + //Création de la fenętre de l'application + JFrame mainFrame = new JFrame("Exemple de FlowLayout"); + + //Création d'un panel + JPanel panelBoutons = new JPanel(); + + //Changement du layout du panel + + + + //Ajout des boutons + + + + + + + //Ajout du panel ŕ la fenętre + mainFrame.add(panelBoutons); + + //'Compactage' de la fenętre + mainFrame.pack(); + + //On quitte l'application quand la fenętre est fermée + mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + //Affichage de la fenętre + mainFrame.setVisible(true);}} diff --git a/EX-01/src/Stock.java b/EX-01/src/Stock.java new file mode 100644 index 0000000..42acaa8 --- /dev/null +++ b/EX-01/src/Stock.java @@ -0,0 +1,72 @@ +import java.util.Arrays; +public class Stock { +public static void main(String[] args) { + + + //----------------------------------------------------- + // Tableau de type de base + //----------------------------------------------------- +// Déclarer les constantes + +// Déclarer les tableaux + char t [] ; + double t2 [] ; + // DECLARATION d'une REFERENCE de type tableau + // CETTE CASE POURRA ENSUITE CONTENIR L’ADRESSE D’UN TABLEAU + +//Allouer les tableaux + t = new char [3]; + t2 = new double [3]; + //ALLOCATION d'une zone memoire pour n entiers dans le TAS + +//Mettre des valeurs dans les cases + t[0] = 'A' ; + t[1] = 'B'; + t[2] = 'C' ; + + t2[0] = 10.5 ; + t2[1] = 2.5 ; + t2[2] = 21.5 ; + // AFFECTATION d'une valeur une case du tableau + +//Parcours des deux tableaux avec deux boucles foreach + // Avec une boucle foreach + //Tableau 1 + System.out.println ("\n Parcours du tableau avec une boucle foreach : "); + for(int i = 0; i < t.length; i++) + { + System.out.println("Ŕ l'emplacement " + i +" du tableau nous avons = " + t[i]); + } + //Tableau 2 + for(int i = 0; i < t2.length; i++) + { + System.out.println("Ŕ l'emplacement " + i +" du tableau nous avons = " + t2[i]); } +//Parcours des deux tableaux en parallčle avec une boucle while et un booleen + //Tableau 1 + boolean fini = false ; + int i = 0 ; + System.out.println ("\n Parcours du tableau1 avec un booleen et une boucle while :"); + System.out.println ("--------------------"); + while (! fini) { + System.out.print ("|"+t[i]+"|"); + i++ ; + fini = i == t.length ; + } + System.out.println ("\n--------------------"); + //Tableau 2 + boolean fini1 = false ; + int i1 = 0 ; + while (! fini1) { + System.out.print ("|"+t2[i1]+"|"); + i1++ ; + fini1 = i1 == t2.length ; + } + System.out.println ("\n--------------------"); + +} +} + + + + + diff --git a/Les Ecuries de la Boissière/.classpath b/Les Ecuries de la Boissière/.classpath new file mode 100644 index 0000000..4fe04fe --- /dev/null +++ b/Les Ecuries de la Boissière/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Les Ecuries de la Boissière/.project b/Les Ecuries de la Boissière/.project new file mode 100644 index 0000000..4e093c5 --- /dev/null +++ b/Les Ecuries de la Boissière/.project @@ -0,0 +1,17 @@ + + + Les Ecuries de la Boissière + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Les Ecuries de la Boissière/.settings/org.eclipse.jdt.core.prefs b/Les Ecuries de la Boissière/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..a698e59 --- /dev/null +++ b/Les Ecuries de la Boissière/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Les Ecuries de la Boissière/bin/controleur/Cheval.class b/Les Ecuries de la Boissière/bin/controleur/Cheval.class new file mode 100644 index 0000000..6a46575 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Cheval.class differ diff --git a/Les Ecuries de la Boissière/bin/controleur/Cours.class b/Les Ecuries de la Boissière/bin/controleur/Cours.class new file mode 100644 index 0000000..5fd220c Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Cours.class differ diff --git a/Les Ecuries de la Boissière/bin/controleur/Eleve.class b/Les Ecuries de la Boissière/bin/controleur/Eleve.class new file mode 100644 index 0000000..29438b2 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Eleve.class differ diff --git a/Les Ecuries de la Boissière/bin/controleur/Formateur.class b/Les Ecuries de la Boissière/bin/controleur/Formateur.class new file mode 100644 index 0000000..fe1e15e Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Formateur.class differ diff --git a/Les Ecuries de la Boissière/bin/controleur/Gestion.class b/Les Ecuries de la Boissière/bin/controleur/Gestion.class new file mode 100644 index 0000000..1040e0e Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Gestion.class differ diff --git a/Les Ecuries de la Boissière/bin/controleur/Profil.class b/Les Ecuries de la Boissière/bin/controleur/Profil.class new file mode 100644 index 0000000..ed5e310 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/controleur/Profil.class differ diff --git a/Les Ecuries de la Boissière/bin/images/choix1.png b/Les Ecuries de la Boissière/bin/images/choix1.png new file mode 100644 index 0000000..5264cda Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/choix1.png differ diff --git a/Les Ecuries de la Boissière/bin/images/choix2.png b/Les Ecuries de la Boissière/bin/images/choix2.png new file mode 100644 index 0000000..8b44952 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/choix2.png differ diff --git a/Les Ecuries de la Boissière/bin/images/choosemyday_logo.png b/Les Ecuries de la Boissière/bin/images/choosemyday_logo.png new file mode 100644 index 0000000..b886521 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/choosemyday_logo.png differ diff --git a/Les Ecuries de la Boissière/bin/images/ec_launcher-web.png b/Les Ecuries de la Boissière/bin/images/ec_launcher-web.png new file mode 100644 index 0000000..2093184 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/ec_launcher-web.png differ diff --git a/Les Ecuries de la Boissière/bin/images/favicon.png b/Les Ecuries de la Boissière/bin/images/favicon.png new file mode 100644 index 0000000..64be2a9 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/favicon.png differ diff --git a/Les Ecuries de la Boissière/bin/images/logo.png b/Les Ecuries de la Boissière/bin/images/logo.png new file mode 100644 index 0000000..5170168 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/images/logo.png differ diff --git a/Les Ecuries de la Boissière/bin/modele/BDD.class b/Les Ecuries de la Boissière/bin/modele/BDD.class new file mode 100644 index 0000000..c95b043 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/BDD.class differ diff --git a/Les Ecuries de la Boissière/bin/modele/Modele.class b/Les Ecuries de la Boissière/bin/modele/Modele.class new file mode 100644 index 0000000..4b6c28f Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/Modele.class differ diff --git a/Les Ecuries de la Boissière/bin/modele/ModeleCheval.class b/Les Ecuries de la Boissière/bin/modele/ModeleCheval.class new file mode 100644 index 0000000..fa2b35d Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/ModeleCheval.class differ diff --git a/Les Ecuries de la Boissière/bin/modele/ModeleCours.class b/Les Ecuries de la Boissière/bin/modele/ModeleCours.class new file mode 100644 index 0000000..63af986 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/ModeleCours.class differ diff --git a/Les Ecuries de la Boissière/bin/modele/ModeleEleve.class b/Les Ecuries de la Boissière/bin/modele/ModeleEleve.class new file mode 100644 index 0000000..7523b9d Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/ModeleEleve.class differ diff --git a/Les Ecuries de la Boissière/bin/modele/ModeleFormateur.class b/Les Ecuries de la Boissière/bin/modele/ModeleFormateur.class new file mode 100644 index 0000000..b94a797 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/modele/ModeleFormateur.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/Connexion.class b/Les Ecuries de la Boissière/bin/vue/Connexion.class new file mode 100644 index 0000000..5741ba8 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/Connexion.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/Generale.class b/Les Ecuries de la Boissière/bin/vue/Generale.class new file mode 100644 index 0000000..f83a699 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/Generale.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueAccueil.class b/Les Ecuries de la Boissière/bin/vue/VueAccueil.class new file mode 100644 index 0000000..18f71e7 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueAccueil.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevaux.class b/Les Ecuries de la Boissière/bin/vue/VueChevaux.class new file mode 100644 index 0000000..78940ff Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevaux.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevauxA$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueChevauxA$StateListener.class new file mode 100644 index 0000000..cd89943 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevauxA$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevauxA.class b/Les Ecuries de la Boissière/bin/vue/VueChevauxA.class new file mode 100644 index 0000000..ad1efc4 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevauxA.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevauxM$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueChevauxM$StateListener.class new file mode 100644 index 0000000..b194eaa Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevauxM$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevauxM.class b/Les Ecuries de la Boissière/bin/vue/VueChevauxM.class new file mode 100644 index 0000000..a1db30a Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevauxM.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueChevauxS.class b/Les Ecuries de la Boissière/bin/vue/VueChevauxS.class new file mode 100644 index 0000000..e295693 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueChevauxS.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueConnexion.class b/Les Ecuries de la Boissière/bin/vue/VueConnexion.class new file mode 100644 index 0000000..4b6b9a2 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueConnexion.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCours.class b/Les Ecuries de la Boissière/bin/vue/VueCours.class new file mode 100644 index 0000000..86e30b3 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCours.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCoursA$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueCoursA$StateListener.class new file mode 100644 index 0000000..2871e1e Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCoursA$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCoursA.class b/Les Ecuries de la Boissière/bin/vue/VueCoursA.class new file mode 100644 index 0000000..79ade86 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCoursA.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCoursM$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueCoursM$StateListener.class new file mode 100644 index 0000000..dfba0f9 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCoursM$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCoursM.class b/Les Ecuries de la Boissière/bin/vue/VueCoursM.class new file mode 100644 index 0000000..2714f1c Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCoursM.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueCoursS.class b/Les Ecuries de la Boissière/bin/vue/VueCoursS.class new file mode 100644 index 0000000..2a24e60 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueCoursS.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueEleves.class b/Les Ecuries de la Boissière/bin/vue/VueEleves.class new file mode 100644 index 0000000..660fe18 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueEleves.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueElevesA$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueElevesA$StateListener.class new file mode 100644 index 0000000..149e782 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueElevesA$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueElevesA.class b/Les Ecuries de la Boissière/bin/vue/VueElevesA.class new file mode 100644 index 0000000..04dbf88 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueElevesA.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueElevesM$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueElevesM$StateListener.class new file mode 100644 index 0000000..acea340 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueElevesM$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueElevesM.class b/Les Ecuries de la Boissière/bin/vue/VueElevesM.class new file mode 100644 index 0000000..ed12d97 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueElevesM.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueElevesS.class b/Les Ecuries de la Boissière/bin/vue/VueElevesS.class new file mode 100644 index 0000000..b6c7ea5 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueElevesS.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateurs.class b/Les Ecuries de la Boissière/bin/vue/VueFormateurs.class new file mode 100644 index 0000000..0dd1b7b Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateurs.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateursA$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueFormateursA$StateListener.class new file mode 100644 index 0000000..ee7350e Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateursA$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateursA.class b/Les Ecuries de la Boissière/bin/vue/VueFormateursA.class new file mode 100644 index 0000000..12d52cb Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateursA.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateursM$StateListener.class b/Les Ecuries de la Boissière/bin/vue/VueFormateursM$StateListener.class new file mode 100644 index 0000000..b6e3d3f Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateursM$StateListener.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateursM.class b/Les Ecuries de la Boissière/bin/vue/VueFormateursM.class new file mode 100644 index 0000000..c8e311b Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateursM.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueFormateursS.class b/Les Ecuries de la Boissière/bin/vue/VueFormateursS.class new file mode 100644 index 0000000..4c2b7d1 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueFormateursS.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueInformation$1.class b/Les Ecuries de la Boissière/bin/vue/VueInformation$1.class new file mode 100644 index 0000000..e8827ec Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueInformation$1.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueInformation.class b/Les Ecuries de la Boissière/bin/vue/VueInformation.class new file mode 100644 index 0000000..0dbe3e0 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueInformation.class differ diff --git a/Les Ecuries de la Boissière/bin/vue/VueProfil.class b/Les Ecuries de la Boissière/bin/vue/VueProfil.class new file mode 100644 index 0000000..69e8fc6 Binary files /dev/null and b/Les Ecuries de la Boissière/bin/vue/VueProfil.class differ diff --git a/Les Ecuries de la Boissière/src/controleur/Cheval.java b/Les Ecuries de la Boissière/src/controleur/Cheval.java new file mode 100644 index 0000000..15e1fe3 --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Cheval.java @@ -0,0 +1,99 @@ +package controleur; + +public class Cheval +{ + private int idcheval, age; + private String nom, sexe, proprietaire, robe, type, race, imagecheval; + public Cheval() + { + this.idcheval=this.age=0; + this.nom = this.sexe=this.proprietaire=this.robe=this.type=this.race=this.imagecheval=""; + } + /*ALL*/ + public Cheval (int idcheval, int age, String nom, String sexe, String proprietaire, String robe, String type, String race, String imagecheval) + { + this.idcheval= idcheval; + this.age = age; + this.nom = nom; + this.sexe = sexe; + this.proprietaire = proprietaire; + this.robe = robe; + this.type = type; + this.race = race; + this.imagecheval = imagecheval; + } + public Cheval (int age, String nom, String sexe, String proprietaire, String robe, String type, String race, String imagecheval) + { + this.age = age; + this.nom = nom; + this.sexe = sexe; + this.proprietaire = proprietaire; + this.robe = robe; + this.type = type; + this.race = race; + this.imagecheval = imagecheval; + } + public Cheval (String nom, String proprietaire) + { + this.nom = nom; + this.proprietaire = proprietaire; + + } + + + public int getIdcheval() { + return idcheval; + } + public void setIdcheval(int idcheval) { + this.idcheval = idcheval; + } + public String getNom() { + return nom; + } + public void setNom(String nom) { + this.nom = nom; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public String getSexe() { + return sexe; + } + public void setSexe(String sexe) { + this.sexe = sexe; + } + public String getProprietaire() { + return proprietaire; + } + public void setProprietaire(String proprietaire) { + this.proprietaire = proprietaire; + } + public String getRobe() { + return robe; + } + public void setRobe(String robe) { + this.robe = robe; + } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getRace() { + return race; + } + public void setRace(String race) { + this.race = race; + } + public String getImagecheval() { + return imagecheval; + } + public void setImagecheval(String imagecheval) { + this.imagecheval = imagecheval; + } + +} diff --git a/Les Ecuries de la Boissière/src/controleur/Cours.java b/Les Ecuries de la Boissière/src/controleur/Cours.java new file mode 100644 index 0000000..cd78975 --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Cours.java @@ -0,0 +1,93 @@ +package controleur; + +public class Cours +{ + private int idcours, ideleve, idformateur, idcheval; + private String datecours, heuredebut, heurefin; + + public Cours() + { + this.idcours=ideleve=idformateur=idcheval=0; + this.datecours = this.heuredebut=this.heurefin=""; + } + public Cours (int idcours, String datecours, String heuredebut, String heurefin, int ideleve, int idformateur, int idcheval) + { + this.idcours= idcours; + this.datecours = datecours; + this.heuredebut = heuredebut; + this.heurefin = heurefin; + this.ideleve= ideleve; + this.idformateur= idformateur; + this.idcheval= idcheval; + + } + public Cours ( String datecours, String heuredebut, String heurefin) + { + this.idcours= 0; + this.ideleve= 1; + this.idformateur= 1; + this.idcheval= 1; + this.datecours = datecours; + this.heuredebut = heuredebut; + this.heurefin = heurefin; + } + public Cours ( String datecours, String heuredebut, String heurefin, int ideleve, int idformateur, int idcheval) + { + this.idcours= 0; + this.ideleve= ideleve; + this.idformateur= idformateur; + this.idcheval= idcheval; + this.datecours = datecours; + this.heuredebut = heuredebut; + this.heurefin = heurefin; + } + public Cours ( String datecours, String heuredebut) + { + this.datecours = datecours; + this.heuredebut = heuredebut; + } + public int getIdCours() { + return idcours; + } + public void setIdCours(int idcours) { + this.idcours = idcours; + } + public String getDateCours() { + return datecours; + } + public void setDateCours(String datecours) { + this.datecours = datecours; + } + public String getHeureDebut() { + return heuredebut; + } + public void setHeureDebut(String heuredebut) { + this.heuredebut = heuredebut; + } + public String getHeureFin() { + return heurefin; + } + public void setHeureFin(String heurefin) { + this.heurefin = heurefin; + } + public int getIdEleve() { + return ideleve; + } + public void setIdEleve(int ideleve) { + this.ideleve = ideleve; + } + public int getIdFormateur() { + return idformateur; + } + public void setIdFormateur(int idformateur) { + this.idformateur = idformateur; + } + public int getIdCheval() { + return idcheval; + } + public void setIdCheval(int idcheval) { + this.idcheval = idcheval; + } + + +} diff --git a/Les Ecuries de la Boissière/src/controleur/Eleve.java b/Les Ecuries de la Boissière/src/controleur/Eleve.java new file mode 100644 index 0000000..9089ea0 --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Eleve.java @@ -0,0 +1,139 @@ +package controleur; + +public class Eleve +{ + private int ideleve, age, galop, privilege; + private String nom, prenom, adresse, adressemail, sexe, pseudo, mdp, imageeleve, dateenregistre; + + public Eleve() + { + this.ideleve=this.age=this.galop=0; + this.privilege=2; + this.nom=this.prenom=this.adresse=this.adressemail=this.sexe=this.pseudo=this.mdp=""; + this.imageeleve=this.dateenregistre="2017-04-25 01:00:00"; + } + /*ALL*/ + public Eleve (int idEleve, int age, int galop, int privilege, String nom, String prenom, String adresse, String adressemail, String sexe, String pseudo, String mdp, String imageeleve, String dateenregistre) + { + this.ideleve= idEleve; + this.age = age; + this.galop = galop; + this.privilege = privilege; + this.nom = nom; + this.prenom = prenom; + this.adresse = adresse; + this.adressemail = adressemail; + this.sexe = sexe; + this.pseudo = pseudo; + this.mdp = mdp; + this.imageeleve = imageeleve; + this.dateenregistre = dateenregistre; + } + public Eleve ( int age, int galop, String nom, String prenom, String adresse, String adressemail, String sexe, String pseudo, String mdp, String imageeleve) + { + this.ideleve= 0; + this.age = age; + this.galop = galop; + this.privilege = 2; + this.nom = nom; + this.prenom = prenom; + this.adresse = adresse; + this.adressemail = adressemail; + this.sexe = sexe; + this.pseudo = pseudo; + this.mdp = mdp; + this.imageeleve = imageeleve; + this.dateenregistre = ""; + } + + public Eleve (String pseudo) + { + + this.pseudo = pseudo; + + } + + + + + + + public int getIdeleve() { + return ideleve; + } + public void setIdeleve(int ideleve) { + this.ideleve = ideleve; + } + public String getNom() { + return nom; + } + public void setNom(String nom) { + this.nom = nom; + } + public String getPrenom() { + return prenom; + } + public void setPrenom(String prenom) { + this.prenom = prenom; + } + public String getAdresse() { + return adresse; + } + public void setAdresse(String adresse) { + this.adresse = adresse; + } + public String getAdressEmail() { + return adressemail; + } + public void setAdressEmail(String adressemail) { + this.adressemail = adressemail; + } + public String getSexe() { + return sexe; + } + public void setSexe(String sexe) { + this.sexe = sexe; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public String getPseudo() { + return pseudo; + } + public void setPseudo(String pseudo) { + this.pseudo = pseudo; + } + public String getMdp() { + return mdp; + } + public void setMdp(String mdp) { + this.mdp = mdp; + } + public String getImageeleve() { + return imageeleve; + } + public void setImageeleve(String imageeleve) { + this.imageeleve = imageeleve; + } + public int getGalop() { + return galop; + } + public void setGalop(int galop) { + this.galop = galop; + } + public int getPrivilege() { + return privilege; + } + public void setPrivilege(int privilege) { + this.privilege = privilege; + } + public String getDateEnregistre() { + return dateenregistre; + } + public void setDateEnregistre(String dateenregistre) { + this.dateenregistre = dateenregistre; + } +} diff --git a/Les Ecuries de la Boissière/src/controleur/Formateur.java b/Les Ecuries de la Boissière/src/controleur/Formateur.java new file mode 100644 index 0000000..f1371cb --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Formateur.java @@ -0,0 +1,110 @@ +package controleur; + +public class Formateur +{ + private int idformateur, age, galop, privilege; + private String nom, prenom, adressemail, sexe, login, mdp; + + public Formateur() + { + this.idformateur=this.age=this.galop=this.privilege=0; + this.nom=this.prenom=this.adressemail=this.sexe=this.mdp=this.login=""; + } + /*ALL*/ + public Formateur (int idformateur, int age, int galop, int privilege, String nom, String prenom, String adressemail, String sexe, String login, String mdp ) + { + this.idformateur= idformateur; + this.nom = nom; + this.prenom = prenom; + this.adressemail = adressemail; + this.sexe = sexe; + this.age = age; + this.galop = galop; + this.privilege = privilege; + this.login = login; + this.mdp = mdp; + } + /* S IDF & Privikege*/ + public Formateur ( int age, int galop, String nom, String prenom, String adressemail, String sexe, String login, String mdp ) + { + this.nom = nom; + this.prenom = prenom; + this.adressemail = adressemail; + this.sexe = sexe; + this.age = age; + this.galop = galop; + this.privilege = 3; + this.login = login; + this.mdp = mdp; + } + + public Formateur (String login) + { + this.login = login; + } + + + + + + public int getIdformateur() { + return idformateur; + } + public void setIdformateur(int idformateur) { + this.idformateur = idformateur; + } + public String getNom() { + return nom; + } + public void setNom(String nom) { + this.nom = nom; + } + public String getPrenom() { + return prenom; + } + public void setPrenom(String prenom) { + this.prenom = prenom; + } + public String getAdressEmail() { + return adressemail; + } + public void setAdressEmail(String adressemail) { + this.adressemail = adressemail; + } + public String getSexe() { + return sexe; + } + public void setSexe(String sexe) { + this.sexe = sexe; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public int getGalop() { + return galop; + } + public void setGalop(int galop) { + this.galop = galop; + } + public int getPrivilege() { + return privilege; + } + public void setPrivilege(int privilege) { + this.privilege = privilege; + } + public String getLogin() { + return login; + } + public void setLogin(String login) { + this.login = login; + } + public String getMdp() { + return mdp; + } + public void setMdp(String mdp) { + this.mdp = mdp; + } +} diff --git a/Les Ecuries de la Boissière/src/controleur/Gestion.java b/Les Ecuries de la Boissière/src/controleur/Gestion.java new file mode 100644 index 0000000..3664e87 --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Gestion.java @@ -0,0 +1,25 @@ +package controleur; + +import vue.Connexion; + +public class Gestion +{ + private static Connexion uneConnexion; + + public Gestion() + { + uneConnexion = new Connexion(); + uneConnexion.rendreVisible(true); + } + + public static void rendreVisible(boolean val) + { + uneConnexion.rendreVisible(val); + } + + public static void main(String[] args) + { + new Gestion(); + } + +} diff --git a/Les Ecuries de la Boissière/src/controleur/Profil.java b/Les Ecuries de la Boissière/src/controleur/Profil.java new file mode 100644 index 0000000..5a18baa --- /dev/null +++ b/Les Ecuries de la Boissière/src/controleur/Profil.java @@ -0,0 +1,117 @@ +package controleur; + +public class Profil +{ + private int idformateur, age, galop, privilege; + private String nom, prenom, adressemail, sexe, login, mdp; + public Profil (int idformateur, int age, int galop, int privilege, String nom, String prenom, String adressemail, String sexe, String login, String mdp ) + { + this.idformateur= idformateur; + this.age = age; + this.galop = galop; + this.privilege = privilege; + this.nom = nom; + this.prenom = prenom; + this.adressemail = adressemail; + this.sexe = sexe; + this.login = login; + this.mdp = mdp; + } + public Profil (int age, int galop, int privilege, String nom, String prenom, String adressemail, String sexe, String login, String mdp ) + { + this.age = age; + this.galop = galop; + this.privilege = privilege; + this.nom = nom; + this.prenom = prenom; + this.adressemail = adressemail; + this.sexe = sexe; + this.login = login; + this.mdp = mdp; + } + public Profil (String adressemail, String mdp) + { + this.adressemail = adressemail; + this.mdp = mdp; + } + + + + + + public int getIdformateur() { + return idformateur; + } + + public void setIdformateur(int idformateur) { + this.idformateur = idformateur; + } + public String getNom() { + return nom; + } + + public void setNom(String nom) { + this.nom = nom; + } + + public String getPrenom() { + return prenom; + } + + public void setPrenom(String prenom) { + this.prenom = prenom; + } + public String getEmail() { + return adressemail; + } + + public void setEmail(String adressemail) { + this.adressemail = adressemail; + } + + public String getSexe() { + return sexe; + } + + public void setSexe(String sexe) { + this.sexe = sexe; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getGalop() { + return galop; + } + public void setGalop(int galop) { + this.galop = galop; + } + + public int getPrivilege() { + return privilege; + } + + public void setPrivilege(int privilege) { + this.privilege = privilege; + } + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + public String getMdp() { + return mdp; + } + + public void setMdp(String mdp) { + this.mdp = mdp; + } + +} diff --git a/Les Ecuries de la Boissière/src/images/choix1.png b/Les Ecuries de la Boissière/src/images/choix1.png new file mode 100644 index 0000000..5264cda Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/choix1.png differ diff --git a/Les Ecuries de la Boissière/src/images/choix2.png b/Les Ecuries de la Boissière/src/images/choix2.png new file mode 100644 index 0000000..8b44952 Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/choix2.png differ diff --git a/Les Ecuries de la Boissière/src/images/choosemyday_logo.png b/Les Ecuries de la Boissière/src/images/choosemyday_logo.png new file mode 100644 index 0000000..b886521 Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/choosemyday_logo.png differ diff --git a/Les Ecuries de la Boissière/src/images/ec_launcher-web.png b/Les Ecuries de la Boissière/src/images/ec_launcher-web.png new file mode 100644 index 0000000..2093184 Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/ec_launcher-web.png differ diff --git a/Les Ecuries de la Boissière/src/images/favicon.png b/Les Ecuries de la Boissière/src/images/favicon.png new file mode 100644 index 0000000..64be2a9 Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/favicon.png differ diff --git a/Les Ecuries de la Boissière/src/images/logo.png b/Les Ecuries de la Boissière/src/images/logo.png new file mode 100644 index 0000000..5170168 Binary files /dev/null and b/Les Ecuries de la Boissière/src/images/logo.png differ diff --git a/Les Ecuries de la Boissière/src/modele/BDD.java b/Les Ecuries de la Boissière/src/modele/BDD.java new file mode 100644 index 0000000..bfd9574 --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/BDD.java @@ -0,0 +1,60 @@ +package modele; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +public class BDD +{ + private String serveur, nombdd, user, mdp; + private Connection maConnexion; + + public BDD(String serveur, String nombdd, String user, String mdp) + { + this.serveur = serveur; + this.nombdd = nombdd; + this.user = user; + this.mdp = mdp; + this.maConnexion = null; + } + + public void chargerPilote() + { + // vérifie la présence du pilote JDBC MySQL + try { + Class.forName("com.mysql.jdbc.Driver"); + } + catch(ClassNotFoundException exp) { + System.out.println("Abscence du pilote JDBC !"); + } + } + + public void seConnecter() + { + // connexion au serveur de la BDD + this.chargerPilote(); + String url = "jdbc:mysql://" + this.serveur + "/" + this.nombdd; + + try { + this.maConnexion = DriverManager.getConnection(url, this.user, this.mdp); + } + catch(SQLException exp) { + System.out.println("Impossible de se connecter ŕ  " + url); + } + } + + public void seDeconnecter() + { + // déconnexion au serveur de la BDD + try { + if(this.maConnexion != null) this.maConnexion.close(); + } + catch(SQLException exp) { + System.out.println("La déconnexion a échoué !"); + } + } + + public Connection getMaConnexion() + { + return this.maConnexion; + } +} diff --git a/Les Ecuries de la Boissière/src/modele/Modele.java b/Les Ecuries de la Boissière/src/modele/Modele.java new file mode 100644 index 0000000..d67f2a6 --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/Modele.java @@ -0,0 +1,137 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Profil; + +public class Modele +{ + /* ARRAY */ + public static ArrayList selectAll() + { + ArrayList lesProfils = new ArrayList(); + String requete = "SELECT * FROM formateur;"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + int idformateur = unRes.getInt("idformateur"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adressemail = unRes.getString("adressemail"); + String sexe = unRes.getString("sexe"); + String login = unRes.getString("login"); + String mdp = unRes.getString("mdp"); + + Profil unProfil = new Profil(idformateur, age, galop, privilege, nom, prenom, adressemail, sexe, login, mdp ); + lesProfils.add(unProfil); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesProfils; + } + /* INSERT */ + public static void insert(Profil unProfil) + { + String requete = "INSERT INTO formateur (age, galop, privilege, nom, prenom, adressemail, sexe, login, mdp ) VALUES ('" + + unProfil.getAge() + "', '" + + unProfil.getGalop() + "', '" + + unProfil.getPrivilege() + "', '" + + unProfil.getNom() + "', '" + + unProfil.getPrenom() + "', '" + + unProfil.getEmail() + "', '" + + unProfil.getSexe() + "', '" + + unProfil.getLogin() + "', '" + + unProfil.getMdp() + "');"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /* DELETE */ + public static void delete(String adressemail) + { + String requete = "DELETE FROM formateur WHERE adressemail='" + adressemail + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /* WHERE* Connexion */ + public static Profil selectWhere(String adressemail, String mdp) + { + String requete = "SELECT * FROM formateur WHERE adressemail='" + adressemail + "' AND mdp='" + mdp + "';"; + + Profil unProfil = null; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + int idformateur = unRes.getInt("idformateur"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String sexe = unRes.getString("sexe"); + String login = unRes.getString("login"); + + unProfil = new Profil(idformateur, age, galop, privilege, nom, prenom, adressemail, sexe, login, mdp ); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unProfil; + } +} diff --git a/Les Ecuries de la Boissière/src/modele/ModeleCheval.java b/Les Ecuries de la Boissière/src/modele/ModeleCheval.java new file mode 100644 index 0000000..860d52f --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/ModeleCheval.java @@ -0,0 +1,161 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Cheval; + +public class ModeleCheval +{ + /* ARRAY* */ + public static ArrayList selectAll() + { + ArrayList lesChevaux = new ArrayList(); + String requete = "SELECT * FROM cheval;"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + int idcheval = unRes.getInt("idcheval"); + int age = unRes.getInt("age"); + String nom = unRes.getString("nom"); + String sexe = unRes.getString("sexe"); + String proprietaire = unRes.getString("proprietaire"); + String robe = unRes.getString("robe"); + String type = unRes.getString("type"); + String race = unRes.getString("race"); + String imagecheval = unRes.getString("imagecheval"); + + + Cheval unCheval = new Cheval( idcheval, age, nom, sexe, proprietaire, robe, type, race, imagecheval); + lesChevaux.add(unCheval); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesChevaux; + } + + /*INSERT* SID */ + public static void insert(Cheval unCheval) + { + String requete = "INSERT INTO cheval (nom, age, sexe, proprietaire, robe, type, race, imagecheval) VALUES ('" + + unCheval.getNom() + "', '" + + unCheval.getAge() + "', '" + + unCheval.getSexe() + "', '" + + unCheval.getProprietaire() + "', '" + + unCheval.getRobe() + "', '" + + unCheval.getType() + "', '" + + unCheval.getRace() + "', '" + + unCheval.getImagecheval()+ "');"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /*UPDATE PAS* */ + public static void update(Cheval unCheval, String nom, String proprietaire) + { + String requete = "UPDATE cheval SET " + + "nom = '"+ unCheval.getNom() +"', proprietaire ='"+ unCheval.getProprietaire() +"', " + + "robe ='"+ unCheval.getRobe() +"', type ='"+ unCheval.getType() +"', " + + "sexe ='"+ unCheval.getSexe() +"', age ='"+ unCheval.getAge() +"', " + + "race ='"+ unCheval.getRace()+"', "+"imagecheval ='"+ unCheval.getImagecheval()+"' " + + "WHERE nom='"+ nom + "'AND proprietaire='" + proprietaire + "';"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /*DELETE* */ + public static void delete(String nom, String proprietaire) + { + String requete = "DELETE FROM cheval WHERE nom='" + nom + "'AND proprietaire='" + proprietaire + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + +/*WHERE* */ + public static Cheval selectWhere(String nom, String proprietaire) + { + String requete = "SELECT * FROM cheval WHERE nom='" + nom + "AND proprietaire='" + proprietaire + "';"; + + Cheval unCheval = null; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + int idcheval = unRes.getInt("idcheval"); + int age = unRes.getInt("age"); + String sexe = unRes.getString("sexe"); + String robe = unRes.getString("robe"); + String type = unRes.getString("type"); + String race = unRes.getString("race"); + String imagecheval = unRes.getString("imagecheval"); + + unCheval = new Cheval( idcheval, age, nom, sexe, proprietaire, robe, type, race, imagecheval); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unCheval; + } + +} diff --git a/Les Ecuries de la Boissière/src/modele/ModeleCours.java b/Les Ecuries de la Boissière/src/modele/ModeleCours.java new file mode 100644 index 0000000..a5e9050 --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/ModeleCours.java @@ -0,0 +1,150 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Cours; + +public class ModeleCours +{ + public static ArrayList selectAll() + { + ArrayList lesCours = new ArrayList(); + String requete = "select * from cours;"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + int idcours = unRes.getInt("idcours"); + String datecours = unRes.getString("datecours"); + String heuredebut = unRes.getString("heuredebut"); + String heurefin = unRes.getString("heurefin"); + int ideleve = unRes.getInt("ideleve"); + int idformateur = unRes.getInt("idformateur"); + int idcheval = unRes.getInt("idcheval"); + + + Cours unCours = new Cours(idcours, datecours, heuredebut, heurefin, ideleve, idformateur, idcheval); + lesCours.add(unCours); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesCours; + } + + + +public static void insert(Cours unCours) +{ + String requete = "insert into cours (datecours, heuredebut, heurefin, ideleve, idformateur, idcheval ) values ('" + + unCours.getDateCours() + "', '" + + unCours.getHeureDebut() + "', '" + + unCours.getHeureFin() + "', '" + + unCours.getIdEleve() + "', '" + + unCours.getIdFormateur() + "', '" + + unCours.getIdCheval() + "');"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } +} + +/*UPDATE PAS* */ +public static void update(Cours unCours, String datecours, String heuredebut) +{ + String requete = "UPDATE cours SET " + + "datecours = '"+ unCours.getDateCours() +"', heuredebut ='"+ unCours.getHeureDebut() +"', " + + "heurefin ='"+ unCours.getHeureFin() +"', "+ "ideleve ='"+ unCours.getIdEleve() +"', " + + "idformateur ='"+ unCours.getIdFormateur() +"', "+ "idcheval ='"+ unCours.getIdCheval() +"' " + + "WHERE datecours='"+ datecours + "' AND heuredebut='" + heuredebut + "';"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } +} + +public static void delete(String datecours, String heuredebut) +{ + String requete = "delete from cours where datecours='" + datecours + "'AND heuredebut='" + heuredebut + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } +} + +public static Cours selectWhere(String datecours, String heuredebut) +{ + String requete = "select * from cours where datecours='" + datecours + "' and heuredebut='" + heuredebut + "';"; + + Cours unCours = null; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + String heurefin = unRes.getString("heurefin"); + + unCours = new Cours(datecours, heuredebut, heurefin); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unCours; + } +} diff --git a/Les Ecuries de la Boissière/src/modele/ModeleEleve.java b/Les Ecuries de la Boissière/src/modele/ModeleEleve.java new file mode 100644 index 0000000..15dd1ab --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/ModeleEleve.java @@ -0,0 +1,168 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Eleve; + +public class ModeleEleve +{ + /*ARRAY* */ + public static ArrayList selectAll() + { + ArrayList lesEleves = new ArrayList(); + String requete = "SELECT * FROM compteeleve;"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + int ideleve = unRes.getInt("ideleve"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adresse = unRes.getString("adresse"); + String adressemail = unRes.getString("adressemail"); + String sexe = unRes.getString("sexe"); + String pseudo = unRes.getString("pseudo"); + String mdp = unRes.getString("mdp"); + String imageeleve = unRes.getString("imageeleve"); + String dateenregistre = unRes.getString("dateenregistre"); + + Eleve unEleve = new Eleve(ideleve, age, galop, privilege, nom, prenom, adresse, adressemail, sexe, pseudo, mdp, imageeleve, dateenregistre); + lesEleves.add(unEleve); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesEleves; + } + /* INSERT S ID & PRIV & DateEnr */ + public static void insert(Eleve unEleve) + { + String requete = "INSERT INTO compteeleve (nom, prenom, adresse, adressemail, sexe, age, galop, pseudo, mdp, imageeleve) VALUES ('" + + unEleve.getNom() + "', '" + + unEleve.getPrenom() + "', '" + + unEleve.getAdresse() + "', '" + + unEleve.getAdressEmail() + "', '" + + unEleve.getSexe() + "', '" + + unEleve.getAge() + "', '" + + unEleve.getGalop() + "', '" + + unEleve.getPseudo() + "', '" + + unEleve.getMdp() + "', '" + + unEleve.getImageeleve()+ "');"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /* DELETE* */ + public static void delete(String pseudo) + { + String requete = "DELETE FROM compteeleve WHERE pseudo='" + pseudo + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /*UPDATE S ID & PRIV & DateEnr */ + public static void update(Eleve unEleve, String pseudo) + { + String requete = "UPDATE compteeleve SET " + + "nom = '"+ unEleve.getNom() +"', prenom ='"+ unEleve.getPrenom() +"', " + + "adresse ='"+ unEleve.getAdresse() +"', adressemail ='"+ unEleve.getAdressEmail() +"', " + + "sexe ='"+ unEleve.getSexe() +"', age ='"+ unEleve.getAge() +"', " + + "galop ='"+ unEleve.getGalop() +"', pseudo ='"+ unEleve.getPseudo() +"', " + + "mdp ='"+ unEleve.getMdp()+"', imageeleve ='"+ unEleve.getImageeleve()+"' " + + "WHERE pseudo='"+ pseudo +"';"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /*WHERE* */ + public static Eleve selectWhere(String pseudo, String mdp) + { + String requete = "SELECT * FROM compteeleve WHERE pseudo='" + pseudo + "' AND mdp='" + mdp + "';"; + + Eleve unEleve = null; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + int ideleve = unRes.getInt("ideleve"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adresse = unRes.getString("adresse"); + String sexe = unRes.getString("sexe"); + String adressemail = unRes.getString("adressemail"); + String imageeleve = unRes.getString("imageeleve"); + String dateenregistre = unRes.getString("dateenregistre"); + + unEleve = new Eleve(ideleve, age, galop, privilege, nom, prenom, adresse, adressemail, sexe, pseudo, mdp, imageeleve, dateenregistre); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unEleve; + } +} diff --git a/Les Ecuries de la Boissière/src/modele/ModeleFormateur.java b/Les Ecuries de la Boissière/src/modele/ModeleFormateur.java new file mode 100644 index 0000000..da575d6 --- /dev/null +++ b/Les Ecuries de la Boissière/src/modele/ModeleFormateur.java @@ -0,0 +1,158 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import controleur.Formateur; +public class ModeleFormateur +{ + /*ARRAY* */ + public static ArrayList selectAll() + { + ArrayList lesFormateurs = new ArrayList(); + String requete = "SELECT * FROM formateur;"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + int idformateur = unRes.getInt("idformateur"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adressemail = unRes.getString("adressemail"); + String sexe = unRes.getString("sexe"); + String mdp = unRes.getString("mdp"); + String login = unRes.getString("login"); + + Formateur unFormateur = new Formateur(idformateur, age, galop, privilege, nom, prenom, adressemail, sexe, login, mdp); + lesFormateurs.add(unFormateur); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesFormateurs; + } + /* INSERT * SID */ + public static void insert(Formateur unFormateur) + { + String requete = "INSERT INTO formateur (nom, prenom, adressemail, sexe, age, galop, privilege, login, mdp) VALUES ('" + + unFormateur.getNom() + "', '" + + unFormateur.getPrenom() + "', '" + + unFormateur.getAdressEmail() + "', '" + + unFormateur.getSexe() + "', '" + + unFormateur.getAge() + "', '" + + unFormateur.getGalop() + "', '" + + unFormateur.getPrivilege() + "', '" + + unFormateur.getLogin() + "', '" + + unFormateur.getMdp() + "');"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /*UPDATE PAS* */ + public static void update(Formateur unFormateur, String login) + { + String requete = "UPDATE formateur SET " + + "nom = '"+ unFormateur.getNom() +"', prenom ='"+ unFormateur.getPrenom() +"', " + + "galop ='"+ unFormateur.getGalop() +"', adressemail ='"+ unFormateur.getAdressEmail() +"', " + + "sexe ='"+ unFormateur.getSexe() +"', age ='"+ unFormateur.getAge() +"', " + + "login ='"+ unFormateur.getLogin()+"', "+"mdp ='"+ unFormateur.getMdp()+"' " + + "WHERE login='"+ login +"';"; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /* DELETE* */ + public static void delete(String login) + { + String requete = "DELETE FROM formateur WHERE login='" + login + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + /* WHERE* */ + public static Formateur selectWhere(String login, String mdp) + { + String requete = "SELECT * FROM formateur WHERE login='" + login + "' AND mdp='" + mdp + "';"; + + Formateur unFormateur = null; + try { + BDD uneBDD = new BDD("localhost", "ecurie", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + int idformateur = unRes.getInt("idformateur"); + int age = unRes.getInt("age"); + int galop = unRes.getInt("galop"); + int privilege = unRes.getInt("privilege"); + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String sexe = unRes.getString("sexe"); + String adressemail = unRes.getString("adressemail"); + + unFormateur = new Formateur(idformateur, age, galop, privilege, nom, prenom,adressemail, sexe, login, mdp); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unFormateur; + } +} diff --git a/Les Ecuries de la Boissière/src/vue/Connexion.java b/Les Ecuries de la Boissière/src/vue/Connexion.java new file mode 100644 index 0000000..6734a65 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/Connexion.java @@ -0,0 +1,46 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class Connexion extends JFrame +{ + public Connexion() + { + + + /*PARAM FENETRE */ + this.setIconImage(new ImageIcon("src/images//favicon.png").getImage());//logo fenetre + this.setTitle("Les Ecuries de la Boissičre");//titre fenetre + this.setBounds(400, 150, 1000, 800);//format fenetre + this.getContentPane().setBackground(new Color(117,157,135));//Couleur fenetre RGB + this.setLayout(null); // pas de grille + this.setResizable(false); // la fenčtre ne pourra pas ętre redimensionnée + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + + /* AFFICHAGE FENETRE*/ + ImageIcon logo = new ImageIcon(new ImageIcon("src/images/logo.png").getImage().getScaledInstance(550,151, Image.SCALE_AREA_AVERAGING));//logo page L/l + JLabel lbLogo = new JLabel(logo); + lbLogo.setBounds(200, 15, 550,151);//position logo page X Y L H + this.add(lbLogo); + JLabel lbTitre = new JLabel("
Bienvenue
Connectez-vous
");//titre page + lbTitre.setBounds(400, 250, 200, 60);// position titre page + lbTitre.setFont(new Font(lbTitre.getText(), Font.ROMAN_BASELINE, 25));// taille titre page + lbTitre.setForeground( Color.white); + this.add(lbTitre); + + this.add(new VueConnexion());//insertion VueConnexion + + this.setVisible(true); + } + + public void rendreVisible(boolean val) + { + this.setVisible(val); + } +} diff --git a/Les Ecuries de la Boissière/src/vue/Generale.java b/Les Ecuries de la Boissière/src/vue/Generale.java new file mode 100644 index 0000000..320094d --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/Generale.java @@ -0,0 +1,603 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +import controleur.Gestion; +import controleur.Profil; + + + +public class Generale extends JFrame implements ActionListener +{ + /*VARIABLE*/ + private JMenuBar uneBarre = new JMenuBar(); + private JMenu mnFichier = new JMenu("Menu"); + private JMenu mnGestion = new JMenu("Gestion"); + private JMenu mnCentre = new JMenu("Centre"); + private JMenu mnAide = new JMenu("Information"); + + private JMenuItem itemAccueil = new JMenuItem("Accueil"); + private JMenuItem itemQuitter = new JMenuItem("Quitter"); + + private JMenuItem itemInformation = new JMenuItem("A propos"); + + private JMenuItem itemCours = new JMenuItem("Liste des cours"); + private JMenuItem itemACours = new JMenuItem("Ajouter des cours"); + private JMenuItem itemMCours = new JMenuItem("Modifier des cours"); + private JMenuItem itemSCours = new JMenuItem("Supprimer des cours"); + + private JMenuItem itemEleves = new JMenuItem("Liste des élčves"); + private JMenuItem itemAEleves = new JMenuItem("Ajouter des élčves"); + private JMenuItem itemMEleves = new JMenuItem("Modifier des élčves"); + private JMenuItem itemSEleves = new JMenuItem("Supprimer des élčves"); + + private JMenuItem itemChevaux = new JMenuItem("Liste des chevaux"); + private JMenuItem itemAChevaux = new JMenuItem("Ajouter des chevaux"); + private JMenuItem itemMChevaux = new JMenuItem("Modifier des chevaux"); + private JMenuItem itemSChevaux = new JMenuItem("Supprimer des chevaux"); + + private JMenuItem itemFormateurs= new JMenuItem("Liste des formateurs"); + private JMenuItem itemAFormateurs = new JMenuItem("Ajouter des formateurs"); + private JMenuItem itemMFormateurs = new JMenuItem("Modifier des formateurs"); + private JMenuItem itemSFormateurs = new JMenuItem("Supprimer des formateurs"); + + + private VueInformation uneVueInformation = new VueInformation(); + private VueEleves uneVueEleves = new VueEleves(); + private VueElevesA uneVueElevesA = new VueElevesA(); + private VueElevesM uneVueElevesM = new VueElevesM(); + private VueElevesS uneVueElevesS = new VueElevesS(); + private VueCours uneVueCours = new VueCours(); + private VueCoursA uneVueCoursA = new VueCoursA(); + private VueCoursM uneVueCoursM = new VueCoursM(); + private VueCoursS uneVueCoursS = new VueCoursS(); + private VueChevaux uneVueChevaux = new VueChevaux(); + private VueChevauxA uneVueChevauxA = new VueChevauxA(); + private VueChevauxM uneVueChevauxM = new VueChevauxM(); + private VueChevauxS uneVueChevauxS = new VueChevauxS(); + private VueFormateurs uneVueFormateurs = new VueFormateurs(); + private VueFormateursA uneVueFormateursA = new VueFormateursA(); + private VueFormateursM uneVueFormateursM = new VueFormateursM(); + private VueFormateursS uneVueFormateursS = new VueFormateursS(); + + + private VueAccueil uneVueAccueil ; + /*VARIABLE*/ + + /*AFFICHAGE GENERAL APPLI*/ + public Generale(Profil unProfil) + { + ImageIcon logo = new ImageIcon(new ImageIcon("src/images/favicon.png").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); + + this.setIconImage(logo.getImage()); + this.setTitle("Les Ecuries de la Boissičre"); + this.setBounds(400, 150, 1000, 800); + this.getContentPane().setBackground(new Color(117,157,135)); + + this.setLayout(null); + this.setResizable(false); + + + this.uneVueAccueil = new VueAccueil(unProfil); + this.add(uneVueAccueil); + this.add(uneVueInformation); + this.add(uneVueChevaux); + this.add(uneVueChevauxA); + this.add(uneVueChevauxM); + this.add(uneVueChevauxS); + this.add(uneVueEleves); + this.add(uneVueElevesA); + this.add(uneVueElevesM); + this.add(uneVueElevesS); + this.add(uneVueCours); + this.add(uneVueCoursA); + this.add(uneVueCoursM); + this.add(uneVueCoursS); + this.add(uneVueFormateurs); + this.add(uneVueFormateursA); + this.add(uneVueFormateursM); + this.add(uneVueFormateursS); + + + /*BARRE MENU GROUPE*/ + this.uneBarre.add(this.mnFichier); + this.uneBarre.add(this.mnCentre); + this.uneBarre.add(this.mnGestion); + this.uneBarre.add(this.mnAide); + /*BARRE MENU GROUPE*/ + + /*BARRE MENU LISTE GROUPE*/ + this.mnFichier.add(this.itemAccueil); + this.mnFichier.add(this.itemQuitter); + this.mnCentre.add(this.itemEleves); + this.mnCentre.add(this.itemChevaux); + this.mnCentre.add(this.itemCours); + this.mnCentre.add(this.itemFormateurs); + this.mnGestion.add(this.itemAEleves); + this.mnGestion.add(this.itemMEleves); + this.mnGestion.add(this.itemSEleves); + this.mnGestion.add(this.itemAChevaux); + this.mnGestion.add(this.itemMChevaux); + this.mnGestion.add(this.itemSChevaux); + this.mnGestion.add(this.itemACours); + this.mnGestion.add(this.itemMCours); + this.mnGestion.add(this.itemSCours); + this.mnGestion.add(this.itemAFormateurs); + this.mnGestion.add(this.itemMFormateurs); + this.mnGestion.add(this.itemSFormateurs); + this.mnAide.add(this.itemInformation); + /*BARRE MENU LISTE GROUPE*/ + + this.itemQuitter.addActionListener(this); + this.itemAccueil.addActionListener(this); + this.itemEleves.addActionListener(this); + this.itemChevaux.addActionListener(this); + this.itemCours.addActionListener(this); + this.itemFormateurs.addActionListener(this); + this.itemACours.addActionListener(this); + this.itemMCours.addActionListener(this); + this.itemSCours.addActionListener(this); + this.itemAEleves.addActionListener(this); + this.itemMEleves.addActionListener(this); + this.itemSEleves.addActionListener(this); + this.itemAChevaux.addActionListener(this); + this.itemMChevaux.addActionListener(this); + this.itemSChevaux.addActionListener(this); + this.itemAFormateurs.addActionListener(this); + this.itemMFormateurs.addActionListener(this); + this.itemSFormateurs.addActionListener(this); + this.itemInformation.addActionListener(this); + + + this.setJMenuBar(this.uneBarre); + + JLabel lbTitre = new JLabel("
Espace de travail
"+ unProfil.getLogin()+"
"); + lbTitre.setBounds(400, 30, 300, 60); + lbTitre.setFont(new Font(lbTitre.getText(), Font.ROMAN_BASELINE, 25));// taille titre page + lbTitre.setForeground( Color.white); + this.add(lbTitre) ; + + this.setVisible(true); + } + /*AFFICHAGE GENERAL APPLI*/ + + /*ACTION GENERAL APPLI*/ + @Override + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.itemQuitter) + { + Gestion.rendreVisible(true); + + this.setVisible(false); + } + else if (e.getSource()==this.itemAccueil) + { + uneVueAccueil.setVisible(true); + uneVueInformation.setVisible(false); + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemInformation) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(true); + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemCours) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(true); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + + else if (e.getSource()==this.itemACours) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(true); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemMCours) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(true); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemSCours) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(true); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemEleves) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(true); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemAEleves) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(true); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemMEleves) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(true); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemSEleves) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(true); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemChevaux) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(true); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemAChevaux) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(true); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemMChevaux) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(true); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemSChevaux) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(true); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemFormateurs) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(true); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemAFormateurs) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(true); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemMFormateurs) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(true); + uneVueFormateursS.setVisible(false); + + } + else if (e.getSource()==this.itemSFormateurs) + { + uneVueAccueil.setVisible(false); + uneVueInformation.setVisible(false); + + uneVueCours.setVisible(false); + uneVueCoursA.setVisible(false); + uneVueCoursM.setVisible(false); + uneVueCoursS.setVisible(false); + uneVueEleves.setVisible(false); + uneVueElevesA.setVisible(false); + uneVueElevesM.setVisible(false); + uneVueElevesS.setVisible(false); + uneVueChevaux.setVisible(false); + uneVueChevauxA.setVisible(false); + uneVueChevauxM.setVisible(false); + uneVueChevauxS.setVisible(false); + uneVueFormateurs.setVisible(false); + uneVueFormateursA.setVisible(false); + uneVueFormateursM.setVisible(false); + uneVueFormateursS.setVisible(true); + + } + + + } + /*ACTION GENERAL APPLI*/ + +} diff --git a/Les Ecuries de la Boissière/src/vue/VueAccueil.java b/Les Ecuries de la Boissière/src/vue/VueAccueil.java new file mode 100644 index 0000000..17d0730 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueAccueil.java @@ -0,0 +1,50 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +import controleur.Profil; + +public class VueAccueil extends JPanel +{ + public VueAccueil(Profil unProfil) + { + /*AFFICHAGE TAB*/ + this.setBounds(50, 130, 900, 500); + this.setBackground(new Color(230,230,230)); + this.setLayout(null); + /* TITRE */ + JLabel lbProfil = new JLabel("Vos informations de profil"); + lbProfil.setBounds(300, 30, 400, 40); + lbProfil.setFont(new Font(lbProfil.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(lbProfil); + /* INFO PROFIL */ + JTextArea txtInfo = new JTextArea(); + txtInfo.setBounds(50, 50, 750, 400); + txtInfo.setEditable(false); + txtInfo.setBackground(new Color(230,230,230)); + txtInfo.setFont(new Font(txtInfo.getText(),Font.ROMAN_BASELINE, 20)); + // txtTitre.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + + txtInfo.setText( "\n\n Login : " + unProfil.getLogin() + + "\n\n Nom : " + unProfil.getNom() + + "\n\n Prénom : " + unProfil.getPrenom() + + "\n\n Adresse Email : " + unProfil.getEmail() + + "\n\n Votre Age : " + unProfil.getAge() + + "\n\n Votre Sexe : " + unProfil.getSexe() + + "\n\n Galop optenue : " + unProfil.getGalop()); + this.add(txtInfo); + + this.setVisible(true); + } + public static void main(String[] args) { + // TODO Stub de la méthode généré automatiquement + + } +} + + + diff --git a/Les Ecuries de la Boissière/src/vue/VueChevaux.java b/Les Ecuries de la Boissière/src/vue/VueChevaux.java new file mode 100644 index 0000000..11f7948 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueChevaux.java @@ -0,0 +1,62 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import controleur.Cheval; +import modele.ModeleCheval; + +public class VueChevaux extends JPanel implements ActionListener +{ + private JTable tableChevaux; + + public VueChevaux () + { /*AFICHAGE ECRAN*/ + this.setBounds(50, 100, 900, 600); + this.setLayout(null); + this.setBackground(new Color(247,245,226)); + /*TITRE*/ + JLabel titre = new JLabel(" Liste des chevaux "); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + titre.setBounds(335, 5, 300, 50); + this.add(titre); + /*GRILLE BDD*/ + String column [] = {"Nom", "Sexe", "Âge", "Propriétaire", "Race", "Robe", "Type"}; + this.tableChevaux = new JTable(this.extraireChevaux(), column); + JScrollPane uneScroll = new JScrollPane(this.tableChevaux); + uneScroll.setBounds(10, 50, 880, 540); + tableChevaux.setEnabled(false); + this.add(uneScroll); + + this.setVisible(false); + } + @Override + public void actionPerformed(ActionEvent e) { + } + //extraire les Chevaux BDD + public Object [][] extraireChevaux () + { + ArrayList lesChevaux = ModeleCheval.selectAll(); + Object [][] donnees = new Object [lesChevaux.size()][7]; + int i =0; + for (Cheval unCheval : lesChevaux) + { + donnees[i][0] = unCheval.getNom(); + donnees[i][1] = unCheval.getSexe(); + donnees[i][2] = unCheval.getAge(); + donnees[i][3] = unCheval.getProprietaire(); + donnees[i][4] = unCheval.getRace(); + donnees[i][5] = unCheval.getRobe(); + donnees[i][6] = unCheval.getType(); + i++; + } + return donnees; + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueChevauxA.java b/Les Ecuries de la Boissière/src/vue/VueChevauxA.java new file mode 100644 index 0000000..6ea96a8 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueChevauxA.java @@ -0,0 +1,191 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Cheval; +import modele.ModeleCheval; + +public class VueChevauxA extends JPanel implements ActionListener +{ + private JTextField txtNom = new JTextField(); + private JTextField txtProprietaire = new JTextField(); + private JTextField txtImgC = new JTextField(); + private JTextField txtAge = new JTextField(); + private JTextField txtRobe = new JTextField(); + private JTextField txtType = new JTextField(); + private JTextField txtRace = new JTextField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("MÂLE"); + private JRadioButton jrF = new JRadioButton("FEMELLE"); + private String txtSexe =null; + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Ajouter"); + +public VueChevauxA() +{ + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel(""); JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + /*AFFICHAGE ECRAN & DONNEES BDD*/ + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(14, 4)); + this.setBackground(new Color(230,230,230)); + + + /*TITRE*/ + JLabel titre = new JLabel("Ajouter un cheval"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); this.add(lbVide5); this.add(lbVide6); + + JLabel lbNom = new JLabel(" *Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbProprietaire = new JLabel(" *Propriétaire :"); + lbProprietaire.setFont(new Font(lbProprietaire.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbProprietaire); this.add(this.txtProprietaire); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbType = new JLabel(" Type :"); + lbType.setFont(new Font(lbType.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbType); this.add(this.txtType); + + JLabel lbRace = new JLabel(" Race :"); + lbRace.setFont(new Font(lbRace.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbRace); this.add(this.txtRace); + + JLabel lbRobe = new JLabel(" Robe :"); + lbRobe.setFont(new Font(lbRobe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbRobe); this.add(this.txtRobe); + + JLabel lbImgC = new JLabel(" Image :"); + lbImgC.setFont(new Font(lbImgC.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbImgC); this.add(this.txtImgC); + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); +} + + +@Override +public void actionPerformed(ActionEvent e) { + if(jrH.isSelected()) + { + txtSexe = jrH.getText(); + } + else if(jrF.isSelected()) + { + txtSexe = jrF.getText(); + } + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtAge.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtProprietaire.setBackground(Color.WHITE); + this.txtRobe.setBackground(Color.WHITE); + this.txtType.setBackground(Color.WHITE); + this.txtRace.setBackground(Color.WHITE); + + + this.txtRobe.setText(null); + this.txtNom.setText(null); + this.txtProprietaire.setText(null); + jrH.setSelected(true); + this.txtRace.setText(null); + this.txtImgC.setText(null); + this.txtAge.setText(null); + this.txtType.setText(null); + } + + else if (e.getSource()==this.btAjouter) + { + int age=Integer.parseInt(this.txtAge.getText()); + String nom = this.txtNom.getText(); + String sexe = this.txtSexe; + jrH.setSelected(true); + String proprietaire = this.txtProprietaire.getText(); + String robe = this.txtRobe.getText(); + String type = this.txtType.getText(); + String race = this.txtRace.getText(); + String imagecheval = this.txtImgC.getText(); + + try{ + + if(nom.equals("")||proprietaire.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + + else + { + Cheval unCheval = new Cheval( age, nom, sexe, proprietaire, robe, type, race, imagecheval); + ModeleCheval.insert(unCheval); + JOptionPane.showMessageDialog(this, "Insertion reussie"); + this.txtAge.setText(null); + this.txtNom.setText(null); + jrH.setSelected(true); + this.txtProprietaire.setText(null); + this.txtRobe.setText(null); + this.txtType.setText(null); + this.txtRace.setText(null); + this.txtImgC.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtAge.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtProprietaire.setBackground(Color.RED); + this.txtRobe.setBackground(Color.RED); + this.txtType.setBackground(Color.RED); + this.txtRace.setBackground(Color.RED); + } + + } + +} + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueChevauxM.java b/Les Ecuries de la Boissière/src/vue/VueChevauxM.java new file mode 100644 index 0000000..a143e95 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueChevauxM.java @@ -0,0 +1,198 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Cheval; +import modele.ModeleCheval; + +public class VueChevauxM extends JPanel implements ActionListener +{ + private JTextField txtNom = new JTextField(); + private JTextField txtProprietaire = new JTextField(); + private JTextField txtImgC = new JTextField(); + private JTextField txtAge = new JTextField(); + private JTextField txtRobe = new JTextField(); + private JTextField txtType = new JTextField(); + private JTextField txtRace = new JTextField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("MÂLE"); + private JRadioButton jrF = new JRadioButton("FEMELLE"); + private String txtSexe = null; + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Modifier"); + +public VueChevauxM() +{ + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel(""); JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(14, 4)); + this.setBackground(new Color(230,230,230)); + + + /*TITRE*/ + JLabel titre = new JLabel("Modifier un cheval"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide5); + JLabel lbinfoM = new JLabel(" ! Tous les champs doivent ętre remplis ! "); + lbinfoM.setFont(new Font(lbinfoM.getText(), Font.CENTER_BASELINE, 12)); + lbinfoM.setForeground(Color.red); + this.add(lbinfoM); + this.add(lbVide6); + + JLabel lbNom = new JLabel(" *Nom d'un cheval éxistant :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbProprietaire = new JLabel(" *Proprietaire du cheval:"); + lbProprietaire.setFont(new Font(lbProprietaire.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbProprietaire); this.add(this.txtProprietaire); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbType = new JLabel(" Type :"); + lbType.setFont(new Font(lbType.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbType); this.add(this.txtType); + + JLabel lbRace = new JLabel(" Race :"); + lbRace.setFont(new Font(lbRace.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbRace); this.add(this.txtRace); + + JLabel lbRobe = new JLabel(" Robe :"); + lbRobe.setFont(new Font(lbRobe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbRobe); this.add(this.txtRobe); + + JLabel lbImgC = new JLabel(" Image :"); + lbImgC.setFont(new Font(lbImgC.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbImgC); this.add(this.txtImgC); + + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); +} + + +@Override +public void actionPerformed(ActionEvent e) { + if(jrH.isSelected()) + { + txtSexe = jrH.getText(); + } + else if(jrF.isSelected()) + { + txtSexe = jrF.getText(); + } + + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtAge.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtProprietaire.setBackground(Color.WHITE); + this.txtRobe.setBackground(Color.WHITE); + this.txtType.setBackground(Color.WHITE); + this.txtRace.setBackground(Color.WHITE); + + + this.txtAge.setText(null); + this.txtNom.setText(null); + jrH.setSelected(true); + this.txtProprietaire.setText(null); + this.txtRobe.setText(null); + this.txtType.setText(null); + this.txtRace.setText(null); + this.txtImgC.setText(null); + } + + else if (e.getSource()==this.btAjouter) + { + int age=Integer.parseInt(this.txtAge.getText()); + String nom = this.txtNom.getText(); + String sexe = this.txtSexe; + String proprietaire = this.txtProprietaire.getText(); + String robe = this.txtRobe.getText(); + String type = this.txtType.getText(); + String race = this.txtRace.getText(); + String imagecheval = this.txtImgC.getText(); + + try{ + + if(nom.equals("")||proprietaire.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + + else + { + Cheval unCheval = new Cheval( age, nom, sexe, proprietaire, robe, type, race, imagecheval); + ModeleCheval.update(unCheval, nom, proprietaire); + JOptionPane.showMessageDialog(this, "Modification reussie"); + this.txtAge.setText(null); + this.txtNom.setText(null); + jrH.setSelected(true); + this.txtProprietaire.setText(null); + this.txtRobe.setText(null); + this.txtType.setText(null); + this.txtRace.setText(null); + this.txtImgC.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtAge.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtProprietaire.setBackground(Color.RED); + this.txtRobe.setBackground(Color.RED); + this.txtType.setBackground(Color.RED); + this.txtRace.setBackground(Color.RED); + this.txtImgC.setBackground(Color.RED); + } + + } + +} + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueChevauxS.java b/Les Ecuries de la Boissière/src/vue/VueChevauxS.java new file mode 100644 index 0000000..e4fa280 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueChevauxS.java @@ -0,0 +1,102 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Cheval; +import modele.ModeleCheval; + +public class VueChevauxS extends JPanel implements ActionListener +{ + private JTextField txtNom = new JTextField(); + private JTextField txtProprietaire = new JTextField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Supprimer"); + + public VueChevauxS() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + + this.setBounds(150, 250, 700, 150); + this.setLayout(new GridLayout(6,2)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Supprimer un cheval"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide3); this.add(lbVide4); this.add(lbVide5); + + JLabel lbNom = new JLabel(" Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbProp = new JLabel(" Propriétaire :"); + lbProp.setFont(new Font(lbProp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbProp); this.add(this.txtProprietaire); + + this.add(lbVide1); this.add(lbVide2); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + } + + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtNom.setBackground(Color.WHITE); + this.txtProprietaire.setBackground(Color.WHITE); + + + this.txtNom.setText(null); + this.txtProprietaire.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + + String nom = this.txtNom.getText(); + String proprietaire = this.txtProprietaire.getText(); + + try{ + + Cheval unCheval = new Cheval(nom , proprietaire); + ModeleCheval.delete(nom,proprietaire); + JOptionPane.showMessageDialog(this, "Suppression reussie de " + unCheval.getNom()); + this.txtNom.setText(null); + this.txtProprietaire.setText(null); + + + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtNom.setBackground(Color.RED); + this.txtProprietaire.setBackground(Color.RED); + } + } + } +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueConnexion.java b/Les Ecuries de la Boissière/src/vue/VueConnexion.java new file mode 100644 index 0000000..810a92f --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueConnexion.java @@ -0,0 +1,87 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import controleur.Gestion; +import controleur.Profil; +import modele.Modele; + +public class VueConnexion extends JPanel implements ActionListener +{ + private JTextField txtMail = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btSeConnecter = new JButton("Se connecter"); + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel(""); + + public VueConnexion() + { + this.setBounds(200, 350, 600, 200); + this.setLayout(new GridLayout(4,2)); + this.setBackground(new Color(230,230,230)); + + + JLabel lbMail = new JLabel(" E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMail); this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMdp); this.add(this.txtMdp); + + this.add(lbVide1);this.add(lbVide2); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btSeConnecter); + this.btSeConnecter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btSeConnecter.addActionListener(this); + + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.btAnnuler) + { + this.txtMail.setText(""); + this.txtMdp.setText(""); + } + else if(e.getSource() == this.btSeConnecter) + { + String adressemail = this.txtMail.getText(); + String mdp = new String(this.txtMdp.getPassword()); + + Profil unProfil = Modele.selectWhere(adressemail, mdp); + if(unProfil == null) + { + JOptionPane.showMessageDialog(this, "Veuillez vérifier vos identifiants !"); + } + else + { + JOptionPane.showMessageDialog(this,"
Connexion réussie!
Bonjour " + unProfil.getLogin() + "
"); + + // ouvrir le menu général + new Generale(unProfil); + this.txtMail.setText(""); + this.txtMdp.setText(""); + Gestion.rendreVisible(false); + } + } + } +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueCours.java b/Les Ecuries de la Boissière/src/vue/VueCours.java new file mode 100644 index 0000000..136b681 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueCours.java @@ -0,0 +1,62 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import controleur.Cours; +import modele.ModeleCours; + +public class VueCours extends JPanel implements ActionListener +{ + private JTable tableCours; + + public VueCours () + { /*AFFICHAGE ECRAN*/ + this.setBounds(50, 100, 900, 600); + this.setLayout(null); + this.setBackground(new Color(247,245,226)); + /*TITRE*/ + JLabel titre = new JLabel(" Liste des cours "); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + titre.setBounds(345, 5, 300, 50); + add(titre); + /*GRILLE BDD*/ + String column [] = {"Date du cours", "Débute ŕ", "Fini ŕ", "ID d'élčve", "ID Formateur","ID de Cheval" }; + this.tableCours = new JTable(this.extraireCours(), column); + JScrollPane uneScroll = new JScrollPane(this.tableCours); + uneScroll.setBounds(10, 50, 880, 540); + tableCours.setEnabled(false); + this.add(uneScroll); + + this.setVisible(false); + } + @Override + public void actionPerformed(ActionEvent e) { + } + //extraire les Cours BDD + public Object [][] extraireCours () + { + ArrayList lesCours = ModeleCours.selectAll(); + Object [][] donnees = new Object [lesCours.size()][6]; + int i =0; + for (Cours unCours : lesCours) + { + donnees[i][0] = unCours.getDateCours(); + donnees[i][1] = unCours.getHeureDebut(); + donnees[i][2] = unCours.getHeureFin(); + donnees[i][3] = unCours.getIdEleve(); + donnees[i][4] = unCours.getIdFormateur(); + donnees[i][5] = unCours.getIdCheval(); + + i++; + } + return donnees; + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueCoursA.java b/Les Ecuries de la Boissière/src/vue/VueCoursA.java new file mode 100644 index 0000000..34a7f26 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueCoursA.java @@ -0,0 +1,171 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.ButtonGroup; +import javax.swing.JComboBox; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import controleur.Cours; +import modele.ModeleCours; + +public class VueCoursA extends JPanel implements ActionListener +{ + private JTextField txtDate = new JTextField(); + private JTextField txtHeureD = new JTextField(); + private JTextField txtHeureF = new JTextField(); + private JTextField txtEleve = new JTextField(); + private JTextField txtFormateur = new JTextField(); + private JTextField txtCheval = new JTextField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Ajouter"); + + public VueCoursA() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel(""); JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + /* AFFICHAGE ECRAN*/ + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(8, 4)); + this.setBackground(new Color(230,230,230)); + + + /*TITRE & DONNEES BDD*/ + JLabel titre = new JLabel("Ajouter un cours"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); this.add(lbVide5); this.add(lbVide6); + + JLabel lbDate = new JLabel(" *Date du cours :"); + lbDate.setFont(new Font(lbDate.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbDate); this.add(this.txtDate); + + JLabel lbHeureD = new JLabel(" *Heure de début :"); + lbHeureD.setFont(new Font(lbHeureD.getText(), Font.CENTER_BASELINE, 18)); + this.txtHeureD.setText("HH:MM:SS"); + this.add(lbHeureD); this.add(this.txtHeureD); + + JLabel lbHeureF = new JLabel(" *Heure de fin :"); + lbHeureF.setFont(new Font(lbHeureF.getText(), Font.CENTER_BASELINE, 18)); + this.txtHeureF.setText("HH:MM:SS"); + this.add(lbHeureF); this.add(this.txtHeureF); + /* + JLabel lbEleve = new JLabel(" *Numéro d'élčve :"); + lbEleve.setFont(new Font(lbEleve.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbEleve); this.add(this.txtEleve); + + JLabel lbFormateur = new JLabel(" *Numéro de formateur:"); + lbFormateur.setFont(new Font(lbFormateur.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbFormateur); this.add(this.txtFormateur); + + JLabel lbCheval = new JLabel(" *Numéro de cheval :"); + lbCheval.setFont(new Font(lbCheval.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbCheval); this.add(this.txtCheval); + */ + JLabel lbinfo = new JLabel(" Format Date : 2017-07-13 - Temps : 11:00:00 "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.txtDate.setText("AAAA-MM-JJ"); + this.add(lbinfo); + this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + } + + /*EVENT*/ + @Override + public void actionPerformed(ActionEvent e) { + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtDate.setBackground(Color.WHITE); + this.txtHeureD.setBackground(Color.WHITE); + this.txtHeureF.setBackground(Color.WHITE); + this.txtEleve.setBackground(Color.WHITE); + this.txtFormateur.setBackground(Color.WHITE); + this.txtCheval.setBackground(Color.WHITE); + + this.txtDate.setText("AAAA-MM-JJ"); + this.txtHeureD.setText("HH:MM:SS"); + this.txtHeureF.setText("HH:MM:SS"); + this.txtEleve.setText(null); + this.txtFormateur.setText(null); + this.txtCheval.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + String datecours = this.txtDate.getText(); + String heuredebut = this.txtHeureD.getText(); + String heurefin = this.txtHeureF.getText(); + + + try{ + + if(datecours.equals("")||heuredebut.equals("")||heurefin.equals("")||heuredebut.equals("HH:MM:SS")||heurefin.equals("HH:MM:SS")||datecours.equals("AAAA-MM-JJ")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs correcte dans les champs obligatoire"); + } + + else + { + Cours unCours = new Cours(datecours, heuredebut, heurefin); + ModeleCours.insert(unCours); + JOptionPane.showMessageDialog(this, "Insertion reussie"); + this.txtFormateur.setText(null); + this.txtCheval.setText(null); + this.txtHeureD.setText("HH:MM:SS"); + this.txtHeureF.setText("HH:MM:SS"); + this.txtEleve.setText(null); + this.txtDate.setText("AAAA-MM-JJ"); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtDate.setBackground(Color.RED); + this.txtHeureD.setBackground(Color.RED); + this.txtHeureF.setBackground(Color.RED); + this.txtEleve.setBackground(Color.RED); + this.txtFormateur.setBackground(Color.RED); + this.txtCheval.setBackground(Color.RED); + } + + } + + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } + +} + + diff --git a/Les Ecuries de la Boissière/src/vue/VueCoursM.java b/Les Ecuries de la Boissière/src/vue/VueCoursM.java new file mode 100644 index 0000000..9c64075 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueCoursM.java @@ -0,0 +1,177 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import controleur.Cours; +import modele.ModeleCours; + +public class VueCoursM extends JPanel implements ActionListener +{ + private JTextField txtDate = new JTextField(); + private JTextField txtHeureD = new JTextField(); + private JTextField txtHeureF = new JTextField(); + private JTextField txtEleve = new JTextField(); + private JTextField txtFormateur = new JTextField(); + private JTextField txtCheval = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Modifier"); + + public VueCoursM() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(11, 4)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Modifier un cours"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); + JLabel lbinfoM = new JLabel(" ! Tous les champs doivent ętre remplis ! "); + lbinfoM.setFont(new Font(lbinfoM.getText(), Font.CENTER_BASELINE, 12)); + lbinfoM.setForeground(Color.red); + this.add(lbinfoM); + this.add(lbVide6); + + JLabel lbDate = new JLabel(" *Date d'un cours existant :"); + lbDate.setFont(new Font(lbDate.getText(), Font.CENTER_BASELINE, 18)); + this.txtDate.setText("AAAA-MM-JJ"); + this.add(lbDate); this.add(this.txtDate); + + JLabel lbHeureD = new JLabel(" *Heure de début du cours :"); + lbHeureD.setFont(new Font(lbHeureD.getText(), Font.CENTER_BASELINE, 18)); + this.txtHeureD.setText("HH:MM:SS"); + this.add(lbHeureD); this.add(this.txtHeureD); + + JLabel lbHeureF = new JLabel(" Heure de fin :"); + lbHeureF.setFont(new Font(lbHeureF.getText(), Font.CENTER_BASELINE, 18)); + this.txtHeureF.setText("HH:MM:SS"); + this.add(lbHeureF); this.add(this.txtHeureF); + + JLabel lbEleve = new JLabel(" Numéro d'élčve :"); + lbEleve.setFont(new Font(lbEleve.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbEleve); this.add(this.txtEleve); + + + JLabel lbFormateur = new JLabel(" Numéro de formateur:"); + lbFormateur.setFont(new Font(lbFormateur.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbFormateur); this.add(this.txtFormateur); + + JLabel lbCheval = new JLabel(" Numéro de cheval :"); + lbCheval.setFont(new Font(lbCheval.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbCheval); this.add(this.txtCheval); + + JLabel lbinfoF = new JLabel(" Format Date : 2017-07-13 - Temps : 11:00:00 "); + lbinfoF.setFont(new Font(lbinfoF.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfoF); + this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + } + + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtDate.setBackground(Color.WHITE); + this.txtHeureD.setBackground(Color.WHITE); + this.txtHeureF.setBackground(Color.WHITE); + this.txtEleve.setBackground(Color.WHITE); + this.txtFormateur.setBackground(Color.WHITE); + this.txtCheval.setBackground(Color.WHITE); + this.txtMdp.setBackground(Color.WHITE); + + this.txtFormateur.setText(null); + this.txtCheval.setText(null); + this.txtHeureD.setText("HH:MM:SS"); + this.txtHeureF.setText("HH:MM:SS"); + this.txtEleve.setText(null); + this.txtDate.setText("AAAA-MM-JJ"); + + } + + else if (e.getSource()==this.btAjouter) + { + String datecours = this.txtDate.getText(); + String heuredebut = this.txtHeureD.getText(); + String heurefin = this.txtHeureF.getText(); + int ideleve = Integer.parseInt(this.txtEleve.getText()); + int idformateur = Integer.parseInt(this.txtFormateur.getText()); + int idcheval = Integer.parseInt(this.txtCheval.getText()); + try{ + + if(datecours.equals("")||heuredebut.equals("")||heurefin.equals("")||heuredebut.equals("HH:MM:SS")||heurefin.equals("HH:MM:SS")||datecours.equals("AAAA-MM-JJ")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs correcte dans les champs obligatoire"); + } + + else + { + Cours unCours = new Cours(datecours, heuredebut, heurefin, ideleve, idformateur, idcheval); + ModeleCours.update(unCours, datecours, heuredebut); + JOptionPane.showMessageDialog(this, "Modification reussie"); + this.txtFormateur.setText(null); + this.txtCheval.setText(null); + this.txtHeureD.setText("HH:MM:SS"); + this.txtHeureF.setText("HH:MM:SS"); + this.txtEleve.setText(null); + this.txtDate.setText("AAAA-MM-JJ"); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtDate.setBackground(Color.RED); + this.txtHeureD.setBackground(Color.RED); + this.txtHeureF.setBackground(Color.RED); + this.txtEleve.setBackground(Color.RED); + this.txtFormateur.setBackground(Color.RED); + this.txtCheval.setBackground(Color.RED); + } + + } + + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } + +} + diff --git a/Les Ecuries de la Boissière/src/vue/VueCoursS.java b/Les Ecuries de la Boissière/src/vue/VueCoursS.java new file mode 100644 index 0000000..bef6346 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueCoursS.java @@ -0,0 +1,109 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import controleur.Cours; +import modele.ModeleCours; + + +public class VueCoursS extends JPanel implements ActionListener +{ + private JTextField txtDate = new JTextField(); + private JTextField txtHeureD = new JTextField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Supprimer"); + + public VueCoursS() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + + this.setBounds(150, 250, 700, 150); + this.setLayout(new GridLayout(6,2)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Supprimer un cours"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide3); this.add(lbVide4); this.add(lbVide5); + + JLabel lbDate = new JLabel(" Date du cours :"); + lbDate.setFont(new Font(lbDate.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbDate); this.add(this.txtDate); + + JLabel lbHeure = new JLabel(" Heure du cours :"); + lbHeure.setFont(new Font(lbHeure.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbHeure); this.add(this.txtHeureD); + + JLabel lbinfo = new JLabel(" Format Date : 2017-07-13 - Temps : 11:00:00 "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + this.add(lbVide2); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + } + + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtDate.setBackground(Color.WHITE); + this.txtHeureD.setBackground(Color.WHITE); + + + this.txtDate.setText(null); + this.txtHeureD.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + + String datecours = this.txtDate.getText(); + String heuredebut = this.txtHeureD.getText(); + + try{ + + Cours unCours = new Cours(datecours , heuredebut); + ModeleCours.delete(datecours,heuredebut); + JOptionPane.showMessageDialog(this, "Suppression reussie du cours de
" + unCours.getHeureDebut() + " du " + unCours.getDateCours()); + this.txtDate.setText(null); + this.txtHeureD.setText(null); + + + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtDate.setBackground(Color.RED); + this.txtHeureD.setBackground(Color.RED); + } + } + } +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueEleves.java b/Les Ecuries de la Boissière/src/vue/VueEleves.java new file mode 100644 index 0000000..0d4d8d8 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueEleves.java @@ -0,0 +1,66 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import controleur.Eleve; +import modele.ModeleEleve; + +public class VueEleves extends JPanel implements ActionListener +{ + private JTable tableEleves; + + public VueEleves () + {/*AFFICHAGE ECRAN*/ + this.setBounds(50, 100, 900, 600); + this.setLayout(null); + this.setBackground(new Color(247,245,226)); + /*TITRE*/ + JLabel titre = new JLabel(" Liste des élčves "); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + titre.setBounds(345, 5, 300, 50); + add(titre); + /*GRILLE BDD*/ + String column [] = {"Nom", "Prénom", "Email", "Sexe", "Âge", "Galop optenu", "Adresse", "Pseudo", "Mot de passe"}; + + this.tableEleves = new JTable(this.extraireEleves(), column); + + JScrollPane uneScroll = new JScrollPane(this.tableEleves); + uneScroll.setBounds(10, 50, 880, 540); + tableEleves.setEnabled(false); + this.add(uneScroll); + + this.setVisible(false); + } + @Override + public void actionPerformed(ActionEvent e) { + } + //extraire les Eleves BDD + public Object [][] extraireEleves () + { + ArrayList lesEleves = ModeleEleve.selectAll(); + Object [][] donnees = new Object [lesEleves.size()][9]; + int i =0; + for (Eleve unEleve : lesEleves) + { + donnees[i][0] = unEleve.getNom(); + donnees[i][1] = unEleve.getPrenom(); + donnees[i][2] = unEleve.getAdressEmail(); + donnees[i][3] = unEleve.getSexe(); + donnees[i][4] = unEleve.getAge(); + donnees[i][5] = unEleve.getGalop(); + donnees[i][6] = unEleve.getAdresse(); + donnees[i][7] = unEleve.getPseudo(); + donnees[i][8] = unEleve.getMdp(); + i++; + } + return donnees; + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueElevesA.java b/Les Ecuries de la Boissière/src/vue/VueElevesA.java new file mode 100644 index 0000000..25065be --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueElevesA.java @@ -0,0 +1,214 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Eleve; +import modele.ModeleEleve; + +public class VueElevesA extends JPanel implements ActionListener +{ + private JTextField txtPseudo = new JTextField(); + private JTextField txtNom = new JTextField(); + private JTextField txtPrenom = new JTextField(); + private JTextField txtAdresse = new JTextField(); + private JTextField txtMail = new JTextField(); + private JTextField txtImgE = new JTextField(); + private JTextField txtGalop = new JTextField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("HOMME"); + private JRadioButton jrF = new JRadioButton("FEMME"); + private String txtSexe =null; + private JTextField txtAge = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Ajouter"); + + public VueElevesA() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + /*AFFICHAGE ECRAN */ + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(16, 4)); + this.setBackground(new Color(230,230,230)); + + /*TITRE & DONNEES BDD*/ + JLabel titre = new JLabel("Ajouter un élčve"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); this.add(lbVide5); this.add(lbVide6); + + + JLabel lbPseudo = new JLabel(" *Pseudo :"); + lbPseudo.setFont(new Font(lbPseudo.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPseudo); this.add(this.txtPseudo); + + JLabel lbNom = new JLabel(" *Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbPrenom = new JLabel(" *Prenom :"); + lbPrenom.setFont(new Font(lbPrenom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPrenom); this.add(this.txtPrenom); + + JLabel lbAdresse = new JLabel(" Adresse :"); + lbAdresse.setFont(new Font(lbAdresse.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAdresse); this.add(this.txtAdresse); + + JLabel lbMail = new JLabel(" *E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMail); this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" *Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMdp); this.add(this.txtMdp); + + JLabel lbImgE = new JLabel(" Avatar :"); + lbImgE.setFont(new Font(lbImgE.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbImgE); this.add(this.txtImgE); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbGalop = new JLabel(" Galop :"); + lbGalop.setFont(new Font(lbGalop.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbGalop); this.add(this.txtGalop); + + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + } + + /*EVENT*/ + @Override + public void actionPerformed(ActionEvent e) { + if (jrH.isSelected()) { + txtSexe = jrH.getText(); + } + else if (jrF.isSelected()) { + txtSexe = jrF.getText(); + } + + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtAge.setBackground(Color.WHITE); + this.txtGalop.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtPrenom.setBackground(Color.WHITE); + this.txtAdresse.setBackground(Color.WHITE); + this.txtMail.setBackground(Color.WHITE); + this.txtPseudo.setBackground(Color.WHITE); + this.txtMdp.setBackground(Color.WHITE); + this.txtImgE.setBackground(Color.WHITE); + + this.txtPseudo.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtAdresse.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtMdp.setText(null); + this.txtImgE.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + int age=Integer.parseInt(this.txtAge.getText()); + int galop=Integer.parseInt(this.txtGalop.getText()); + String nom = this.txtNom.getText(); + String prenom = this.txtPrenom.getText(); + String adresse = this.txtAdresse.getText(); + String adressemail = this.txtMail.getText(); + String sexe = this.txtSexe; + String pseudo = this.txtPseudo.getText(); + String mdp = (this.txtMdp.getText()); + String imageeleve = this.txtImgE.getText(); + + try{ + if(pseudo.equals("")||nom.equals("")||prenom.equals("")||adressemail.equals("")||mdp.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + else + { + Eleve unEleve = new Eleve(age, galop, nom, prenom, adresse, adressemail, sexe, pseudo, mdp, imageeleve); + ModeleEleve.insert(unEleve); + JOptionPane.showMessageDialog(this, "Insertion reussie"); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtAdresse.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtSexe=(null); + this.txtPseudo.setText(null); + this.txtMdp.setText(null); + this.txtImgE.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtAge.setBackground(Color.RED); + this.txtGalop.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtPrenom.setBackground(Color.RED); + this.txtAdresse.setBackground(Color.RED); + this.txtMail.setBackground(Color.RED); + this.txtPseudo.setBackground(Color.RED); + this.txtMdp.setBackground(Color.RED); + this.txtImgE.setBackground(Color.RED); + } + + } + + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } + +} + diff --git a/Les Ecuries de la Boissière/src/vue/VueElevesM.java b/Les Ecuries de la Boissière/src/vue/VueElevesM.java new file mode 100644 index 0000000..1bfaa39 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueElevesM.java @@ -0,0 +1,209 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Eleve; +import modele.ModeleEleve; + +public class VueElevesM extends JPanel implements ActionListener +{ + private JTextField txtPseudo = new JTextField(); + private JTextField txtNom = new JTextField(); + private JTextField txtPrenom = new JTextField(); + private JTextField txtAdresse = new JTextField(); + private JTextField txtMail = new JTextField(); + private JTextField txtImgE = new JTextField(); + private JTextField txtGalop = new JTextField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("HOMME"); + private JRadioButton jrF = new JRadioButton("FEMME"); + private String txtSexe =null; + private JTextField txtAge = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Modifier"); + + public VueElevesM() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(16, 4)); + this.setBackground(new Color(230,230,230)); + + + /*TITRE*/ + JLabel titre = new JLabel("Modifier un élčve"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); + JLabel lbinfoM = new JLabel(" ! Tous les champs doivent ętre remplis ! "); + lbinfoM.setFont(new Font(lbinfoM.getText(), Font.CENTER_BASELINE, 12)); + lbinfoM.setForeground(Color.red); + this.add(lbinfoM); + this.add(lbVide6); + + JLabel lbPseudo = new JLabel(" *Pseudo d'un élčve éxistant:"); + lbPseudo.setFont(new Font(lbPseudo.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPseudo); this.add(this.txtPseudo); + + JLabel lbNom = new JLabel(" Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbPrenom = new JLabel(" Prenom :"); + lbPrenom.setFont(new Font(lbPrenom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPrenom); this.add(this.txtPrenom); + + JLabel lbAdresse = new JLabel(" Adresse :"); + lbAdresse.setFont(new Font(lbAdresse.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAdresse); this.add(this.txtAdresse); + + JLabel lbMail = new JLabel(" E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMail); this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMdp); this.add(this.txtMdp); + + JLabel lbImgE = new JLabel(" Avatar :"); + lbImgE.setFont(new Font(lbImgE.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbImgE); this.add(this.txtImgE); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbGalop = new JLabel(" Galop :"); + lbGalop.setFont(new Font(lbGalop.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbGalop); this.add(this.txtGalop); + + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires"); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + } + @Override + public void actionPerformed(ActionEvent e) { + if (jrH.isSelected()) { + txtSexe = jrH.getText(); + } + else if (jrF.isSelected()) { + txtSexe = jrF.getText(); + } + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtAge.setBackground(Color.WHITE); + this.txtGalop.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtPrenom.setBackground(Color.WHITE); + this.txtAdresse.setBackground(Color.WHITE); + this.txtMail.setBackground(Color.WHITE); + this.txtPseudo.setBackground(Color.WHITE); + this.txtMdp.setBackground(Color.WHITE); + this.txtImgE.setBackground(Color.WHITE); + + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtAdresse.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtSexe=(null); + this.txtPseudo.setText(null); + this.txtMdp.setText(null); + this.txtImgE.setText(null); + } + else if (e.getSource()==this.btAjouter) + { + int age=Integer.parseInt(this.txtAge.getText()); + int galop=Integer.parseInt(this.txtGalop.getText()); + String nom = this.txtNom.getText(); + String prenom = this.txtPrenom.getText(); + String adresse = this.txtAdresse.getText(); + String adressemail = this.txtMail.getText(); + String sexe = this.txtSexe; + String pseudo = this.txtPseudo.getText(); + String mdp = (this.txtMdp.getText()); + String imageeleve = this.txtImgE.getText(); + + try{ + + if(pseudo.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + else + { + Eleve unEleve = new Eleve(age, galop, nom, prenom, adresse, adressemail, sexe, pseudo, mdp, imageeleve); + ModeleEleve.update(unEleve, pseudo); + JOptionPane.showMessageDialog(this, "Modification reussie"); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtAdresse.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtSexe=(null); + this.txtPseudo.setText(null); + this.txtMdp.setText(null); + this.txtImgE.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtAge.setBackground(Color.RED); + this.txtGalop.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtPrenom.setBackground(Color.RED); + this.txtAdresse.setBackground(Color.RED); + this.txtMail.setBackground(Color.RED); + this.txtPseudo.setBackground(Color.RED); + this.txtMdp.setBackground(Color.RED); + this.txtImgE.setBackground(Color.RED); + } + } + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + } + } +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueElevesS.java b/Les Ecuries de la Boissière/src/vue/VueElevesS.java new file mode 100644 index 0000000..875e069 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueElevesS.java @@ -0,0 +1,96 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Eleve; +import modele.ModeleEleve; + +public class VueElevesS extends JPanel implements ActionListener +{ + + private JTextField txtPseudo = new JTextField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Supprimer"); + + public VueElevesS() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + + this.setBounds(150, 250, 700, 150); + this.setLayout(new GridLayout(5,2)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Supprimer un élčve"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide3); this.add(lbVide4); this.add(lbVide5); + + JLabel lbPseudo = new JLabel(" Pseudo :"); + lbPseudo.setFont(new Font(lbPseudo.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPseudo); this.add(this.txtPseudo); + + this.add(lbVide1); this.add(lbVide2); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + } + + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + + this.txtPseudo.setBackground(Color.WHITE); + this.txtPseudo.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + + String pseudo = this.txtPseudo.getText(); + + try{ + + Eleve unEleve = new Eleve(pseudo); + ModeleEleve.delete(pseudo); + JOptionPane.showMessageDialog(this, "Suppression reussie de " + unEleve.getPseudo()); + this.txtPseudo.setText(null); + + + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtPseudo.setBackground(Color.RED); + + } + + } + + } + +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueFormateurs.java b/Les Ecuries de la Boissière/src/vue/VueFormateurs.java new file mode 100644 index 0000000..eda388b --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueFormateurs.java @@ -0,0 +1,63 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import controleur.Formateur; +import modele.ModeleFormateur; + +public class VueFormateurs extends JPanel implements ActionListener +{ + private JTable tableFormateurs; + + public VueFormateurs () + { /*AFFICHAGE ECRAN*/ + this.setBounds(50, 100, 900, 600); + this.setLayout(null); + this.setBackground(new Color(247,245,226)); + /*TITRE*/ + JLabel titre = new JLabel(" Liste des formateurs "); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + titre.setBounds(315, 5, 300, 50); + add(titre); + /*GRILLE BDD*/ + String column [] = {"Nom", "Prénom", "Email", "Sexe", "Âge", "Galop optenu", "Login", "Mot de passe"}; + this.tableFormateurs = new JTable(this.extraireFormateurs(), column); + JScrollPane uneScroll = new JScrollPane(this.tableFormateurs); + uneScroll.setBounds(10, 50, 880, 540); + tableFormateurs.setEnabled(false); + this.add(uneScroll); + + this.setVisible(false); + } + @Override + public void actionPerformed(ActionEvent e) { + } + //extraire les Formateurs BDD + public Object [][] extraireFormateurs () + { + ArrayList lesFormateurs = ModeleFormateur.selectAll(); + Object [][] donnees = new Object [lesFormateurs.size()][8]; + int i =0; + for (Formateur unFormateur : lesFormateurs) + { + donnees[i][0] = unFormateur.getNom(); + donnees[i][1] = unFormateur.getPrenom(); + donnees[i][2] = unFormateur.getAdressEmail(); + donnees[i][3] = unFormateur.getSexe(); + donnees[i][4] = unFormateur.getAge(); + donnees[i][5] = unFormateur.getGalop(); + donnees[i][6] = unFormateur.getLogin(); + donnees[i][7] = unFormateur.getMdp(); + i++; + } + return donnees; + } +} diff --git a/Les Ecuries de la Boissière/src/vue/VueFormateursA.java b/Les Ecuries de la Boissière/src/vue/VueFormateursA.java new file mode 100644 index 0000000..c5b76e1 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueFormateursA.java @@ -0,0 +1,198 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Formateur; +import modele.ModeleFormateur; + +public class VueFormateursA extends JPanel implements ActionListener +{ + private JTextField txtLogin = new JTextField(); + private JTextField txtNom = new JTextField(); + private JTextField txtPrenom = new JTextField(); + private JTextField txtMail = new JTextField(); + private JTextField txtAge = new JTextField(); + private JTextField txtGalop = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("HOMME"); + private JRadioButton jrF = new JRadioButton("FEMME"); + private String txtSexe =null; + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Ajouter"); + + public VueFormateursA() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel(""); JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + /* AFFICHAGE ECRAN*/ + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(14, 4)); + this.setBackground(new Color(230,230,230)); + + + /*TITRE & DONNEES BDD*/ + JLabel titre = new JLabel("Ajouter un formateur"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); this.add(lbVide5); this.add(lbVide6); + + JLabel lbLogin = new JLabel(" *Login :"); + lbLogin.setFont(new Font(lbLogin.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbLogin); this.add(this.txtLogin); + + JLabel lbNom = new JLabel(" *Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbPrenom = new JLabel(" *Prenom :"); + lbPrenom.setFont(new Font(lbPrenom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPrenom); this.add(this.txtPrenom); + + JLabel lbMail = new JLabel(" *E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMail); this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" *Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMdp); this.add(this.txtMdp); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbGalop = new JLabel(" Galop :"); + lbGalop.setFont(new Font(lbGalop.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbGalop); this.add(this.txtGalop); + + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + } + + /*EVENT*/ + @Override + public void actionPerformed(ActionEvent e) { + if(jrH.isSelected()) + { + txtSexe = jrH.getText(); + } + else if(jrF.isSelected()) + { + txtSexe = jrF.getText(); + } + + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtLogin.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtPrenom.setBackground(Color.WHITE); + this.txtMail.setBackground(Color.WHITE); + this.txtAge.setBackground(Color.WHITE); + this.txtGalop.setBackground(Color.WHITE); + this.txtMdp.setBackground(Color.WHITE); + + this.txtLogin.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtMdp.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + String login = this.txtLogin.getText(); + String nom = this.txtNom.getText(); + String prenom = this.txtPrenom.getText(); + String adressemail = this.txtMail.getText(); + String sexe = this.txtSexe; + int age=Integer.parseInt(this.txtAge.getText()); + int galop=Integer.parseInt(this.txtGalop.getText()); + @SuppressWarnings("deprecation") + String mdp = (this.txtMdp.getText()); + + try{ + + if(login.equals("")||nom.equals("")||prenom.equals("")||adressemail.equals("")||mdp.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + + else + { + Formateur unFormateur = new Formateur(age, galop, nom, prenom, adressemail, sexe, login, mdp); + ModeleFormateur.insert(unFormateur); + JOptionPane.showMessageDialog(this, "Insertion reussie"); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtLogin.setText(null); + this.txtMdp.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtLogin.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtPrenom.setBackground(Color.RED); + this.txtMail.setBackground(Color.RED); + this.txtAge.setBackground(Color.RED); + this.txtGalop.setBackground(Color.RED); + this.txtMdp.setBackground(Color.RED); + } + + } + + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } + +} + diff --git a/Les Ecuries de la Boissière/src/vue/VueFormateursM.java b/Les Ecuries de la Boissière/src/vue/VueFormateursM.java new file mode 100644 index 0000000..eb05f3c --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueFormateursM.java @@ -0,0 +1,200 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Formateur; +import modele.ModeleFormateur; + +public class VueFormateursM extends JPanel implements ActionListener +{ + private JTextField txtLogin = new JTextField(); + private JTextField txtNom = new JTextField(); + private JTextField txtPrenom = new JTextField(); + private JTextField txtMail = new JTextField(); + private JTextField txtAge = new JTextField(); + private JTextField txtGalop = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private ButtonGroup bg = new ButtonGroup(); + private JRadioButton jrH = new JRadioButton("HOMME"); + private JRadioButton jrF = new JRadioButton("FEMME"); + private String txtSexe =null; + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Modifier"); + + public VueFormateursM() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel(""); JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + JLabel lbVide6 = new JLabel(""); + this.setBounds(50, 130, 900, 500); + this.setLayout(new GridLayout(14, 4)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Modifier un formateur"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); + JLabel lbinfoM = new JLabel(" ! Tous les champs doivent ętre remplis ! "); + lbinfoM.setFont(new Font(lbinfoM.getText(), Font.CENTER_BASELINE, 12)); + lbinfoM.setForeground(Color.red); + this.add(lbinfoM); + this.add(lbVide6); + + JLabel lbLogin = new JLabel(" *Login d'un formateur existant :"); + lbLogin.setFont(new Font(lbLogin.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbLogin); this.add(this.txtLogin); + + JLabel lbNom = new JLabel(" Nom :"); + lbNom.setFont(new Font(lbNom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbNom); this.add(this.txtNom); + + JLabel lbPrenom = new JLabel(" Prenom :"); + lbPrenom.setFont(new Font(lbPrenom.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbPrenom); this.add(this.txtPrenom); + + JLabel lbMail = new JLabel(" E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMail); this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbMdp); this.add(this.txtMdp); + + JLabel lbSexe = new JLabel(" Sexe :"); + lbSexe.setFont(new Font(lbSexe.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbSexe); this.add(lbVide2); + jrH.setSelected(true); + bg.add(jrH); this.add(jrH); + bg.add(jrF); this.add(jrF); + + JLabel lbAge = new JLabel(" Âge :"); + lbAge.setFont(new Font(lbAge.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbAge); this.add(this.txtAge); + + JLabel lbGalop = new JLabel(" Galop :"); + lbGalop.setFont(new Font(lbGalop.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbGalop); this.add(this.txtGalop); + + this.add(lbVide3); this.add(lbVide4); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + + JLabel lbinfo = new JLabel(" Les champs précédés d'une * sont obligatoires "); + lbinfo.setFont(new Font(lbinfo.getText(), Font.CENTER_BASELINE, 12)); + this.add(lbinfo); + } + + + @Override + public void actionPerformed(ActionEvent e) { + if(jrH.isSelected()) + { + txtSexe = jrH.getText(); + } + else if(jrF.isSelected()) + { + txtSexe = jrF.getText(); + } + + + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + this.txtLogin.setBackground(Color.WHITE); + this.txtNom.setBackground(Color.WHITE); + this.txtPrenom.setBackground(Color.WHITE); + this.txtMail.setBackground(Color.WHITE); + this.txtAge.setBackground(Color.WHITE); + this.txtGalop.setBackground(Color.WHITE); + this.txtMdp.setBackground(Color.WHITE); + + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtLogin.setText(null); + this.txtMdp.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + String login = this.txtLogin.getText(); + String nom = this.txtNom.getText(); + String prenom = this.txtPrenom.getText(); + String adressemail = this.txtMail.getText(); + String sexe = this.txtSexe; + int age=Integer.parseInt(this.txtAge.getText()); + int galop=Integer.parseInt(this.txtGalop.getText()); + String mdp = (this.txtMdp.getText()); + + try{ + + if(login.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir des valeurs dans les champs obligatoire"); + } + + else + { + Formateur unFormateur = new Formateur(age, galop, nom, prenom, adressemail, sexe, login, mdp); + ModeleFormateur.update(unFormateur, login); + JOptionPane.showMessageDialog(this, "Modification reussie"); + this.txtAge.setText(null); + this.txtGalop.setText(null); + this.txtNom.setText(null); + this.txtPrenom.setText(null); + this.txtMail.setText(null); + jrH.setSelected(true); + this.txtLogin.setText(null); + this.txtMdp.setText(null); + } + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtLogin.setBackground(Color.RED); + this.txtNom.setBackground(Color.RED); + this.txtPrenom.setBackground(Color.RED); + this.txtMail.setBackground(Color.RED); + this.txtAge.setBackground(Color.RED); + this.txtGalop.setBackground(Color.RED); + this.txtMdp.setBackground(Color.RED); + } + + } + + } + class StateListener implements ActionListener{ + public void actionPerformed(ActionEvent e) { + + + } + } + +} + diff --git a/Les Ecuries de la Boissière/src/vue/VueFormateursS.java b/Les Ecuries de la Boissière/src/vue/VueFormateursS.java new file mode 100644 index 0000000..6019937 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueFormateursS.java @@ -0,0 +1,97 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import controleur.Formateur; +import modele.ModeleFormateur; + +public class VueFormateursS extends JPanel implements ActionListener +{ + private JTextField txtLogin = new JTextField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btAjouter = new JButton("Supprimer"); + + public VueFormateursS() + { + JLabel lbVide1 = new JLabel("");JLabel lbVide2 = new JLabel("");JLabel lbVide3 = new JLabel("");JLabel lbVide4 = new JLabel("");JLabel lbVide5 = new JLabel(""); + + this.setBounds(150, 250, 700, 150); + this.setLayout(new GridLayout(5,2)); + this.setBackground(new Color(230,230,230)); + + /*TITRE*/ + JLabel titre = new JLabel("Supprimer un formateur"); + titre.setFont(new Font(titre.getText(), Font.ROMAN_BASELINE + Font.BOLD, 25)); + this.add(titre); + + this.add(lbVide1); this.add(lbVide2); this.add(lbVide3); + + JLabel lbLogin = new JLabel(" Login :"); + lbLogin.setFont(new Font(lbLogin.getText(), Font.CENTER_BASELINE, 18)); + this.add(lbLogin); this.add(this.txtLogin); + + this.add(lbVide4); this.add(lbVide5); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + + this.add(this.btAjouter); + this.btAjouter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAjouter.addActionListener(this); + } + + + @Override + public void actionPerformed(ActionEvent e) { + // TODO Stub de la méthode généré automatiquement + if(e.getSource()==this.btAnnuler) + { + + this.txtLogin.setBackground(Color.WHITE); + this.txtLogin.setText(null); + + } + + else if (e.getSource()==this.btAjouter) + { + + String login = this.txtLogin.getText(); + + try{ + + Formateur unFormateur = new Formateur(login); + ModeleFormateur.delete(login); + JOptionPane.showMessageDialog(this, "Suppression reussie de " + unFormateur.getLogin()); + this.txtLogin.setText(null); + + + this.setVisible(true); // fin d'enregistrement +} + catch (NumberFormatException exp) + { + + JOptionPane.showMessageDialog(this,"Erreur dans la saisie"); + this.txtLogin.setBackground(Color.RED); + + } + + } + + } + + +} \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueInformation.java b/Les Ecuries de la Boissière/src/vue/VueInformation.java new file mode 100644 index 0000000..8fa09a6 --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueInformation.java @@ -0,0 +1,75 @@ +package vue; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JLabel; +import javax.swing.JPanel; + + public class VueInformation extends JPanel + { + public VueInformation() + { + /* FENETRE */ + this.setBounds(50, 130, 900, 500); + this.setBackground(new Color(230,230,230)); + this.setLayout(null); + /* TITRE */ + JLabel lbAbout = new JLabel("A propos"); + lbAbout.setFont(new Font(lbAbout.getText(),Font.ROMAN_BASELINE + Font.BOLD, 25)); + lbAbout.setBounds(390, 20, 250, 40); + this.add(lbAbout); + /*TEXT LIEN*/ + JLabel txtPC = new JLabel("Politique de confidentialité"); + txtPC.setFont(new Font(txtPC.getText(),Font.ROMAN_BASELINE, 20)); + txtPC.setBounds(335, 210, 250, 30); + this.add(txtPC); + /*LIEN*/ + JLabel txtlink = new JLabel("http://localhost:82/les_ecuries_de_la_boissiere/privacy_policy.php"); + txtlink.setFont(new Font(txtlink.getText(),Font.ROMAN_BASELINE, 25)); + txtlink.setForeground(new Color(230,230,230)); + txtlink.setBounds(335, 210, 230, 30); + this.add(txtlink); + + /* Clickable */ + txtPC.setFocusable(false); + txtPC.setCursor(new Cursor(Cursor.HAND_CURSOR)); + txtlink.setFocusable(false); + txtlink.setCursor(new Cursor(Cursor.HAND_CURSOR)); + addListener(txtlink); + /*DESCRIPTION*/ + JLabel txtLabel = new JLabel(); + txtLabel.setText("Les Ecuries de la Boissičre © 2017 Tous droits réservés"); + txtLabel.setFont(new Font(txtLabel.getText(),Font.ROMAN_BASELINE + Font.BOLD, 20)); + txtLabel.setBounds(180, 450, 550, 30); + this.add(txtLabel); + } + /* EVENT CLICKABLE */ + private void addListener(JLabel txtlink) { + txtlink.addMouseListener(new MouseAdapter() { + //Click sur le lien + public void mouseClicked(MouseEvent e) { + JLabel label=(JLabel)e.getSource(); + String plainText = label.getText().replaceAll("/<.*?/>", ""); + try { + Desktop.getDesktop().browse(new URI(plainText)); + } catch (URISyntaxException ex) { + Logger.getLogger(VueInformation.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + Logger.getLogger(VueInformation.class.getName()).log(Level.SEVERE, null, ex); + } + } + + }); + this.setVisible(true); + } + } + \ No newline at end of file diff --git a/Les Ecuries de la Boissière/src/vue/VueProfil.java b/Les Ecuries de la Boissière/src/vue/VueProfil.java new file mode 100644 index 0000000..af4e50a --- /dev/null +++ b/Les Ecuries de la Boissière/src/vue/VueProfil.java @@ -0,0 +1,45 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +import controleur.Profil; + +public class VueProfil extends JPanel { + public VueProfil(Profil unProfil) + { + this.setBounds(50, 130, 900, 500); + this.setBackground(new Color(230,230,230)); + this.setLayout(null); + + JLabel lbProfil = new JLabel("Votre Profil"); + lbProfil.setBounds(90, 10, 150, 40); + lbProfil.setFont(new Font(lbProfil.getText(), Font.PLAIN + Font.BOLD, 18)); + this.add(lbProfil); + + JTextArea txtTitre = new JTextArea(); + txtTitre.setBounds(20, 50, 280, 200); + //txtTitre.setBounds(x, y, width, height); + txtTitre.setEditable(false); + txtTitre.setBackground(Color.pink); + txtTitre.setFont(new Font(txtTitre.getText(), Font.PLAIN, 15)); + // txtTitre.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + + txtTitre.setText("\n ID : " + unProfil.getIdformateur() + +"\n Nom : " + unProfil.getNom() + + "\n Prénom : " + unProfil.getPrenom() + + "\n Mail : " + unProfil.getEmail() + + "\n Sexe : " + unProfil.getSexe() + + "\n Age : " + unProfil.getAge() + + "\n Galop : " + unProfil.getGalop() + + "\n privilege : " + unProfil.getPrivilege() + + "\n login : " + unProfil.getLogin() + + "\n Mot de passe : " + unProfil.getMdp()); + this.add(txtTitre); + + this.setVisible(true); + } +} \ No newline at end of file diff --git a/PJCalc/.classpath b/PJCalc/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/PJCalc/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/PJCalc/.project b/PJCalc/.project new file mode 100644 index 0000000..91a5225 --- /dev/null +++ b/PJCalc/.project @@ -0,0 +1,17 @@ + + + PJCalc + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/PJCalc/.settings/org.eclipse.jdt.core.prefs b/PJCalc/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/PJCalc/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/PJCalc/bin/Calculatrice$ChiffreListener.class b/PJCalc/bin/Calculatrice$ChiffreListener.class new file mode 100644 index 0000000..90aca5e Binary files /dev/null and b/PJCalc/bin/Calculatrice$ChiffreListener.class differ diff --git a/PJCalc/bin/Calculatrice$DivListener.class b/PJCalc/bin/Calculatrice$DivListener.class new file mode 100644 index 0000000..c3dc144 Binary files /dev/null and b/PJCalc/bin/Calculatrice$DivListener.class differ diff --git a/PJCalc/bin/Calculatrice$EgalListener.class b/PJCalc/bin/Calculatrice$EgalListener.class new file mode 100644 index 0000000..1ec7fdb Binary files /dev/null and b/PJCalc/bin/Calculatrice$EgalListener.class differ diff --git a/PJCalc/bin/Calculatrice$MoinsListener.class b/PJCalc/bin/Calculatrice$MoinsListener.class new file mode 100644 index 0000000..53c2dae Binary files /dev/null and b/PJCalc/bin/Calculatrice$MoinsListener.class differ diff --git a/PJCalc/bin/Calculatrice$MultiListener.class b/PJCalc/bin/Calculatrice$MultiListener.class new file mode 100644 index 0000000..3f58b1d Binary files /dev/null and b/PJCalc/bin/Calculatrice$MultiListener.class differ diff --git a/PJCalc/bin/Calculatrice$PlusListener.class b/PJCalc/bin/Calculatrice$PlusListener.class new file mode 100644 index 0000000..a3b6c0e Binary files /dev/null and b/PJCalc/bin/Calculatrice$PlusListener.class differ diff --git a/PJCalc/bin/Calculatrice$ResetListener.class b/PJCalc/bin/Calculatrice$ResetListener.class new file mode 100644 index 0000000..6c303ef Binary files /dev/null and b/PJCalc/bin/Calculatrice$ResetListener.class differ diff --git a/PJCalc/bin/Calculatrice.class b/PJCalc/bin/Calculatrice.class new file mode 100644 index 0000000..7010abc Binary files /dev/null and b/PJCalc/bin/Calculatrice.class differ diff --git a/PJCalc/bin/Main.class b/PJCalc/bin/Main.class new file mode 100644 index 0000000..0fb942b Binary files /dev/null and b/PJCalc/bin/Main.class differ diff --git a/PJCalc/src/Calculatrice.java b/PJCalc/src/Calculatrice.java new file mode 100644 index 0000000..e586af0 --- /dev/null +++ b/PJCalc/src/Calculatrice.java @@ -0,0 +1,244 @@ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + + +public class Calculatrice extends JFrame { + private JPanel container = new JPanel(); + //Tableau stockant les éléments ŕ afficher dans la calculatrice + String[] tab_string = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "=", "C", "+", "-", "*", "/"}; + //Un bouton par élément ŕ afficher + JButton[] button = new JButton[tab_string.length]; + private JLabel ecran = new JLabel(); + private Dimension dim = new Dimension(50, 30); + public double getValeur; + private boolean clicOperateur = false, update = false; + private String operateur = ""; + + public Calculatrice(){ + this.setSize(280, 300); + this.setTitle("Calculette"); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLocationRelativeTo(null); + this.setResizable(false); + //On initialise le conteneur avec tous les composants + initComposant(); + //On ajoute le conteneur + this.setContentPane(container); + this.setVisible(true); + } + + private void initComposant(){ + //On définit la police d'écriture ŕ utiliser + Font police = new Font("Bell MT", Font.BOLD, 50); + ecran = new JLabel("0"); + ecran.setFont(police); + //On aligne les informations ŕ droite dans le JLabel + ecran.setHorizontalAlignment(JLabel.RIGHT); + ecran.setPreferredSize(new Dimension(220, 40)); + ecran.setForeground(Color.blue); + JPanel operateur = new JPanel(); + operateur.setPreferredSize(new Dimension(75, 255)); + JPanel chiffre = new JPanel(); + chiffre.setPreferredSize(new Dimension(165, 255)); + JPanel panEcran = new JPanel(); + panEcran.setPreferredSize(new Dimension(250, 50)); + + //On parcourt le tableau initialisé + //afin de créer nos boutons + for(int i = 0; i < tab_string.length; i++){ + button[i] = new JButton(tab_string[i]); + button[i].setBackground(Color.lightGray); + button[i].setPreferredSize(dim); + + switch(i){ + //Pour chaque élément situé ŕ la fin du tableau + //et qui n'est pas un chiffre + //on définit le comportement ŕ avoir grâce ŕ un listener + case 11 : + button[i].addActionListener(new EgalListener()); + button[i].setBackground(Color.green); + chiffre.add(button[i]); + break; + case 12 : + button[i].setForeground(Color.blue); + button[i].addActionListener(new ResetListener()); + button[i].setBackground(Color.gray); + operateur.add(button[i]); + break; + case 13 : + button[i].addActionListener(new PlusListener()); + button[i].setPreferredSize(dim); + button[i].setBackground(Color.red); + operateur.add(button[i]); + break; + case 14 : + button[i].addActionListener(new MoinsListener()); + button[i].setPreferredSize(dim); + button[i].setBackground(Color.red); + operateur.add(button[i]); + break; + case 15 : + button[i].addActionListener(new MultiListener()); + button[i].setPreferredSize(dim); + button[i].setBackground(Color.red); + operateur.add(button[i]); + break; + case 16 : + button[i].addActionListener(new DivListener()); + button[i].setPreferredSize(dim); + button[i].setBackground(Color.red); + operateur.add(button[i]); + break; + default : + //Par défaut, ce sont les premiers éléments du tableau + //donc des chiffres, on affecte alors le bon listener + chiffre.add(button[i]); + button[i].addActionListener(new ChiffreListener()); + break; + } + } + panEcran.add(ecran); + panEcran.setBorder(BorderFactory.createLineBorder(Color.MAGENTA)); + container.add(panEcran, BorderLayout.NORTH); + container.add(chiffre, BorderLayout.CENTER); + container.add(operateur, BorderLayout.EAST); + } + + //Méthode permettant d'effectuer un calcul selon l'opérateur sélectionné + private void calcul(){ + if(operateur.equals("+")){ + getValeur = getValeur + + Double.valueOf(ecran.getText()).doubleValue(); + ecran.setText(String.valueOf(getValeur)); + } + if(operateur.equals("-")){ + getValeur = getValeur - + Double.valueOf(ecran.getText()).doubleValue(); + ecran.setText(String.valueOf(getValeur)); + } + if(operateur.equals("*")){ + getValeur = getValeur * + Double.valueOf(ecran.getText()).doubleValue(); + ecran.setText(String.valueOf(getValeur)); + } + if(operateur.equals("/")){ + try{ + getValeur = getValeur / + Double.valueOf(ecran.getText()).doubleValue(); + ecran.setText(String.valueOf(getValeur)); + } catch(ArithmeticException e) { + ecran.setText("0"); + } + } + } + + //Listener utilisé pour les chiffres + //Permet de stocker les chiffres et de les afficher + class ChiffreListener implements ActionListener { + public void actionPerformed(ActionEvent e){ + //On affiche le chiffre additionnel dans le label + String str = ((JButton)e.getSource()).getText(); + if(update){ + update = false; + } + else{ + if(!ecran.getText().equals("0")) + str = ecran.getText() + str; + } + ecran.setText(str); + } + } + + //Listener affecté au bouton = + class EgalListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + calcul(); + update = true; + clicOperateur = false; + } + } + + //Listener affecté au bouton + + class PlusListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + if(clicOperateur){ + calcul(); + ecran.setText(String.valueOf(getValeur)); + } + else{ + getValeur = Double.valueOf(ecran.getText()).doubleValue(); + clicOperateur = true; + } + operateur = "+"; + update = true; + } + } + + //Listener affecté au bouton - + class MoinsListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + if(clicOperateur){ + calcul(); + ecran.setText(String.valueOf(getValeur)); + } + else{ + getValeur = Double.valueOf(ecran.getText()).doubleValue(); + clicOperateur = true; + } + operateur = "-"; + update = true; + } + } + + //Listener affecté au bouton * + class MultiListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + if(clicOperateur){ + calcul(); + ecran.setText(String.valueOf(getValeur)); + } + else{ + getValeur = Double.valueOf(ecran.getText()).doubleValue(); + clicOperateur = true; + } + operateur = "*"; + update = true; + } + } + + //Listener affecté au bouton / + class DivListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + if(clicOperateur){ + calcul(); + ecran.setText(String.valueOf(getValeur)); + } + else{ + getValeur = Double.valueOf(ecran.getText()).doubleValue(); + clicOperateur = true; + } + operateur = "/"; + update = true; + } + } + + //Listener affecté au bouton de remise ŕ zéro + class ResetListener implements ActionListener { + public void actionPerformed(ActionEvent arg0){ + clicOperateur = false; + update = true; + getValeur = 0; + operateur = ""; + ecran.setText(""); + } + } +} \ No newline at end of file diff --git a/PJCalc/src/Main.java b/PJCalc/src/Main.java new file mode 100644 index 0000000..54f1ae8 --- /dev/null +++ b/PJCalc/src/Main.java @@ -0,0 +1,6 @@ + +public class Main { + public static void main(String[] args) { + Calculatrice calculette = new Calculatrice(); + } +} \ No newline at end of file diff --git a/PPE2/.classpath b/PPE2/.classpath new file mode 100644 index 0000000..4fe04fe --- /dev/null +++ b/PPE2/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/PPE2/.project b/PPE2/.project new file mode 100644 index 0000000..0d81f7a --- /dev/null +++ b/PPE2/.project @@ -0,0 +1,17 @@ + + + PPE2 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/PPE2/.settings/org.eclipse.jdt.core.prefs b/PPE2/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..a698e59 --- /dev/null +++ b/PPE2/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/PPE2/bin/controleur/Client.class b/PPE2/bin/controleur/Client.class new file mode 100644 index 0000000..feee0cc Binary files /dev/null and b/PPE2/bin/controleur/Client.class differ diff --git a/PPE2/bin/controleur/Gestion.class b/PPE2/bin/controleur/Gestion.class new file mode 100644 index 0000000..1040e0e Binary files /dev/null and b/PPE2/bin/controleur/Gestion.class differ diff --git a/PPE2/bin/controleur/Profil.class b/PPE2/bin/controleur/Profil.class new file mode 100644 index 0000000..54138cf Binary files /dev/null and b/PPE2/bin/controleur/Profil.class differ diff --git a/PPE2/bin/images/choix1.png b/PPE2/bin/images/choix1.png new file mode 100644 index 0000000..5264cda Binary files /dev/null and b/PPE2/bin/images/choix1.png differ diff --git a/PPE2/bin/images/choix2.png b/PPE2/bin/images/choix2.png new file mode 100644 index 0000000..8b44952 Binary files /dev/null and b/PPE2/bin/images/choix2.png differ diff --git a/PPE2/bin/images/choosemyday_logo.png b/PPE2/bin/images/choosemyday_logo.png new file mode 100644 index 0000000..b886521 Binary files /dev/null and b/PPE2/bin/images/choosemyday_logo.png differ diff --git a/PPE2/bin/modele/BDD.class b/PPE2/bin/modele/BDD.class new file mode 100644 index 0000000..c911932 Binary files /dev/null and b/PPE2/bin/modele/BDD.class differ diff --git a/PPE2/bin/modele/Modele.class b/PPE2/bin/modele/Modele.class new file mode 100644 index 0000000..a7561b7 Binary files /dev/null and b/PPE2/bin/modele/Modele.class differ diff --git a/PPE2/bin/modele/ModeleClient.class b/PPE2/bin/modele/ModeleClient.class new file mode 100644 index 0000000..d954ea4 Binary files /dev/null and b/PPE2/bin/modele/ModeleClient.class differ diff --git a/PPE2/bin/vue/Connexion.class b/PPE2/bin/vue/Connexion.class new file mode 100644 index 0000000..1e0844d Binary files /dev/null and b/PPE2/bin/vue/Connexion.class differ diff --git a/PPE2/bin/vue/Generale.class b/PPE2/bin/vue/Generale.class new file mode 100644 index 0000000..87f7661 Binary files /dev/null and b/PPE2/bin/vue/Generale.class differ diff --git a/PPE2/bin/vue/VueAccueil.class b/PPE2/bin/vue/VueAccueil.class new file mode 100644 index 0000000..e790104 Binary files /dev/null and b/PPE2/bin/vue/VueAccueil.class differ diff --git a/PPE2/bin/vue/VueClients.class b/PPE2/bin/vue/VueClients.class new file mode 100644 index 0000000..2f537a6 Binary files /dev/null and b/PPE2/bin/vue/VueClients.class differ diff --git a/PPE2/bin/vue/VueConnexion.class b/PPE2/bin/vue/VueConnexion.class new file mode 100644 index 0000000..0b2ddac Binary files /dev/null and b/PPE2/bin/vue/VueConnexion.class differ diff --git a/PPE2/ecole_trigger.sql b/PPE2/ecole_trigger.sql new file mode 100644 index 0000000..d8c20eb --- /dev/null +++ b/PPE2/ecole_trigger.sql @@ -0,0 +1,191 @@ +CrĂ©er la base donnĂ©es Ecole : + +CrĂ©er les tables suivantes : + + Classe (idclasse, libelle, salle, nbEleves) + Eleve (ideleve, civilite, nom, prenom, idclasse#) + + +Create database Ecole; +Use Ecole; +Create table Classe( + idclasse int (3) NOT NULL AUTO_INCREMENT, + libelle varchar (10), + salle varchar (15), + nbEleves int(3), + primary key(idclasse) +); + +Create table Eleve( + ideleve int (3) NOT NULL AUTO_INCREMENT, + civilite varchar(30), + nom varchar(30), + prenom varchar(30), + idclasse int(3) NOT NULL, + primary key (ideleve), + foreign key (idclasse) references Classe (idclasse) +); + +insert into Classe values + (null, "SIO SLAM 2", "salle 2", 0), + (null, "SIO SISR 2", "salle 3", 1); + +insert into Eleve values + (null, "Monsieur", "Mozar", "Kevin", 2); +insert into Eleve values + (null, "Madame", "Marie", "Ellen", 1); + +Afficher les triggers + show triggers ; + +Rappel : 6 triggers + Action : avant before, apres after + Requete : update, delete, insert + Ancien enregistrement: old + Nouveau enregistrement: new + Old: quand il y a delete, update + New: quand il y a insert, update +CrĂ©er un trigger qui permet d incrementer le nb eleves de la classe +Ă  chaque insertion d un eleve + +delimiter | +create trigger After_insert_Eleve +After insert on Eleve +For each row +Begin + Update classe + Set nbEleves = nbEleves + 1 + where idclasse = new.idclasse; +End | +delimiter ; + +insert into Eleve values + (null, "Madame", "Marie", "Ellen", 1); + +Supprimer un trigger +Drop trigger After_insert_Eleve ; + +CrĂ©er un trigger qui permet de dĂ©crĂ©menter le nombre d eleves +Ă  chaque suppression d un eleve de la classe. + +delimiter | +create trigger Alter_delete_Eleve +After delete on Eleve +For each row +Begin + update classe + Set nbEleves = nbEleves -1 + where idclasse = old.idclasse; + +end | +delimiter ; + + +delete from Eleve where ideleve=1; +-- cree un trigger pour changer un eleve d'une classe Ă  une autre + +delimiter | +create trigger After_Update_Eleve +After update on Eleve +For each row +Begin + update classe + Set nbEleves = nbEleves -1 + where idclasse = old.idclasse; + update classe + Set nbEleves = nbEleves +1 + where idclasse = new.idclasse; +End | +delimiter ; + +update eleve set idclasse=2 where ideleve = 2; + +ecrire un trigger qui permet a chaque suppresion dun eleve de stocker +dans une table a creer ancieneleve avec les memes champs aux quels on +ajoute la date de la suppresion. + +create table ancienEleve( + idEleve int(3) NOT NULL AUTO_INCREMENT, + civilite varchar(30), + nom varchar(30), + prenom varchar(30), + dateDepart date, + PRIMARY KEY (idEleve) + ); + +delimiter | +create trigger After_Delete_Eleve +After delete on Eleve +For each row +Begin + insert into ancienEleve values( + null, + old.civilite, + old.nom, + old.prenom, + now() + ); +end | +delimiter ; + +Trigger qui controle les donnees avant insertion. + +Syntaxe : +Alternative + if condition then + instruction SQL + end ; + if condition then + instruction SQL + else + instruction SQL + end; + +Affectation + Set champ = valeur ; +Declaration + Declare variable type ; + Declare variable type default valeur; + + Exo : ecrire un trigger qui verifie avant insertion que si la civilite + n est pas Mr ou Mme, par defaut elle sera egal Ă  Mr. + + +delimiter | +create trigger Before_Insert_Eleve +Before insert on Eleve +For each row + +Begin + if (new.civilite <> 'Mr' and new.civilite <> 'Mme') then + set new.civilite = 'Mr'; + end if; +end | + +delimiter ; + +ajouter le champ age dans la table eleve. +Mettez ce champ a zero pour tous les eleves +modifier le trigger qui verifie que si Ă  l insertion le champ age est negatif +on insere la valeur 0. + +ALTER TABLE Eleve ADD age int(3); + +update eleve set age = 0; + +delimiter | +create trigger Before_Insert_Eleve +Before insert on Eleve +For each row + +Begin + if (new.civilite <> 'Mr' and new.civilite <> 'Mme') then + set new.civilite = 'Mr'; + end if; + if (new.age < 0) then + set new.age = 0; + end if; +end | + +delimiter ; + diff --git a/PPE2/ppe2.sql b/PPE2/ppe2.sql new file mode 100644 index 0000000..8b3d37d --- /dev/null +++ b/PPE2/ppe2.sql @@ -0,0 +1,34 @@ +drop database if exists PPE2; +create database PPE2; +use PPE2; + +create table Profil ( + idprofil int (3) not null auto_increment, + nom varchar (50), + prenom varchar (50), + adresse varchar (100), + mail varchar (100), + mdp varchar (50), + droits varchar (50), + primary key (idprofil) +); + +insert into Profil values +(null, "admin Nom", "admin Prenom", "rue de Paris", "admin@gmail.com", "123", "tout"), +(null, "user Nom", "user Prenom", "rue de Lyon", "user@gmail.com", "123", "0-0-1"); + +create table client ( + + idclient int(5) not null auto_increment, + nom varchar(50), + prenom varchar(50), + email varchar(50), + adresse varchar(50), + primary key (idclient) +); + +insert into client values ("","ben", "oka","oka@gmail.com","rue de paris, limoges"), +("","Edwina", "serine","ed@gmail.com","rue de lyon, paris"), +("","kamar", "elie","kamar@gmail.com","rue de lille, Lyon"); + + diff --git a/PPE2/src/controleur/Client.java b/PPE2/src/controleur/Client.java new file mode 100644 index 0000000..45d8cae --- /dev/null +++ b/PPE2/src/controleur/Client.java @@ -0,0 +1,60 @@ +package controleur; + +public class Client +{ + private int idclient; + private String nom, prenom, email, adresse; + + public Client() + { + this.idclient =0; + this.nom = this.prenom=this.adresse=this.email=""; + } + public Client (int idClient, String nom, String prenom, String email, String adresse) + { + this.idclient= idClient; + this.nom = nom; + this.prenom = prenom; + this.email = email; + this.adresse = adresse; + } + public Client ( String nom, String prenom, String email, String adresse) + { + this.idclient= 0; + this.nom = nom; + this.prenom = prenom; + this.email = email; + this.adresse = adresse; + } + public int getIdclient() { + return idclient; + } + public void setIdclient(int idclient) { + this.idclient = idclient; + } + public String getNom() { + return nom; + } + public void setNom(String nom) { + this.nom = nom; + } + public String getPrenom() { + return prenom; + } + public void setPrenom(String prenom) { + this.prenom = prenom; + } + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + public String getAdresse() { + return adresse; + } + public void setAdresse(String adresse) { + this.adresse = adresse; + } + +} diff --git a/PPE2/src/controleur/Gestion.java b/PPE2/src/controleur/Gestion.java new file mode 100644 index 0000000..3664e87 --- /dev/null +++ b/PPE2/src/controleur/Gestion.java @@ -0,0 +1,25 @@ +package controleur; + +import vue.Connexion; + +public class Gestion +{ + private static Connexion uneConnexion; + + public Gestion() + { + uneConnexion = new Connexion(); + uneConnexion.rendreVisible(true); + } + + public static void rendreVisible(boolean val) + { + uneConnexion.rendreVisible(val); + } + + public static void main(String[] args) + { + new Gestion(); + } + +} diff --git a/PPE2/src/controleur/Profil.java b/PPE2/src/controleur/Profil.java new file mode 100644 index 0000000..6d8cb5a --- /dev/null +++ b/PPE2/src/controleur/Profil.java @@ -0,0 +1,66 @@ +package controleur; + +public class Profil +{ + private String nom, prenom, adresse, mail, mdp, droits; + + public Profil(String nom, String prenom, String adresse, String mail, String mdp, String droits) + { + this.nom = nom; + this.prenom = prenom; + this.adresse = adresse; + this.mail = mail; + this.mdp = mdp; + this.droits = droits; + } + + public String getNom() { + return nom; + } + + public void setNom(String nom) { + this.nom = nom; + } + + public String getPrenom() { + return prenom; + } + + public void setPrenom(String prenom) { + this.prenom = prenom; + } + + public String getAdresse() { + return adresse; + } + + public void setAdresse(String adresse) { + this.adresse = adresse; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getMdp() { + return mdp; + } + + public void setMdp(String mdp) { + this.mdp = mdp; + } + + public String getDroits() { + return droits; + } + + public void setDroits(String droits) { + this.droits = droits; + } + + +} diff --git a/PPE2/src/images/choix1.png b/PPE2/src/images/choix1.png new file mode 100644 index 0000000..5264cda Binary files /dev/null and b/PPE2/src/images/choix1.png differ diff --git a/PPE2/src/images/choix2.png b/PPE2/src/images/choix2.png new file mode 100644 index 0000000..8b44952 Binary files /dev/null and b/PPE2/src/images/choix2.png differ diff --git a/PPE2/src/images/choosemyday_logo.png b/PPE2/src/images/choosemyday_logo.png new file mode 100644 index 0000000..b886521 Binary files /dev/null and b/PPE2/src/images/choosemyday_logo.png differ diff --git a/PPE2/src/modele/BDD.java b/PPE2/src/modele/BDD.java new file mode 100644 index 0000000..46415c0 --- /dev/null +++ b/PPE2/src/modele/BDD.java @@ -0,0 +1,60 @@ +package modele; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +public class BDD +{ + private String serveur, nombdd, user, mdp; + private Connection maConnexion; + + public BDD(String serveur, String nombdd, String user, String mdp) + { + this.serveur = serveur; + this.nombdd = nombdd; + this.user = user; + this.mdp = mdp; + this.maConnexion = null; + } + + public void chargerPilote() + { + // vĂ©rifie la prĂ©sence du pilote JDBC MySQL + try { + Class.forName("com.mysql.jdbc.Driver"); + } + catch(ClassNotFoundException exp) { + System.out.println("Abscence du pilote JDBC !"); + } + } + + public void seConnecter() + { + // connexion au serveur de la BDD + this.chargerPilote(); + String url = "jdbc:mysql://" + this.serveur + "/" + this.nombdd; + + try { + this.maConnexion = DriverManager.getConnection(url, this.user, this.mdp); + } + catch(SQLException exp) { + System.out.println("Impossible de se connecter Ă  " + url); + } + } + + public void seDeconnecter() + { + // dĂ©connexion au serveur de la BDD + try { + if(this.maConnexion != null) this.maConnexion.close(); + } + catch(SQLException exp) { + System.out.println("La dĂ©connexion a Ă©chouĂ© !"); + } + } + + public Connection getMaConnexion() + { + return this.maConnexion; + } +} diff --git a/PPE2/src/modele/Modele.java b/PPE2/src/modele/Modele.java new file mode 100644 index 0000000..a83be2e --- /dev/null +++ b/PPE2/src/modele/Modele.java @@ -0,0 +1,124 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import controleur.Profil; + +public class Modele +{ + public static ArrayList selectAll() + { + ArrayList lesProfils = new ArrayList(); + String requete = "select * from Profil;"; + + try { + BDD uneBDD = new BDD("localhost", "ppe2", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adresse = unRes.getString("adresse"); + String mail = unRes.getString("mail"); + String mdp = unRes.getString("mdp"); + String droits = unRes.getString("droits"); + + Profil unProfil = new Profil(nom, prenom, adresse, mail, mdp, droits); + lesProfils.add(unProfil); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesProfils; + } + + public static void insert(Profil unProfil) + { + String requete = "insert into Profil (nom, prenom, adresse, mail, mdp, droits) values ('" + + unProfil.getNom() + "', '" + + unProfil.getPrenom() + "', '" + + unProfil.getAdresse() + "', '" + + unProfil.getMail() + "', '" + + unProfil.getMdp() + "', '" + + unProfil.getDroits() + "');"; + try { + BDD uneBDD = new BDD("localhost", "ppe2", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + + public static void delete(String mail) + { + String requete = "delete from Profil where mail='" + mail + "';"; + + try { + BDD uneBDD = new BDD("localhost", "ppe2", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + } + + public static Profil selectWhere(String mail, String mdp) + { + String requete = "select * from Profil where mail='" + mail + "' and mdp='" + mdp + "';"; + + Profil unProfil = null; + try { + BDD uneBDD = new BDD("localhost", "ppe2", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + if(unRes.next()) + { + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adresse = unRes.getString("adresse"); + String droits = unRes.getString("droits"); + + unProfil = new Profil(nom, prenom, adresse, mail, mdp, droits); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return unProfil; + } +} diff --git a/PPE2/src/modele/ModeleClient.java b/PPE2/src/modele/ModeleClient.java new file mode 100644 index 0000000..871bb67 --- /dev/null +++ b/PPE2/src/modele/ModeleClient.java @@ -0,0 +1,47 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Client; + +public class ModeleClient +{ + public static ArrayList selectAll() + { + ArrayList lesClients = new ArrayList(); + String requete = "select * from Client;"; + + try { + BDD uneBDD = new BDD("localhost", "ppe2", "root", ""); + uneBDD.seConnecter(); + + Statement unStat = uneBDD.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + + while(unRes.next()) + { + String nom = unRes.getString("nom"); + String prenom = unRes.getString("prenom"); + String adresse = unRes.getString("adresse"); + String email = unRes.getString("email"); + int idclient = unRes.getInt("idclient"); + + Client unClient = new Client(idclient, nom, prenom, email, adresse); + lesClients.add(unClient); + } + + unStat.close(); + unRes.close(); + uneBDD.seDeconnecter(); + } + catch(SQLException exp) + { + System.out.println("Erreur d'execution de la requete " + requete); + } + + return lesClients; + } +} diff --git a/PPE2/src/vue/Connexion.java b/PPE2/src/vue/Connexion.java new file mode 100644 index 0000000..c96746b --- /dev/null +++ b/PPE2/src/vue/Connexion.java @@ -0,0 +1,45 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class Connexion extends JFrame +{ + public Connexion() + { + ImageIcon logo = new ImageIcon(new ImageIcon("src/images/choosemyday_logo.png").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); + + this.setIconImage(logo.getImage()); + this.setTitle("ChooseMyDay"); + this.setBounds(200, 200, 430, 450); + this.getContentPane().setBackground(new Color(254, 231, 240)); + + this.setLayout(null); // pas de grille + this.setResizable(false); // la fenĂŞtre ne pourra pas ĂŞtre redimensionnĂ©e + + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + + JLabel lbLogo = new JLabel(logo); + lbLogo.setBounds(150, 0, 150, 150); + this.add(lbLogo); + + JLabel lbTitre = new JLabel("Bienvenue"); + lbTitre.setBounds(170, 150, 110, 20); + lbTitre.setFont(new Font(lbTitre.getText(), Font.PLAIN, 20)); + this.add(lbTitre); + + this.add(new VueConnexion()); + + this.setVisible(true); + } + + public void rendreVisible(boolean val) + { + this.setVisible(val); + } +} diff --git a/PPE2/src/vue/Generale.java b/PPE2/src/vue/Generale.java new file mode 100644 index 0000000..7cb7d2a --- /dev/null +++ b/PPE2/src/vue/Generale.java @@ -0,0 +1,98 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +import controleur.Gestion; +import controleur.Profil; + +public class Generale extends JFrame implements ActionListener +{ + private JMenuBar uneBarre = new JMenuBar(); + private JMenu mnFichier = new JMenu("Fichier"); + private JMenu mnGestion = new JMenu("Gestion"); + private JMenu mnAide = new JMenu("Aide"); + + //private JMenuItem itmProfil = new JMenuItem("Profil"); + private JMenuItem itmModifier = new JMenuItem("Modifier"); + private JMenuItem itmQuitter = new JMenuItem("Quitter"); + + private JMenuItem itemLister = new JMenuItem("Lister Clients"); + + + private VueClients uneVueClient = new VueClients(); + private VueAccueil uneVueAccueil ; + + public Generale(Profil unProfil) + { + ImageIcon logo = new ImageIcon(new ImageIcon("src/images/choosemyday_logo.png").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); + + this.setIconImage(logo.getImage()); + this.setTitle("Accueil"); + this.setBounds(200, 200, 430, 450); + this.getContentPane().setBackground(new Color(254, 231, 240)); + + this.setLayout(null); + this.setResizable(false); + + this.uneVueAccueil = new VueAccueil(unProfil); + this.add(uneVueAccueil); + + this.add(uneVueClient); + + + this.uneBarre.add(this.mnFichier); + this.uneBarre.add(this.mnGestion); + this.uneBarre.add(this.mnAide); + + this.mnFichier.add(this.itmQuitter); + this.mnGestion.add(this.itmModifier); + + this.mnGestion.add(this.itemLister); + + this.itemLister.addActionListener(this); + + this.itmQuitter.addActionListener(this); + this.itmModifier.addActionListener(this); + //this.itmProfil.addActionListener(this); + + this.setJMenuBar(this.uneBarre); + + JLabel lbTitre = new JLabel("Accueil"); + lbTitre.setBounds(170, 10, 110, 20); + lbTitre.setFont(new Font(lbTitre.getText(), Font.PLAIN, 20)); + this.add(lbTitre); + + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.itmQuitter) + { + Gestion.rendreVisible(true); + + this.setVisible(false); + } + else if (e.getSource()==this.itemLister) + { + //rendre visible panel Clients + uneVueAccueil.setVisible(false); + uneVueClient.setVisible(true); + + } + } + + +} diff --git a/PPE2/src/vue/VueAccueil.java b/PPE2/src/vue/VueAccueil.java new file mode 100644 index 0000000..6515972 --- /dev/null +++ b/PPE2/src/vue/VueAccueil.java @@ -0,0 +1,46 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.util.ArrayList; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import controleur.Profil; + +public class VueAccueil extends JPanel +{ + public VueAccueil(Profil unProfil) + { + this.setBounds(70, 50, 300, 250); + this.setBackground(Color.pink); + this.setLayout(null); + + JLabel lbProfil = new JLabel("Votre Profil"); + lbProfil.setBounds(90, 10, 150, 40); + lbProfil.setFont(new Font(lbProfil.getText(), Font.PLAIN + Font.BOLD, 18)); + this.add(lbProfil); + + JTextArea txtTitre = new JTextArea(); + txtTitre.setBounds(20, 50, 280, 200); + //txtTitre.setBounds(x, y, width, height); + txtTitre.setEditable(false); + txtTitre.setBackground(Color.pink); + txtTitre.setFont(new Font(txtTitre.getText(), Font.PLAIN, 15)); + // txtTitre.setAlignmentX(JTextArea.CENTER_ALIGNMENT); + + txtTitre.setText( "\n Nom : " + unProfil.getNom() + + "\n PrĂ©nom : " + unProfil.getPrenom() + + "\n Adresse : " + unProfil.getAdresse() + + "\n Droits : " + unProfil.getDroits()); + this.add(txtTitre); + + this.setVisible(true); + } + public static void main(String[] args) { + // TODO Stub de la méthode généré automatiquement + + } +} diff --git a/PPE2/src/vue/VueClients.java b/PPE2/src/vue/VueClients.java new file mode 100644 index 0000000..d908729 --- /dev/null +++ b/PPE2/src/vue/VueClients.java @@ -0,0 +1,64 @@ +package vue; + +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import controleur.Client; +import modele.ModeleClient; + +public class VueClients extends JPanel implements ActionListener +{ + private JLabel titre = new JLabel("Liste des clients "); + private JTable tableClients; + + public VueClients () + { + this.setBounds(50, 30, 350, 250); + this.setLayout(null); + this.setBackground(Color.CYAN); + this.titre.setBounds(100, 10, 100, 20); + this.add(this.titre); + + String titres [] = {"id Client", "Nom", "Prénom", "Email","Adresse"}; + + this.tableClients = new JTable(this.extraireClients(), titres); + + JScrollPane uneScroll = new JScrollPane(this.tableClients); + uneScroll.setBounds(10, 10, 340, 240); + this.add(uneScroll); + + this.setVisible(false); + } + + @Override + public void actionPerformed(ActionEvent e) { + + } + + //extraire les clients + + public Object [][] extraireClients () + { + ArrayList lesClients = ModeleClient.selectAll(); + Object [][] donnees = new Object [lesClients.size()][5]; + int i =0; + for (Client unClient : lesClients) + { + donnees[i][0] = unClient.getIdclient(); + donnees[i][1] = unClient.getNom(); + donnees[i][2] = unClient.getPrenom(); + donnees[i][3] = unClient.getEmail(); + donnees[i][4] = unClient.getAdresse(); + i++; + } + return donnees; + } + +} diff --git a/PPE2/src/vue/VueConnexion.java b/PPE2/src/vue/VueConnexion.java new file mode 100644 index 0000000..8d6a5a4 --- /dev/null +++ b/PPE2/src/vue/VueConnexion.java @@ -0,0 +1,87 @@ +package vue; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import controleur.Gestion; +import controleur.Profil; +import modele.Modele; + +public class VueConnexion extends JPanel implements ActionListener +{ + private JTextField txtMail = new JTextField(); + private JPasswordField txtMdp = new JPasswordField(); + private JButton btAnnuler = new JButton("Annuler"); + private JButton btSeConnecter = new JButton("Se connecter"); + + public VueConnexion() + { + this.setBounds(70, 200, 300, 150); + this.setLayout(new GridLayout(3, 2)); + this.setBackground(Color.pink); + + JLabel lbMail = new JLabel(" E-mail :"); + lbMail.setFont(new Font(lbMail.getText(), Font.PLAIN, 16)); + this.add(lbMail); + this.add(this.txtMail); + + JLabel lbMdp = new JLabel(" Mot de passe :"); + lbMdp.setFont(new Font(lbMdp.getText(), Font.PLAIN, 16)); + this.add(lbMdp); + this.add(this.txtMdp); + + this.add(this.btAnnuler); + this.btAnnuler.setIcon(new ImageIcon(new ImageIcon("src/images/choix1.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btAnnuler.addActionListener(this); + + this.add(this.btSeConnecter); + this.btSeConnecter.setIcon(new ImageIcon(new ImageIcon("src/images/choix2.png").getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT))); + this.btSeConnecter.addActionListener(this); + + this.setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.btAnnuler) + { + this.txtMail.setText(""); + this.txtMdp.setText(""); + } + else if(e.getSource() == this.btSeConnecter) + { + String mail = this.txtMail.getText(); + String mdp = new String(this.txtMdp.getPassword()); + + Profil unProfil = Modele.selectWhere(mail, mdp); + if(unProfil == null) + { + JOptionPane.showMessageDialog(this, "Veuillez vĂ©rifier vos identifiants !"); + } + else + { + JOptionPane.showMessageDialog(this, "Connexion rĂ©ussie\n" + + "Bienvenue M./Mme " + unProfil.getNom() + " " + unProfil.getPrenom()); + + // ouvrir le menu gĂ©nĂ©ral + new Generale(unProfil); + this.txtMail.setText(""); + this.txtMdp.setText(""); + Gestion.rendreVisible(false); + } + } + } +} \ No newline at end of file diff --git a/Stock/.classpath b/Stock/.classpath new file mode 100644 index 0000000..4fe04fe --- /dev/null +++ b/Stock/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Stock/.project b/Stock/.project new file mode 100644 index 0000000..61d00ac --- /dev/null +++ b/Stock/.project @@ -0,0 +1,17 @@ + + + Stock + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Stock/.settings/org.eclipse.jdt.core.prefs b/Stock/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Stock/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Stock/bin/controleur/Gestion.class b/Stock/bin/controleur/Gestion.class new file mode 100644 index 0000000..3d1e0c3 Binary files /dev/null and b/Stock/bin/controleur/Gestion.class differ diff --git a/Stock/bin/controleur/Produit.class b/Stock/bin/controleur/Produit.class new file mode 100644 index 0000000..c19d830 Binary files /dev/null and b/Stock/bin/controleur/Produit.class differ diff --git a/Stock/bin/images/enregistrer.png b/Stock/bin/images/enregistrer.png new file mode 100644 index 0000000..b18f9ab Binary files /dev/null and b/Stock/bin/images/enregistrer.png differ diff --git a/Stock/bin/modele/Bdd.class b/Stock/bin/modele/Bdd.class new file mode 100644 index 0000000..bd0f79e Binary files /dev/null and b/Stock/bin/modele/Bdd.class differ diff --git a/Stock/bin/modele/Modele.class b/Stock/bin/modele/Modele.class new file mode 100644 index 0000000..41dd033 Binary files /dev/null and b/Stock/bin/modele/Modele.class differ diff --git a/Stock/bin/vue/VueAccueil.class b/Stock/bin/vue/VueAccueil.class new file mode 100644 index 0000000..931926f Binary files /dev/null and b/Stock/bin/vue/VueAccueil.class differ diff --git a/Stock/bin/vue/VueDelete.class b/Stock/bin/vue/VueDelete.class new file mode 100644 index 0000000..65a6d80 Binary files /dev/null and b/Stock/bin/vue/VueDelete.class differ diff --git a/Stock/bin/vue/VueInsert.class b/Stock/bin/vue/VueInsert.class new file mode 100644 index 0000000..37b9c0d Binary files /dev/null and b/Stock/bin/vue/VueInsert.class differ diff --git a/Stock/bin/vue/VueMenu.class b/Stock/bin/vue/VueMenu.class new file mode 100644 index 0000000..39a42d0 Binary files /dev/null and b/Stock/bin/vue/VueMenu.class differ diff --git a/Stock/sql.sql b/Stock/sql.sql new file mode 100644 index 0000000..a288543 --- /dev/null +++ b/Stock/sql.sql @@ -0,0 +1,40 @@ +Cours de base de donnĂ©es +1. Les requetes select +Soit la base de donnĂ©es suivante Stock avec la seule table produit. +_________________________________________ +Create database stock; +use stock; +create table produit ( + reference varchar(50) not null, + designation varchar (50), + prix float (5.2), + qte int (5), + categorie varchar(50), + primary key(reference) + ); +___________________________________________ +insert into produit values ("001", "lait", 1.20, 3, "alimentaire"), + ("002", "sucre", 0.90, 10, "alimentaire"), + ("003", "savon", 2.20, 5, "beautĂ©"), + ("004", "pelle", 6.20, 2, "jardinage"); +Question +Afficher tous les produits : + Select * from produit; + +Select * from produit where categorie = "alimentaire"; + +Afficher la rĂ©fĂ©rence, dĂ©signation, et quantitĂ© des +produits ayant un prix supĂ©rieur Ă  1 €. + +select reference, designation, qte +from produit +where prix > 1; + +Select reference, designation from produit where qte <= 2; + +Select * from produit +where prix < 1 and categorie = "alimentaire"; + + + + diff --git a/Stock/src/controleur/Gestion.java b/Stock/src/controleur/Gestion.java new file mode 100644 index 0000000..df992d6 --- /dev/null +++ b/Stock/src/controleur/Gestion.java @@ -0,0 +1,124 @@ +package controleur; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +import org.omg.CORBA.UNSUPPORTED_POLICY_VALUE; + +import modele.Modele; +import vue.VueAccueil; +import vue.VueDelete; +import vue.VueInsert; +import vue.VueMenu; + +public class Gestion extends JFrame implements ActionListener +{ + + private static VueInsert uneVueInsert ; + private static VueMenu uneVueMenu; + private static VueAccueil uneVueAccueil; + private static VueDelete uneVueDelete; + + private JMenuBar uneBarre = new JMenuBar(); + private JMenu mnFichier = new JMenu("Fichier"); + private JMenu mnOperations = new JMenu("Opérations"); + private JMenu mnAide = new JMenu("Aide"); + private JMenuItem itemQuitter = new JMenuItem("Quitter"); + + private JMenuItem itemAjouter = new JMenuItem("Ajouter"); + private JMenuItem itemLister = new JMenuItem("Lister"); + private JMenuItem itemSupprimer= new JMenuItem("Supprimer"); + private JMenuItem itemRechercher= new JMenuItem("Rechercher"); + private JMenuItem itemApropos = new JMenuItem("Apropos"); + + + + public Gestion() + { + this.setTitle("Gestion de stock"); + this.setBounds(200, 200, 500, 350); + this.setResizable(false); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); + this.setLayout(null); + uneVueInsert = new VueInsert(); + uneVueMenu = new VueMenu(); + uneVueAccueil = new VueAccueil(); + uneVueDelete = new VueDelete(); + + this.add(uneVueInsert); + this.add(uneVueMenu); + this.add(uneVueAccueil); + this.add(uneVueDelete); + + this.mnFichier.add(this.itemQuitter); + this.mnOperations.add(this.itemAjouter); + this.mnOperations.add(this.itemLister); + this.mnOperations.add(this.itemSupprimer); + this.mnOperations.add(this.itemRechercher); + this.mnAide.add(this.itemApropos); + + this.uneBarre.add(this.mnFichier); + this.uneBarre.add(this.mnOperations); + this.uneBarre.add(this.mnAide); + + this.setJMenuBar(this.uneBarre); + //rendre les items cliquables + this.itemAjouter.addActionListener(this); + this.itemSupprimer.addActionListener(this); + this.itemLister.addActionListener(this); + this.itemRechercher.addActionListener(this); + this.itemApropos.addActionListener(this); + this.itemQuitter.addActionListener(this); + + this.setVisible(true); + } + public static void main(String[] args) { + new Gestion (); + } + + public static void rendreVisibleVueInsert(boolean valeur) + { + uneVueInsert.setVisible(valeur); + } + public static void rendreVisibleVueAccueil(boolean valeur) + { + uneVueAccueil.setVisible(valeur); + } + + + public static void rendreVisibleVueDelete(boolean valeur) + { + uneVueDelete.setVisible(valeur); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource()==this.itemAjouter) + { + uneVueAccueil.setVisible(false); + uneVueDelete.setVisible(false); + uneVueInsert.setVisible(true); + }else if (e.getSource()==this.itemApropos) + { + uneVueDelete.setVisible(false); + uneVueAccueil.setVisible(true); + uneVueInsert.setVisible(false); + }else if (e.getSource()==this.itemQuitter) + { + this.dispose(); + }else if (e.getSource()==this.itemSupprimer) + { + uneVueDelete.setVisible(true); + uneVueAccueil.setVisible(false); + uneVueInsert.setVisible(false); + } + + } + +} diff --git a/Stock/src/controleur/Produit.java b/Stock/src/controleur/Produit.java new file mode 100644 index 0000000..a1871e5 --- /dev/null +++ b/Stock/src/controleur/Produit.java @@ -0,0 +1,61 @@ +package controleur; + +public class Produit +{ + private String reference, designation; + private int qte; + private float prix ; + + public Produit () + { + this.reference =""; + this.designation =""; + this.qte = 0; + this.prix = 0; + + } + public Produit(String reference, String designation, int qte, float prix) + { + this.reference = reference; + this.designation = designation; + this.qte = qte; + this.prix = prix ; + + } + + public String toString () + { + return this.reference+"-"+this.designation+"-"+this.qte+"-"+this.prix; + } + + + + public String getReference() { + return reference; + } + public void setReference(String reference) { + this.reference = reference; + } + public String getDesignation() { + return designation; + } + public void setDesignation(String designation) { + this.designation = designation; + } + public int getQte() { + return qte; + } + public void setQte(int qte) { + this.qte = qte; + } + public float getPrix() { + return prix; + } + public void setPrix(float prix) { + this.prix = prix; + } + + //getters et setters + + +} diff --git a/Stock/src/images/enregistrer.png b/Stock/src/images/enregistrer.png new file mode 100644 index 0000000..b18f9ab Binary files /dev/null and b/Stock/src/images/enregistrer.png differ diff --git a/Stock/src/modele/Bdd.java b/Stock/src/modele/Bdd.java new file mode 100644 index 0000000..3323429 --- /dev/null +++ b/Stock/src/modele/Bdd.java @@ -0,0 +1,67 @@ +package modele; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class Bdd +{ + private String serveur, bdd, user, mdp; + + private Connection maConnexion ; + + public Bdd (String serveur, String bdd, String user, String mdp) + { + this.serveur = serveur; + this.bdd = bdd; + this.user = user; + this.mdp = mdp; + this.maConnexion = null; + } + + public void chargerPilote () + { + //verifie la presence du pilote JDBC Mysql + try + { + Class.forName("com.mysql.jdbc.Driver"); + } + catch (ClassNotFoundException exp) + { + System.out.println("Absence du pilote Jdbc !"); + } + } + public void seConnecter () + { + //connexion au serveur et la bdd + this.chargerPilote(); + String url ="jdbc:mysql://"+this.serveur+"/"+this.bdd; + try + { + this.maConnexion = DriverManager.getConnection(url, this.user, this.mdp); + } + catch(SQLException exp) + { + System.out.println("Impossible de se connecter a : "+url); + } + } + public void seDeConnecter () + { + //fermeture de la connexion + try + { + if (this.maConnexion!=null) + { + this.maConnexion.close(); + } + } + catch (SQLException exp) + { + System.out.println("Erreur de fermeture de la connexion !"); + } + } + public Connection getMaConnexion () + { + return this.maConnexion; + } +} diff --git a/Stock/src/modele/Modele.java b/Stock/src/modele/Modele.java new file mode 100644 index 0000000..d83467f --- /dev/null +++ b/Stock/src/modele/Modele.java @@ -0,0 +1,110 @@ +package modele; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; + +import controleur.Produit; + +public class Modele +{ + + public static ArrayList selectAll() + { + ArrayList lesProduits = new ArrayList(); + String requete ="Select * from produit ; "; + try + { + Bdd uneBdd = new Bdd("localhost", "stock", "root", ""); + uneBdd.seConnecter(); + + Statement unStat = uneBdd.getMaConnexion().createStatement(); + ResultSet unRes = unStat.executeQuery(requete); + while (unRes.next()) + { + String reference = unRes.getString("reference"); + String designation = unRes.getString("designation"); + int qte = unRes.getInt("qte"); + float prix = unRes.getFloat("prix"); + Produit unProduit = new Produit(reference, designation, qte, prix); + lesProduits.add(unProduit); + } + unStat.close(); + unRes.close(); + uneBdd.seDeConnecter(); + } + catch (SQLException exp) + { + System.out.println("Erreur d'execution de la requete : "+requete); + } + + return lesProduits; + } + + public static void insert (Produit unProduit) + { + String requete ="insert into produit (reference, designation, qte, prix)" + + " values ('"+unProduit.getReference()+"','"+unProduit.getDesignation() + +"',"+unProduit.getQte()+","+unProduit.getPrix()+"); "; + + try + { + Bdd uneBdd = new Bdd("localhost", "stock", "root", ""); + uneBdd.seConnecter(); + + Statement unStat = uneBdd.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBdd.seDeConnecter(); + } + catch (SQLException exp) + { + System.out.println("Erreur d'execution requete : "+requete); + } + } + + public static void delete (String reference) + { + String requete = "DELETE FROM Produit WHERE reference ='"+reference+"';"; + + try + { + Bdd uneBdd = new Bdd("localhost", "stock", "root", ""); + uneBdd.seConnecter(); + + Statement unStat = uneBdd.getMaConnexion().createStatement(); + unStat.execute(requete); + unStat.close(); + uneBdd.seDeConnecter(); + } + catch (SQLException exp) + { + System.out.println("Erreur d'execution requete : "+requete); + } + } + + + + + + + + + + + +} + + + + + + + + + + + + + diff --git a/Stock/src/vue/VueAccueil.java b/Stock/src/vue/VueAccueil.java new file mode 100644 index 0000000..dc4b3ef --- /dev/null +++ b/Stock/src/vue/VueAccueil.java @@ -0,0 +1,27 @@ +package vue; + +import java.awt.Color; +import java.awt.GridLayout; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class VueAccueil extends JPanel +{ + public VueAccueil() + { + this.setBounds(150, 20, 300, 300); + this.setLayout(new GridLayout(4, 1)); + + this.setBackground(Color.GRAY); + JLabel titre = new JLabel("Logiciel realisé le 6-01-2017"); + JLabel logo = new JLabel(new ImageIcon("src/images/enregistrer.png")); + + this.add(new JLabel()); + this.add(titre); + this.add(logo); + this.add(new JLabel()); + this.setVisible(true); + } +} diff --git a/Stock/src/vue/VueDelete.java b/Stock/src/vue/VueDelete.java new file mode 100644 index 0000000..cbcacdd --- /dev/null +++ b/Stock/src/vue/VueDelete.java @@ -0,0 +1,65 @@ +package vue; + +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import controleur.Produit; +import modele.Modele; + +public class VueDelete extends JPanel implements ActionListener +{ + private JComboBox cbxChoix = new JComboBox(); + private JButton btOk = new JButton("Ok"); + private JLabel lbMessage = new JLabel(""); + + public VueDelete () + { + this.setBounds(150, 20, 300, 300); + this.setLayout(new GridLayout(5, 1)); + + this.setBackground(Color.GRAY); + this.add(new JLabel("Suppression d'un produit")); + this.add(this.cbxChoix); + this.add(this.btOk); + this.add(lbMessage); + this.add(new JLabel()); + + this.remplirCbx(); + this.btOk.addActionListener(this); + + this.setVisible(false); + + } + + public void remplirCbx() + { + ArrayList lesProduits = Modele.selectAll(); + for (Produit unProduit : lesProduits) + { + this.cbxChoix.addItem(unProduit.toString()); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource()== this.btOk) + { + String chaine = this.cbxChoix.getSelectedItem().toString(); + String tab[]= chaine.split("-"); + Modele.delete(tab[0]); + JOptionPane.showMessageDialog(this, "Suppression effectuee"); + this.lbMessage.setText("Suppression effectue"); + //on vide tout dans le cbx et on rerempli ou supprimer l'item seclectionne + } + + } +} diff --git a/Stock/src/vue/VueInsert.java b/Stock/src/vue/VueInsert.java new file mode 100644 index 0000000..1d261ff --- /dev/null +++ b/Stock/src/vue/VueInsert.java @@ -0,0 +1,115 @@ +package vue; + +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import controleur.Produit; +import modele.Modele; + +public class VueInsert extends JPanel implements ActionListener +{ + private JTextField txtRef = new JTextField(); + private JTextField txtDes = new JTextField(); + private JTextField txtQte = new JTextField(); + private JTextField txtPrix = new JTextField(); + + private JButton btAnnuler = new JButton("Annuler"); + private JButton btEnregistrer = new JButton("Enregistrer"); + + public VueInsert() + { + this.setBounds(150, 20, 300, 300); + this.setLayout(new GridLayout(6, 2)); + this.setBackground(Color.CYAN); + + this.add(new JLabel("Insertion")); + this.add(new JLabel("")); + + this.add(new JLabel("Référence : ")); + this.add(this.txtRef); + + this.add(new JLabel("Désignation : ")); + this.add(this.txtDes); + + this.add(new JLabel("Quantité : ")); + this.add(this.txtQte); + + this.add(new JLabel("Prix : ")); + this.add(this.txtPrix); + + this.add(this.btAnnuler); + this.add(this.btEnregistrer); + this.btEnregistrer.setIcon(new ImageIcon("src/images/enregistrer.png")); + + //rendre les boutons cliquables + this.btAnnuler.addActionListener(this); + this.btEnregistrer.addActionListener(this); + + this.setVisible(false); + + } + + @Override + public void actionPerformed(ActionEvent e) { + this.txtQte.setBackground(Color.WHITE); + this.txtPrix.setBackground(Color.WHITE); + + if(e.getSource() == this.btAnnuler) + { + this.txtDes.setText(""); + this.txtRef.setText(""); + this.txtQte.setText(""); + this.txtPrix.setText(""); + } + else if (e.getSource()==this.btEnregistrer) + { + String reference = this.txtRef.getText(); + String designation = this.txtDes.getText(); + int qte; + float prix; + try{ + + if (reference.equals("")||designation.equals("")) + { + JOptionPane.showMessageDialog(this, "Veuillez saisir la ref ou la des"); + } + else + { + qte = Integer.parseInt(this.txtQte.getText()); + prix = Float.parseFloat(this.txtPrix.getText()); + Produit unProduit = new Produit(reference, designation, qte, prix); + Modele.insert(unProduit); + JOptionPane.showMessageDialog(this, "Insertion reussie"); + this.txtDes.setText(""); + this.txtRef.setText(""); + this.txtQte.setText(""); + this.txtPrix.setText(""); + } + + this.setVisible(false); //fin enregistrement + } + catch(NumberFormatException exp) + { + JOptionPane.showMessageDialog(this, "Erreur de saisie sur la Qte ou prix"); + this.txtQte.setBackground(Color.RED); + this.txtPrix.setBackground(Color.RED); + } + } + + } +} + + + + + + diff --git a/Stock/src/vue/VueMenu.java b/Stock/src/vue/VueMenu.java new file mode 100644 index 0000000..4d63177 --- /dev/null +++ b/Stock/src/vue/VueMenu.java @@ -0,0 +1,66 @@ +package vue; + +import java.awt.Color; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import controleur.Gestion; + +public class VueMenu extends JPanel implements ActionListener +{ + private JButton btAjouter = new JButton("Ajouter"); + private JButton btLister = new JButton("Lister"); + private JButton btSupprimer = new JButton("Supprimer"); + private JButton btRechercher = new JButton("Rechercher"); + private JButton btQuitter= new JButton("Quitter"); + + public VueMenu () + { + this.setBounds(20, 20, 120, 300); + this.setLayout(new GridLayout(7, 1)); + this.setBackground(Color.YELLOW); + + this.add(new JLabel()); + this.add(this.btAjouter); + this.add(this.btLister); + this.add(this.btSupprimer); + this.add(this.btRechercher); + this.add(this.btQuitter); + this.add(new JLabel()); + + //rendre les boutons cliquables + this.btAjouter.addActionListener(this); + this.btLister.addActionListener(this); + this.btSupprimer.addActionListener(this); + this.btRechercher.addActionListener(this); + + this.setVisible(true); + + } + + @Override + public void actionPerformed(ActionEvent e) + { + if (e.getSource()==this.btQuitter) + { + + } + else if (e.getSource()==this.btAjouter) + { + Gestion.rendreVisibleVueInsert(true); + Gestion.rendreVisibleVueAccueil(false); + Gestion.rendreVisibleVueDelete(false); + + }else if (e.getSource()==this.btSupprimer) + { + Gestion.rendreVisibleVueInsert(false); + Gestion.rendreVisibleVueAccueil(false); + Gestion.rendreVisibleVueDelete(true); + } + } +} diff --git a/Test1/.classpath b/Test1/.classpath new file mode 100644 index 0000000..51a8bba --- /dev/null +++ b/Test1/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Test1/.project b/Test1/.project new file mode 100644 index 0000000..bd0683a --- /dev/null +++ b/Test1/.project @@ -0,0 +1,17 @@ + + + Test1 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Test1/.settings/org.eclipse.jdt.core.prefs b/Test1/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Test1/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/TestTableau/.classpath b/TestTableau/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/TestTableau/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/TestTableau/.project b/TestTableau/.project new file mode 100644 index 0000000..77fe2b6 --- /dev/null +++ b/TestTableau/.project @@ -0,0 +1,17 @@ + + + TestTableau + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/TestTableau/.settings/org.eclipse.jdt.core.prefs b/TestTableau/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/TestTableau/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/TestTableau/bin/modele/ButtonEditor$ButtonListener.class b/TestTableau/bin/modele/ButtonEditor$ButtonListener.class new file mode 100644 index 0000000..ed289e8 Binary files /dev/null and b/TestTableau/bin/modele/ButtonEditor$ButtonListener.class differ diff --git a/TestTableau/bin/modele/ButtonEditor.class b/TestTableau/bin/modele/ButtonEditor.class new file mode 100644 index 0000000..fa5c39a Binary files /dev/null and b/TestTableau/bin/modele/ButtonEditor.class differ diff --git a/TestTableau/bin/modele/ButtonRenderer.class b/TestTableau/bin/modele/ButtonRenderer.class new file mode 100644 index 0000000..e02bfe1 Binary files /dev/null and b/TestTableau/bin/modele/ButtonRenderer.class differ diff --git a/TestTableau/bin/modele/ComboRenderer.class b/TestTableau/bin/modele/ComboRenderer.class new file mode 100644 index 0000000..a9c675a Binary files /dev/null and b/TestTableau/bin/modele/ComboRenderer.class differ diff --git a/TestTableau/bin/modele/DeleteButtonEditor$DeleteButtonListener.class b/TestTableau/bin/modele/DeleteButtonEditor$DeleteButtonListener.class new file mode 100644 index 0000000..69fc13c Binary files /dev/null and b/TestTableau/bin/modele/DeleteButtonEditor$DeleteButtonListener.class differ diff --git a/TestTableau/bin/modele/DeleteButtonEditor.class b/TestTableau/bin/modele/DeleteButtonEditor.class new file mode 100644 index 0000000..9249c74 Binary files /dev/null and b/TestTableau/bin/modele/DeleteButtonEditor.class differ diff --git a/TestTableau/bin/modele/Fenetre$MoreListener.class b/TestTableau/bin/modele/Fenetre$MoreListener.class new file mode 100644 index 0000000..6c8d1e7 Binary files /dev/null and b/TestTableau/bin/modele/Fenetre$MoreListener.class differ diff --git a/TestTableau/bin/modele/Fenetre.class b/TestTableau/bin/modele/Fenetre.class new file mode 100644 index 0000000..dd8fd55 Binary files /dev/null and b/TestTableau/bin/modele/Fenetre.class differ diff --git a/TestTableau/bin/modele/ZModel.class b/TestTableau/bin/modele/ZModel.class new file mode 100644 index 0000000..c719ffa Binary files /dev/null and b/TestTableau/bin/modele/ZModel.class differ diff --git a/TestTableau/src/modele/ButtonEditor.java b/TestTableau/src/modele/ButtonEditor.java new file mode 100644 index 0000000..c14d016 --- /dev/null +++ b/TestTableau/src/modele/ButtonEditor.java @@ -0,0 +1,66 @@ +package modele; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultCellEditor; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +//CTRL + SHIFT + O pour générer les imports +public class ButtonEditor extends DefaultCellEditor { + + protected JButton button; + private ButtonListener bListener = new ButtonListener(); + + public ButtonEditor(JCheckBox checkBox) { + //Par défaut, ce type d'objet travaille avec un JCheckBox + super(checkBox); + //On crée ŕ nouveau notre bouton + button = new JButton(); + button.setOpaque(true); + //On lui attribue un listener + button.addActionListener(bListener); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column){ + //On définit le numéro de ligne ŕ notre listener + bListener.setRow(row); + //Idem pour le numéro de colonne + bListener.setColumn(column); + //On passe aussi en paramčtre le tableau pour des actions potentielles + bListener.setTable(table); + //On réaffecte le libellé au bouton + button.setText( (value ==null) ? "" : value.toString() ); + //On renvoie le bouton + return button; + } + + class ButtonListener implements ActionListener { + + private int column, row; + private JTable table; + private int nbre = 0; + private JButton button; + + public void setColumn(int col){this.column = col;} + public void setRow(int row){this.row = row;} + public void setTable(JTable table){this.table = table;} + public JButton getButton(){return this.button;} + + public void actionPerformed(ActionEvent event) { + //On affiche un message mais vous pourriez faire ce que vous voulez + System.out.println("coucou du bouton : "+ + ((JButton)event.getSource()).getText() ); + //On affecte un nouveau libellé ŕ une celulle de la ligne + ((AbstractTableModel)table.getModel()).setValueAt("New Value " + (++nbre), this.row, (this.column -1)); + //Permet de dire ŕ notre tableau qu'une valeur a changé + //ŕ l'emplacement déterminé par les valeurs passées en paramčtres + ((AbstractTableModel)table.getModel()).fireTableCellUpdated(this.row, this.column - 1); + this.button = ((JButton)event.getSource()); + } +} +} \ No newline at end of file diff --git a/TestTableau/src/modele/ButtonRenderer.java b/TestTableau/src/modele/ButtonRenderer.java new file mode 100644 index 0000000..548dd36 --- /dev/null +++ b/TestTableau/src/modele/ButtonRenderer.java @@ -0,0 +1,18 @@ +package modele; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JTable; +import javax.swing.table.TableCellRenderer; + +//CTRL + SHIFT + O pour générer les imports +public class ButtonRenderer extends JButton implements TableCellRenderer{ + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean isFocus, int row, int col) { + //On écrit dans le bouton avec la valeur de la cellule + setText((value != null) ? value.toString() : ""); + //On retourne notre bouton + return this; + } +} diff --git a/TestTableau/src/modele/ComboRenderer.java b/TestTableau/src/modele/ComboRenderer.java new file mode 100644 index 0000000..f1f18e4 --- /dev/null +++ b/TestTableau/src/modele/ComboRenderer.java @@ -0,0 +1,19 @@ +package modele; + +import java.awt.Component; + +import javax.swing.JComboBox; +import javax.swing.JTable; +import javax.swing.table.TableCellRenderer; + +//CTRL + SHIFT + O pour générer les imports +public class ComboRenderer extends JComboBox implements TableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean isFocus, int row, int col) { + + this.addItem("Trčs bien"); + this.addItem("Bien"); + this.addItem("Mal"); + return this; + } + } \ No newline at end of file diff --git a/TestTableau/src/modele/DeleteButtonEditor.java b/TestTableau/src/modele/DeleteButtonEditor.java new file mode 100644 index 0000000..429d9aa --- /dev/null +++ b/TestTableau/src/modele/DeleteButtonEditor.java @@ -0,0 +1,57 @@ +package modele; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultCellEditor; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JTable; + +//CTRL + SHIFT + O pour générer les imports +public class DeleteButtonEditor extends DefaultCellEditor { + + protected JButton button; + private DeleteButtonListener bListener = new DeleteButtonListener(); + + public DeleteButtonEditor(JCheckBox checkBox) { + //Par défaut, ce type d'objet travaille avec un JCheckBox + super(checkBox); + //On crée ŕ nouveau notre bouton + button = new JButton(); + button.setOpaque(true); + //On lui attribue un listener + button.addActionListener(bListener); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + //On définit le numéro de ligne ŕ notre listener + bListener.setRow(row); + //On passe aussi en paramčtre le tableau pour des actions potentielles + bListener.setTable(table); + //On réaffecte le libellé au bouton + button.setText( (value ==null) ? "" : value.toString() ); + //On renvoie le bouton + return button; + } + + class DeleteButtonListener implements ActionListener { + + private int row; + private JTable table; + + public void setRow(int row){this.row = row;} + public void setTable(JTable table){this.table = table;} + + public void actionPerformed(ActionEvent event) { + if(table.getRowCount() > 0){ + //On affiche un message mais vous pourriez faire ce que vous voulez + System.out.println("coucou du bouton : "+ ((JButton)event.getSource()).getText() ); + //On affecte un nouveau libellé ŕ une celulle de la ligne + ((ZModel)table.getModel()).removeRow(this.row); + + } + } + } +} \ No newline at end of file diff --git a/TestTableau/src/modele/Fenetre.java b/TestTableau/src/modele/Fenetre.java new file mode 100644 index 0000000..2bdcb0f --- /dev/null +++ b/TestTableau/src/modele/Fenetre.java @@ -0,0 +1,83 @@ +package modele; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultCellEditor; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +//CTRL + SHIFT + O pour générer les imports +public class Fenetre extends JFrame { + + private JTable tableau; + private JButton change = new JButton("Changer la taille"); + private String[] comboData = {"Trčs bien", "Bien", "Mal"}; + private String supp = "Supprimer la ligne"; + private JComboBox combo; + + public Fenetre(){ + this.setLocationRelativeTo(null); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setTitle("JTable"); + this.setSize(600, 250); + this.createContent(); + } + + private void createContent(){ + //Données de notre tableau + Object[][] data = { + {"Cysboy", "6boy", comboData[0], new Boolean(true), supp}, + {"BZHHydde", "BZH", comboData[0], new Boolean(false), supp}, + {"IamBow", "BoW", comboData[0], new Boolean(false), supp}, + {"FunMan", "Year", comboData[0], new Boolean(true), supp} + }; + + String title[] = {"Pseudo", "Age", "Taille", "OK ?", "Suppression"}; + combo = new JComboBox(comboData); + + //Notre modčle d'affichage spécifique destiné ŕ pallier + //les bugs d'affichage ! + ZModel zModel = new ZModel(data, title); + + this.tableau = new JTable(zModel); + this.tableau.setRowHeight(30); + this.tableau.getColumn("Age").setCellRenderer(new ButtonRenderer()); + this.tableau.getColumn("Age").setCellEditor(new ButtonEditor(new JCheckBox())); + + //On définit l'éditeur par défaut pour la cellule + //en lui spécifiant quel type d'affichage prendre en compte + this.tableau.getColumn("Taille").setCellEditor(new DefaultCellEditor(combo)); + DefaultTableCellRenderer dcr = new DefaultTableCellRenderer(); + this.tableau.getColumn("Taille").setCellRenderer(dcr); + + //On définit un éditeur pour la colonne "supprimer" + this.tableau.getColumn("Suppression").setCellEditor(new DeleteButtonEditor(new JCheckBox())); + + //On ajoute le tableau + this.getContentPane().add(new JScrollPane(tableau), BorderLayout.CENTER); + + JButton ajouter = new JButton("Ajouter une ligne"); + ajouter.addActionListener(new MoreListener()); + this.getContentPane().add(ajouter, BorderLayout.SOUTH); + } + + class MoreListener implements ActionListener{ + public void actionPerformed(ActionEvent event) { + Object[] donnee = new Object[] + {"Angelo", "Rennais", comboData[0], new Boolean(false), supp}; + ((ZModel)tableau.getModel()).addRow(donnee); + } + } + + public static void main(String[] args){ + Fenetre fen = new Fenetre(); + fen.setVisible(true); + } +} \ No newline at end of file diff --git a/TestTableau/src/modele/ZModel.java b/TestTableau/src/modele/ZModel.java new file mode 100644 index 0000000..31298c1 --- /dev/null +++ b/TestTableau/src/modele/ZModel.java @@ -0,0 +1,96 @@ +package modele; + +import javax.swing.JTextField; +import javax.swing.table.AbstractTableModel; + +class ZModel extends AbstractTableModel{ + private Object[][] data; + private String[] title; + + //Constructeur + public ZModel(Object[][] data, String[] title){ + this.data = data; + this.title = title; + } + + //Retourne le titre de la colonne ŕ l'indice spécifié + public String getColumnName(int col) { + return this.title[col]; + } + + //Retourne le nombre de colonnes + public int getColumnCount() { + return this.title.length; + } + + //Retourne le nombre de lignes + public int getRowCount() { + return this.data.length; + } + + //Retourne la valeur ŕ l'emplacement spécifié + public Object getValueAt(int row, int col) { + return this.data[row][col]; + } + + //Définit la valeur ŕ l'emplacement spécifié + public void setValueAt(Object value, int row, int col) { + //On interdit la modification sur certaines colonnes ! + if(!this.getColumnName(col).equals("Age") + && !this.getColumnName(col).equals("Suppression")) + this.data[row][col] = value; + } + + //Retourne la classe de la donnée de la colonne + public Class getColumnClass(int col){ + //On retourne le type de la cellule ŕ la colonne demandée + //On se moque de la ligne puisque les données sont les męmes + //On choisit donc la premičre ligne + return this.data[0][col].getClass(); + } + + //Méthode permettant de retirer une ligne du tableau + public void removeRow(int position){ + + int indice = 0, indice2 = 0; + int nbRow = this.getRowCount()-1; + int nbCol = this.getColumnCount(); + Object temp[][] = new Object[nbRow][nbCol]; + + for(Object[] value : this.data){ + if(indice != position){ + temp[indice2++] = value; + } + System.out.println("Indice = " + indice); + indice++; + } + this.data = temp; + temp = null; + //Cette méthode permet d'avertir le tableau que les données + //ont été modifiées, ce qui permet une mise ŕ jour complčte du tableau + this.fireTableDataChanged(); + } + + //Permet d'ajouter une ligne dans le tableau + public void addRow(Object[] data){ + int indice = 0, nbRow = this.getRowCount(), nbCol = this.getColumnCount(); + + Object temp[][] = this.data; + this.data = new Object[nbRow+1][nbCol]; + + for(Object[] value : temp) + this.data[indice++] = value; + + + this.data[indice] = data; + temp = null; + //Cette méthode permet d'avertir le tableau que les données + //ont été modifiées, ce qui permet une mise ŕ jour complčte du tableau + this.fireTableDataChanged(); + } + + + public boolean isCellEditable(int row, int col){ + return true; + } + } \ No newline at end of file diff --git a/bug_game/.classpath b/bug_game/.classpath new file mode 100644 index 0000000..315bd1e --- /dev/null +++ b/bug_game/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/bug_game/.project b/bug_game/.project new file mode 100644 index 0000000..a4125e0 --- /dev/null +++ b/bug_game/.project @@ -0,0 +1,17 @@ + + + bug_game + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/bug_game/.settings/org.eclipse.jdt.core.prefs b/bug_game/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..7341ab1 --- /dev/null +++ b/bug_game/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/bug_game/Thumbs.db b/bug_game/Thumbs.db new file mode 100644 index 0000000..f592ec8 Binary files /dev/null and b/bug_game/Thumbs.db differ diff --git a/bug_game/bin/Bug.class b/bug_game/bin/Bug.class new file mode 100644 index 0000000..24c1dde Binary files /dev/null and b/bug_game/bin/Bug.class differ diff --git a/bug_game/bin/Friandise.class b/bug_game/bin/Friandise.class new file mode 100644 index 0000000..37e0732 Binary files /dev/null and b/bug_game/bin/Friandise.class differ diff --git a/bug_game/bin/Gadget.class b/bug_game/bin/Gadget.class new file mode 100644 index 0000000..7b72b9b Binary files /dev/null and b/bug_game/bin/Gadget.class differ diff --git a/bug_game/bin/Game.class b/bug_game/bin/Game.class new file mode 100644 index 0000000..da65843 Binary files /dev/null and b/bug_game/bin/Game.class differ diff --git a/bug_game/bin/Labyrinthe.class b/bug_game/bin/Labyrinthe.class new file mode 100644 index 0000000..f96a862 Binary files /dev/null and b/bug_game/bin/Labyrinthe.class differ diff --git a/bug_game/bug_bot.jpeg b/bug_game/bug_bot.jpeg new file mode 100644 index 0000000..2494554 Binary files /dev/null and b/bug_game/bug_bot.jpeg differ diff --git a/bug_game/bug_game/.project b/bug_game/bug_game/.project new file mode 100644 index 0000000..a4125e0 --- /dev/null +++ b/bug_game/bug_game/.project @@ -0,0 +1,17 @@ + + + bug_game + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/bug_game/bug_left.jpeg b/bug_game/bug_left.jpeg new file mode 100644 index 0000000..cf0aaa4 Binary files /dev/null and b/bug_game/bug_left.jpeg differ diff --git a/bug_game/bug_right.jpeg b/bug_game/bug_right.jpeg new file mode 100644 index 0000000..de63506 Binary files /dev/null and b/bug_game/bug_right.jpeg differ diff --git a/bug_game/bug_top.jpeg b/bug_game/bug_top.jpeg new file mode 100644 index 0000000..1f248d8 Binary files /dev/null and b/bug_game/bug_top.jpeg differ diff --git a/bug_game/fraise.jpeg b/bug_game/fraise.jpeg new file mode 100644 index 0000000..d07cc30 Binary files /dev/null and b/bug_game/fraise.jpeg differ diff --git a/bug_game/src/Bug.java b/bug_game/src/Bug.java new file mode 100644 index 0000000..87def57 --- /dev/null +++ b/bug_game/src/Bug.java @@ -0,0 +1,75 @@ +import java.util.Random; + + + +public class Bug { + private int pos_x; + private int pos_y; + private String orientation; + + public Bug(int i, int j) { + pos_x = i; pos_y = j; orientation = "right"; + } + + public void deplacer() { + if (orientation == "right") { + pos_y++; + } + else if (orientation == "left") { + pos_y--; + } + else if (orientation == "top") { + pos_x--; + } + else if (orientation == "bot") { + pos_x++; + } + } + + public String afficher() { + return orientation; + } + + public boolean rotation(int taillePlateau) { + Random r = new Random(); + int alea = r.nextInt(4); + boolean correctRotation = true; + + switch(alea){ + case 0: + if(pos_y + 1 > taillePlateau){ + correctRotation = false; + break; + } + orientation = "right"; + break; + case 1: + if(pos_y - 1 < 0){ + correctRotation = false; + break; + } + orientation = "left"; + break; + case 2: + if(pos_x - 1 < 0){ + correctRotation = false; + break; + } + orientation = "top"; + break; + case 3: default: + if(pos_x + 1 > taillePlateau){ + correctRotation = false; + break; + } + orientation = "bot"; + break; + } + + return correctRotation; + } + public int getX() {return pos_x;} + public int getY() {return pos_y;} + + +} diff --git a/bug_game/src/Friandise.java b/bug_game/src/Friandise.java new file mode 100644 index 0000000..93793c3 --- /dev/null +++ b/bug_game/src/Friandise.java @@ -0,0 +1,18 @@ +public class Friandise extends Gadget { + private String nom; + + public Friandise(int i, int j) { + super(i, j); + nom = "Friandise"; + } + + @Override + public void utilisation(Bug g) { + + } + + public String afficher() { + return nom; + } + +} diff --git a/bug_game/src/Gadget.java b/bug_game/src/Gadget.java new file mode 100644 index 0000000..41586c4 --- /dev/null +++ b/bug_game/src/Gadget.java @@ -0,0 +1,18 @@ +public abstract class Gadget { + + /* Attribut */ + private int pos_x; + private int pos_y; + + /* Constructeur */ + protected Gadget(int i, int j) { + pos_x = i; + pos_y = j; + } + + public abstract void utilisation(Bug b); + + public abstract String afficher(); + public int getX() {return pos_x;} + public int getY() {return pos_y;} +} diff --git a/bug_game/src/Game.java b/bug_game/src/Game.java new file mode 100644 index 0000000..030e13a --- /dev/null +++ b/bug_game/src/Game.java @@ -0,0 +1,137 @@ +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Image; + +import java.util.concurrent.TimeUnit; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + + +public class Game extends JPanel{ + + /** + * + */ + private static final long serialVersionUID = 1L; + public static int taille = 7; + + public static void main(String[] args) { + + Labyrinthe lab = new Labyrinthe(taille - 1); + JFrame fenetre = new JFrame(); + fenetre.setTitle("BUG GAME"); + fenetre.setSize(500,500); + fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + fenetre.setLocationRelativeTo(null); + fenetre.setResizable(false); + fenetre.setLayout(new BorderLayout()); + JPanel content = new JPanel(); + fenetre.setContentPane(content); + content.setLayout(new GridLayout(taille, taille));//on dĂ©finit la taille de la grille de 10 sur 10 + + JPanel[][] plat = afficherLabyrinthe(lab, content); + fenetre.setVisible(true); + + while(lab.getGadgets()[0].getX() != lab.getBugs()[0].getX() || + lab.getGadgets()[0].getY() != lab.getBugs()[0].getY()) { + // Pour une execution pas Ă  pas + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + deplacement(lab, plat, content); + + } + lab.getGadgets()[0].utilisation(lab.getBugs()[0]); + plat[lab.getGadgets()[0].getX()][lab.getGadgets()[0].getY()].removeAll(); + plat[lab.getGadgets()[0].getX()][lab.getGadgets()[0].getY()].add(getImg(lab.getBugs()[0].afficher())); + plat[lab.getGadgets()[0].getX()][lab.getGadgets()[0].getY()].revalidate(); + + + } + + + //creation des cases et des collones du jpanel + public static JPanel[][] afficherLabyrinthe(Labyrinthe lab, JPanel content){ + JPanel[][] plateau = new JPanel[taille-1][taille-1]; + + int taille = lab.getTaille(); + // Creation du plateau + Bug[] bugs = lab.getBugs(); + Gadget[] gadgets = lab.getGadgets(); + content.setBackground(Color.white); + + for(int i=0; i=5.1.13 + + - Fix for Bug#73594 (19450418), ClassCastException in MysqlXADataSource if pinGlobalTxToPhysicalConnection=true + + - Fix for Bug#19354014, changeUser() call results in "packets out of order" error when useCompression=true. + + - Fix for Bug#73577 (19443777), CHANGEUSER() CALL WITH USECOMPRESSION=TRUE COULD LEAD TO IO FREEZE + + - Fix for Bug#19172037, TEST FAILURES WHEN RUNNING AGAINST 5.6.20 SERVER VERSION + +08-04-14 - Version 5.1.32 + + - Fix for Bug#71923 (18344403), Incorrect generated keys if ON DUPLICATE KEY UPDATE not exact. + Additionally several methods in StringUtils were fixed/upgraded. + + - Fix for Bug#72502 (18691866), NullPointerException in isInterfaceJdbc() when using DynaTrace + + - Fix for Bug#72890 (18970520), Java jdbc driver returns incorrect return code when it's part of XA transaction. + + - Fabric client now supports Fabric 1.5. Older versions are no longer supported. + + - Fix for Bug#71672 (18232840), Every SQL statement is checked if it contains "ON DUPLICATE KEY UPDATE" or not. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#73070 (19034681), Preparing a stored procedure call with Fabric results in an exception + + - Fix for Bug#73053 (19022745), Endless loop in MysqlIO.clearInputStream due to Linux kernel bug. + In the source of this issue is a Linux kernel bug described in the patch "tcp: fix FIONREAD/SIOCINQ" + (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=a3374c4). + + - Fix for Bug#18869381, CHANGEUSER() FOR SHA USER RESULTS IN NULLPOINTEREXCEPTION + + - Fix for Bug#62577 (16722757), XA connection fails with ClassCastException + + - Fix for Bug#18852587, CONNECT WITH A USER CREATED USING SHA256_PASSWORD PLUGIN FAILS WHEN PWD IS BLANK + + - Fix for Bug#18852682, TEST TESTSHA256PASSWORDPLUGIN FAILS WHEN EXECUTE AGAINST COMMERCIAL SERVER + + - Fix for failing tests when running test suite with Java 6+. + Includes fix for Bug#35829 (11748301), build.xml check for java6 should use or instead of and. + + - Charset mappings refactored. + + - Fix for Bug#72712 (18836319), No way to configure Connector JDBC to not do extra queries on connection + +06-09-14 - Version 5.1.31 + + - Fix for Bug#66947 (16004987), Calling ServerPreparedStatement.close() twice corrupts cached statements. + + - Fix for Bug#61213 (18009254), ON DUPLICATE KEY UPDATE breaks generated key list when extended INSERT is used + + - Test cases updated to comply with MySQL 5.7.4 new STRICT_MODE behavior and no longer supported IGNORE clause in + ALTER TABLE statement. + + - Added support for sha256_password authentication with RSA encryption. + + - Fix for Bug#71753 (18260918), Bad SSL socket transform. + + - Added tests for changes in GET DIAGNOSTIC syntax introduced in MySQL 5.7.0. + + - Fix for Bug#67803 (16708231), XA commands sent twice to MySQL server. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#55680 (16737192), MySQL Connector/J memory leak + + - Fix for Bug#72326 (18598665), Typo in fullDebug.properties - gatherPerMetrics should be gatherPerfMetrics + + - Fix for Bug#72023 (18403456), Avoid byte array creation in MysqlIO#unpackBinaryResultSetRow. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#72000 (18402873), java.lang.ArrayIndexOutOfBoundsException on java.sql.ResultSet.getInt(String). + + - Fix for Bug#71850 (18318197), init() is called twice on exception interceptors + + - Fix for Bug#72008 (18389973), Avoid useless object creation in StringUtils#getBytes-methods. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#72006 (18403199), Avoid creation of a character array in PreparedStatement$ParseInfo. + Thanks to Andrej Golovnin for his contribution. + Additionally, unneeded StringBuffer replaced by StringBuilder instances in StringUtils. + + - Fix for Bug#72301 (18549472), Fabric driver swallows exceptions thrown during connection creation using JDBC4 + +03-28-14 - Version 5.1.30 + + - Fix for Bug#71679 (18236388), Avoid iterator creation when invoking statement interceptors in MysqlIO. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#70944 (17831255), community and commercial builds should have the same line number tables + + - Fix for Bug#71861 (18327245), Avoid manual array copy in MysqlIO and LoadBalancingConnectionProxy. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#71623 (18228668), Field#getStringFromBytes() creates useless byte array when using JVM converter. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#71621 (18228302), MysqlXAConnection#xidToString(Xid xid) produces too much garbage. + Thanks to Andrej Golovnin for his contribution. + + - Fix for Bug#67318 (16722637), SQLException thrown on already closed ResultSet. Thanks to Thomas Manville and Andrej Golovnin for their contribution. + + - Fix for Bug#71396 (18110320), setMaxRows (SQL_SELECT_LIMIT) from one query used in later queries (sometimes). + Additionally, SQL_SELECT_LIMIT is no longer sent unnecessarily between consecutive queries. + + - Fix for Bug#71432 (18107621), Key store files not closed when making SSL connection + + - Reserved words lists updated from latest official SQL:92 and SQL:2003 specifications. + + - Fix for Bug#18091639, STRINGINDEXOUTOFBOUNDSEXCEPTION IN PREPAREDSTATEMENT.SETTIMESTAMP WITH 5.6.15 + + - Added Fabric support + +02-10-14 - Version 5.1.29 + + - Fix for Bug#70701 (17647584), DatabaseMetaData.getSQLKeywords() doesn't match MySQL 5.6 reserved words. + + - Fix for Bug#17435879, REMOVE SRC/LIB-NODIST DIRECTORY FROM LAUNCHPAD DISTRIBUTION. + Additional "com.mysql.jdbc.extra.libs" parameter must be used for ant build. + + - Fix for Bug#71038, Add an option for custom collations detection. + Added new connection property detectCustomCollations=[true|false], with default false. + Please be aware that these changed the previous default behavior and if you use custom charsets or collations + you need to set detectCustomCollations=true. + + - Added tests for new index renaming syntax introduced in 5.7.1. + +12-23-13 - Version 5.1.28 + + - Fix for Bug#69579, DriverManager.setLoginTimeout not honored. + + - Fix for Bug#51313, Escape processing is confused by multiple backslashes. + + - Fix for Bug#55340, initializeResultsMetadataFromCache fails on second call to stored proc. + + - Fix for Bug#70969, Shadow declaration of OperationNotSupportedException in RowDataDynamic. + + - Fix for Bug#70835 (17750877), SQLExceptions thrown because of query interruption (KILL QUERY, query timeout, etc.) + didn't extend java.sql.SQLNonTransientException for JDBC4+ deployments. + + - Fix for Bug#24344 test case, test fails if it's run with UTC timezone settings. + + - Fix for Bug#69777, Setting maxAllowedPacket below 8203 makes blobSendChunkSize negative. + + - Fix for Bug#35115, yearIsDateType=false has no effect on result's column type and class. + + - Fix for Bug#68916 (16691047), closeOnCompletion doesn't work. + + - Fix for Bug #69746 (17164058), ResultSet closed after Statement.close() when dontTrackOpenResources=true + + - Fix for Bug#70842 (17753369), Adding live management of replication host topographies. + +11-04-13 - Version 5.1.27 + + - Fix for Bug#17248345, getFunctionColumns() method returns columns of procedure. + + - Fix for Bug#69290 (16879239), JDBC Table type "SYSTEM TABLE" is used inconsistently. + + - Fix for Bug#68562, Combination rewriteBatchedStatements and useAffectedRows not working as expected. + + - Fix for Bug#69452 (17015673), memory size connection property doesn't support large values well. + + - Added tests for InnoDB full-text search support introduced in 5.6GA. + + - Extended slow query warning with query execution plan for INSERT, REPLACE, UPDATE and DELETE. + + - Added tests for IPv6 functions introduced in 5.6GA. + + - Added support of authentication data up to 2^64-1 bytes. + + - Fix for Bug#38252, ResultSet.absolute(0) is not behaving according to JDBC specification. + + - Fix for Bug#62469, JDBC Authentication Fails with Null Byte in Scramble + + - Fix for Bug#69506, XAER_DUPID error code is not returned when a duplicate XID is offered in Java. + + - Added support for multi-master replication topographies in ReplicationDriver. ReplicationDriver now uses two discrete load-balanced + connections, one each for master and slave connections. The same load-balancing options which apply to load-balanced connections + now also apply to ReplicationConnections. By default, this means that when a ReplicationConnection uses master connections + (because the read-only property of the Connection is false), work may be re-balanced between configured master hosts at transaction + boundaries. As with load-balanced connections, the ReplicationConnection host list may be managed within the JVM (see + com.mysql.jdbc.ReplicationConnectionGroupManager) or optionally via JMX (using replicationEnableJMX configuration option; see + com.mysql.jdbc.jmx.ReplicationGroupManagerMBean). To specify multi-master replication topographies, define each host "type" + property using the following format: + + address=(host=hostname)(port=3306)(type=[master|slave]) + + In the absense of explicit type definitions, the driver will assume a single master listed first, with all subsequently-listed + hosts configured as slaves. + + - Fix for Bug#63354 (16443992), JDBC cannot make new connections if master is down. + + - Fix for Bug#17003626, REGRESSION TEST FAILURE WITH SERVER VERSION 5.7.1 + + - Removed ant-contrib.jar from C/J distribution. + + - Added tests for GIS precise spatial operations introduced in 5.6GA. + + - Fixed META-INF information + + - Fix for Bug#17251955, ARRAYINDEXOUTOFBOUNDSEXCEPTION ON LONG MULTI-BYTE DB/USER NAMES + + - Fix for Bug#50538, DatabaseMetaData.getDriverVersion() contains unexpanded ${bzr.revision-id} + +08-05-13 - Version 5.1.26 + + - Fix for Bug#69298 (16845965), Methods DatabaseMetaData.getProcedures() and DatabaseMetaData.getProcedureColumns(), in JDBC4, + return stored procedure only or both stored procedures and functions metadata information, depending on the value set in the + connection property "getProceduresReturnsFunctions", having default value 'true'. Several fixes in Functions and + Procedures metadata so that consulting I__S and MySQL/DDL returns the same info. + + - Fix for Bug#69308 (16879267), Avoid calling batchedStatement.close() twice, and thus raising and ignoring an undercover SQLException, in methods + PreparedStatement.executeBatchedInserts and PreparedStatement.executePreparedBatchAsMultiStatement. + + - Fix for Bug#68400, useCompression=true and connect to server, zip native method cause out of memory. + CompressedInputStream now does not keep reference to connection. + Thank Dominic Tootell for his investigation, proposed solution and all the help he provided. + + - Fix for Bug#65871, DatabaseMetaData.getColumns() throws an MySQLSyntaxErrorException. + Delimited names of databases and tables are handled correctly now. The edge case is ANSI quoted + identifiers with leading and trailing "`" symbols, for example CREATE DATABASE "`dbname`". Methods + like DatabaseMetaData.getColumns() allow parameters passed both in unquoted and quoted form, + quoted form is not JDBC-compliant but used by third party tools. So when you pass the indentifier + "`dbname`" in unquoted form (`dbname`) driver handles it as quoted by "`" symbol. To handle such + identifiers correctly a new behavior was added to pedantic mode (connection property pedantic=true), + now if it set to true methods like DatabaseMetaData.getColumns() treat all parameters as unquoted. + + - Fix for Bug#45757 (11754192), Don't allow updateRow() to be called when updatable cursor is positioned on insert row. + + - Fix for Bug#68098 (16224299), Return indexes sorted by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION in DatabaseMetaData.getIndexInfo. + + - Fix for Bug#68307 (16707803), Return correct COLUMN_TYPE from both getProcedureColumns() and getFunctionColumns(). + + - Fix for Bug#42267, PreparedStatementWrapper doesn't have a toString() implementation + + - Fix for Bug#44451 (11753081), Added missing fields in methods getColumns(), getProcedureColumns(), getTables() and getUDTs(). + Methods getClientInfoProperties() and getFunctions() were made available in all *DatabaseMetaDataUsingInfoSchema implementations. + +05-06-13 - Version 5.1.25 + + - Fix for Bug#68801, java webstart mysql-connector-java lib calls -bin library. + + - Fix for Bug#16426462, SyntaxRegressionTest failing on C/J 5.1.24 against MySQL 5.6.10 + + - Fix for Bug#60816, Cannot pass NULL to an INOUT procedure parameter. + + - Added support for Connection Attributes when used with MySQL Server versions (5.6+) which support this feature. + By default, the following standard attributes are sent to the server, where they can be seen in the + performance_schema.session_connect_attrs table: + * _client_version : the version of MySQL Connector Java in use + * _client_name : "MySQL Connector Java" + * _runtime_version : the version of the Java runtime environment in which the driver is running + * _runtime_vendor : the name of company which produced the Java runtime environment + Additionally, users may supply their own key/value attributes to be exposed by providing them in + "key1:value1,key2:value2" format in the connectionAttributes connection property. + To avoid sending any connection attributes to the server, set connectionAttributes property to "none". + + - Fix for Bug#68763 (16545334), ReplicationConnection.isMasterConnection() returns false always. + + - Fix for Bug#68733 (16526938), ReplicationConnection doesn't ping all slaves. + + - Fix for Bug#68556, Tomcat can't stop a cleanup thread by clearReferencesStopThreads. + + - Fix for Bug#16436511, getDriverName() returns a string with company name "MySQL-AB". Driver name changed to "MySQL Connector Java". + + - Fix for Bug#68664 (16486957), Enable packaging of .JAR file from Eclipse. + +03-05-13 - Version 5.1.24 + + - Fix for Bug#64204, ResultSet.close hangs if streaming query is killed. + + - Fix for Bug#16224249, Deadlock on concurrently used LoadBalancedMySQLConnection: + 1) abortInternal() method was moved from com.mysql.jdbc.MySQLConnection to com.mysql.jdbc.Connection interface; + 2) load-balanced/failover proxy now broadcasts abortInternal() to all underlying physical connections; + 3) load-balanced/failover proxy now prevents picking of new physical connection after close() or abortInternal() were called explicitly on proxy; + 4) connection synchronization mutex was refactored, now mutex is proxy instance for proxied connection or connection instance itself if there is no proxy. + + - Fix for Bug#64805, StatementImpl$CancelTask occasionally throws NullPointerExceptions. + + - Fixed typos in descriptions of properties. + + - Fix for Bug#68011, Invalid error message noDatetimeSync property instead of noDatetimeStringSync. + +02-04-13 - Version 5.1.23 + + - Fix for Bug#35653, executeQuery() in Statement.java let "TRUNCATE" queries being executed. "TRUNCATE" and "RENAME" are now filtered for executeQuery(). + + - Fix for Bug#65909, referenceThread causes memory leak in Tomcat. + Abandoned connection cleanup thread was refactored to have static shutdown method. + If you encountered this leak problem, your application should implement context listener with + AbandonedConnectionCleanupThread.shutdown() call in contextDestroyed method. + + For example: + @WebListener + public class YourThreadsListener implements ServletContextListener { + public void contextDestroyed(ServletContextEvent arg0) { + try { + AbandonedConnectionCleanupThread.shutdown(); + } catch (InterruptedException e) { + } + } + ... + } + + Note that if container does not support annotations you should add description to web.xml: + + user.package.YourThreadsListener + + + - Added tests for explicit partition selection syntax introduced in 5.6GA. + + - Added support of password expiration protocol. This introduces new boolean connection property disconnectOnExpiredPasswords. + If disconnectOnExpiredPasswords = true and password expired then connection will be rejected by server with ErrorCode == 1820 (ER_MUST_CHANGE_PASSWORD). + If disconnectOnExpiredPasswords = false then connection will enter to "sandbox" mode, + all commands except SET PASSWORD = ... and SET PASSWORD FOR CURRRENT_USER() = ... will cause an error to be thrown. + + - Added tests for EXCHANGE PARTITION syntax introduced in 5.6GA. + + - Added tests for transportable tablespaces syntax introduced in 5.6GA. + + - Added tests for CREATE TABLE syntax changed in 5.6GA: CREATE TABLE ... DATA DIRECTORY = 'absolute/path/to/directory/' + + - Added tests for ALTER TABLE syntax changed in 5.6GA: ALGORITHM and LOCK keywords. + + - Fix for Bug#67954, stack trace used for point-of-origin in log and exception messages + causes permgen leak with webapp classloader on application redeploy. We no longer store the entire + stack trace, only the calling class and method, and even then, that only when using the usage advisor + or when profiling. + + - Fix for Bug#11237, useCompression=true and LOAD DATA LOCAL INFILE SQL Command. + + - Static charset/collation maps were updated. + + - Fix for Bug#14260352, difference in Timestamp value returned with rewriteBatchedStatements=true. + + - Fix for Bug#60598, nativeSQL() truncates fractional seconds. + + - Fix for Bug#40279, Timestamp values get truncated when passed as prepared statement parameters. + This was partly fixed in 5.1.19 but that fix did not cover useLegacyDatetimeCode=true case. + + - Fix for Bug#14665141, Diff results returned from ResultSet and CachedRowSet with new password hashing. + Test suite modified to don't perform comparison of PASSWORD() results if old_passwords=2 + because with SHA-256 password hashing enabled they are nondeterministic. + + - The driver now allows the mechanism for caching MySQL server configuration values replaceable at runtime, + via the "serverConfigCacheFactory" property. The default is an implementation that is a per-VM concurrent + map, keyed by URL. The driver will invalidate cache entries when SQLExceptions that indicate communications + errors are thrown (on the assumption that the server has been or is restarting), or if the server version + that is being connected to, differs from the one that was present when the cached values were populated. + + To replace the default implementation, implement CacheAdapterFactory>, and + use the fully-qualified class name of this implementation for "serverConfigCacheFactory". + + - Connection.setReadOnly() will take advantage of server-side support for read-only transactions + present in MySQL-5.6 and newer. Calling .isReadOnly() will incur a round-trip if useLocalSessionState + is not enabled. + +09-06-12 - Version 5.1.22 + - Fix for Bug#57662, Incorrect Query Duration When useNanosForElapsedTime Enabled. + + - Fix for Bug#65503, ResultSets created by PreparedStatement.getGeneratedKeys() are not close()d. + + - Fix for Bug#63800, getVersionColumns() does not return timestamp fields; always empty. + Added support of ON UPDATE CURRENT_TIMESTAMP for TIMESTAMP and DATETIME fields. + + - Fix for Bug#41752, Can't connect mysqld which character_set_server=ucs2. + + - Fix for Bug#65508, getCharsetNameForIndex() should be faster. + + - Fix for Bug#14563127, Load-balanced connection fails to select valid host, closes connection + on re-balance. + +07-05-12 - Version 5.1.21 + - Added new built-in authentication plugin com.mysql.jdbc.authentication.Sha256PasswordPlugin + ("sha256_password"). + + - Fix for Bug#64731, StringUtils.getBytesWrapped throws StringIndexOutOfBoundsException. + + - Added new built-in authentication plugin com.mysql.jdbc.authentication.MysqlClearPasswordPlugin + ("mysql_clear_password"). It allows C/J based clients to connect to MySQL accounts which use + PAM authentication for example. SSL connection required for this authentication method. + If SSL is not enabled then authentication which requires "mysql_clear_password" will lead to an error. + + - Fix for Bug#13980303, Auth plugin's confidentiality requirements are not checked after Auth Switch Request. + + - Fix for Bug#64205, Connected through Connector/J 5.1 to MySQL 5.5, the error message is garbled. + + - Fix for Bug#37931, Null Pointer Exception Thrown When specifying invalid character_set_results enc. + + - Fix for Bug#36662, TimeUtil.java: MEST mapping n/a. + + - Fix a scalability/memory footprint issue where Object.finalize() was being used on + ConnectionImpl to clean up the low-level network connection to MySQL should a + connection be abandoned by the application before being cleanly close()d. We now + track connections in a phantom reference queue, and have a single thread per-vm + clean these up when the VM notices the connection is no longer referenced by + anything else. + + - Added the ability to add new client-side prepared statement parse info caches by + implementing com.mysql.jdbc.CacheAdapterFactory and telling the driver to use it + when "cachePrepStmts=true" via the "parseInfoCacheFactory" configuration property. + + - Implemented JDBC-4.1 methods from Java-7: + + - Connection.setSchema(String) - no-op, until we support database==schema in the driver + - Connection.getSchema() - see above + - Connection.abort(Executor executor) + - Connection.setNetworkTimeout(Executor, int) + - Connection.getNetworkTimeout() throws SQLException; + - CallableStatement.getObject(int, Class) + - CallableStatement.getObject(String, Class) + - DBMD.getPseudoColumns() - returns an empty result set + - DBMD.generatedKeyAlwaysReturned() - always true for MySQL + - ResultSet.getObject(int, Class) + - ResultSet.getObject(String, Class) + - Statement.closeOnCompletion() + - Statement.isCloseOnCompletion() + +05-02-12 - Version 5.1.20 + - Fix for Bug#64983, 5.1.19 not working with JBoss AS 4.2.3.GA. + + - Fix for Bug#13960556, java.lang.StringIndexOutOfBoundsException in com.mysql.jdbc.PreparedStatement.formatNanos(int nanos). + + - Fix for pluggable authentication tests to run on Windows. + + - Fix for Bug#13897714, NPE in testsuite.regression.StatementRegressionTest.testBug1933() with 5.6.5_m8 server. + + - Fix for Bug#55962, Savepoint identifier is occasionally considered as floating point numbers. + + - Fix for Bug#13955027, SET OPTION syntax was removed starting from 5.6.5 server version. + + - Fix for Bug#13958793, ClassCastException in ConnectionImpl.buildCollationMapping() with 4.1 server. + + - Fix for Bug#36478, Client prepared statement bugged if word 'limit' included in the query. + +04-02-12 - Version 5.1.19 + - Fix for Bug#64621, setMaxRows was not correctly processed during CS PS metadata + collection causing entire resultset to be fetched and possibly leading to OOM. + + - Fix for Bug#63456, MetaData precision is different when using UTF8 or Latin1 tables. + The problem was in finding maxBytesPerChar through versioned mapping from Java charset to MySQL charset. + That map returns "utf8mb4" instead "utf8" for server versions starting with 5.5.2. + CharsetMapping, ConnectionImpl and Field have been reorganized to use static maps INDEX_TO_MYSQL_CHARSET, + STATIC_CHARSET_TO_NUM_BYTES_MAP instead. Also dynamic maps ConnectionImpl.indexToCustomMysqlCharset + and ConnectionImpl.mysqlCharsetToCustomMblen have been added for custom charsets. + + - Added support for pluggable authentication via the com.mysql.jdbc.AuthenticationPlugin + interface (which extends standard "extension" interface). Examples are in + com/mysql/jdbc/authentication and in testsuite.regression.ConnectionRegressionTest. + This introduces three new properties: + + authenticationPlugins defines comma-delimited list of classes that implement + com.mysql.jdbc.AuthenticationPlugin and which will be used for authentication + unless disabled by "disabledAuthenticationPlugins" property. + + disabledAuthenticationPlugins defines comma-delimited list of classes implementing + com.mysql.jdbc.AuthenticationPlugin or mechanisms, i.e. "mysql_native_password". + The authentication plugins or mechanisms listed will not be used for authentication + which will fail if it requires one of them. It is an error to disable the default + authentication plugin (either the one named by "defaultAuthenticationPlugin" property + or the hard-coded one if "defaultAuthenticationPlugin" propery is not set). + + defaultAuthenticationPlugin defines name of a class implementing + com.mysql.jdbc.AuthenticationPlugin which will be used as the default authentication + plugin. It is an error to use a class which is not listed in "authenticationPlugins" + nor it is one of the built-in plugins. It is an error to set as default a plugin + which was disabled with "disabledAuthenticationPlugins" property. It is an error + to set this value to null or the empty string (i.e. there must be at least a valid + default authentication plugin specified for the connection, meeting all constraints + listed above). + + - Fix for Bug#63526. The problem happens in com.mysql.jdbc.EscapeProcessor#escapeSQL. The function recognizes the string in the create table statement as an escape sequence (line 136+138). The "if" construct beginning in line 182 tries to match a white-space collapsed version of the string to prefixes for valid jdbc-escapes (till line 300). Since no matching escape sequence is found and no "else" clause is defined, neither the token, nor replacement are added to the resulting escaped SQL string. + + - Fix for Bug#61203, noAccessToProcedureBodies does not work anymore. + + - Fix for Bug#63811, pointless Socket.bind() when using ephemeral ports and interfaces, which limits scalability on some platforms. + + - Connection.changeUser() would not check for closed connections, leading to NPEs when this method was called on a closed connection. + + - Fix for Bug#63284, memory leak with Failover proxied Statement/PreparedStatement with DBCP due to improper implementation of equals(). + + - Prepared statements would needlessly allocate a 4K buffer for converting + streams when no set*Stream() methods had been used. + +10-03-11 - Version 5.1.18 + + - Fix for Bug#12565726, not putting the space between VALUES() and ON DUPLICATE KEY UPDATE + causes C/J a) enter rewriting the query although it has ON UPDATE + and b) to generate the wrong query with multiple ON DUPLICATE KEY + + - Fix for Bug#12784170, "process fork failure" errors while running test suite via ant on Windows. + Added new ant flag, com.mysql.jdbc.junit.fork, which controls whether JUnit will fork new processes + for testing ("on", default and legacy behavior) or not ("off", required for Windows). + + - Reverting changes made to ConnectionImpl.java, + private boolean characterSetNamesMatches function. + + - Added function MYSQL_INDEX_TO_MYSQL_CHARSET to retrieve server charset name + using index instead of parsing variables to CharsetMapping.java. + + - Completed fix for Bug#61201/12649557, fixed tests failures. + + - Fix for Bug#61201/12649557, Can't establish connection when url has + sessionVariables and characterEncoding. Fix covers only MySQL server 4.1+ + + - Fix for Bug#61501 - Calling Statement.cancel() on a statement that isn't + currently executing will cause some later-executed query on the same + connection to be cancelled unexpectedly. The driver now guards against this + condition, but it is an underlying server issue. The MySQL statement "KILL QUERY" + (which is what the driver uses to implement Statement.cancel()) is rather + non-deterministic, and thus the use of Statement.cancel() should be avoided + if possible. + + - Fix for Bug#61866/12791594 - Calling Statement.getWarnings() after + Statement.clearWarnings() has been called, returns the "old" warnings. + + - Fix for Bug#13036537 - LRUCache was really a least-recently-added cache. + + - Fix for Bug#13036309, Correcting parameter name in maxPerformance.properties. + + +07-04-11 - Version 5.1.17 + + - Fix for Bug#61332 - LIKE not optimized in server when run against I__S tables and no wildcards used. + Databases/tables with "_" and/or "%" in their names (escaped or not) will be handled by this code path, + although slower, since it's rare to find these characters in table names in SQL. If there's a "_" or "%" + in the string, LIKE will take care of that, otherwise we now use = . The only exception is + information_schema database which is handled separately. Patch covers both getTables() and getColumns(). + + - Fix for Bug#61150 - First call to stored procedure fails with "No Database Selected". + The workaround introduced in DatabaseMetaData.getCallStmtParameterTypes to fix + the bug in server where SHOW CREATE PROCEDURE was not respecting lower-case table names + is misbehaving when connection is not attached to database and on non-casesensitive OS. + + - Fix for Bug#61105 - Avoid a concurrent bottleneck in Java's character set + encoding/decoding when converting bytes to/from Strings. + +04-21-11 - Version 5.1.16 + + - Partial fix for BUG#54135 - setQueryTimeout unsafe across VIP. Fix prevents c/J from + killing the right ConnectionID but on wrong server. + + - Fix for BUG#57808 - wasNull not set for DATE field with value 0000-00-00 + in getDate() although zeroDateTimeBehavior is convertToNull. + + - Fix for Bug#54425 - Bypassing the server protocol bug where DB should be null-terminated + whether it exists or not. Affects COM_CHANGE_USER. + + - Fix for Bug#60313 (11890729), bug in + com.mysql.jdbc.ResultSetRow.getTimestampFast(). + + - Fix for bug 11782297, DBMD.getTables (so thus getColumns too) fails with + table names containing dot (like "junk_[Sp:e,c/ C-h+a=.r]"). + + - Added the ability to determine if the connection is against a server on the + same host via the Connection.isServerLocal() method. + + - Fix for bug 12325877, Setting "autoReconnect=true" and + "cacheServerConfiguration=true" would cause connections created after + an existing connection fails to have non-existent values for server + variables which lead to exceeding of max allowed packet exceptions when the + new connections were used. + +02-08-11 - Version 5.1.15 + + - Fix for Bug#38367, parameters metadata did not reflect the fact that NULL is allowed + parameter value. So DatabaseMetaData.getProcedureColumns will set isNullable member to + java.sql.DatabaseMetaData.procedureNullable now. + + - Completed fix for Bug#27916. + + - Fix for Bug#59224, adding 5.5 reserved words to DatabaseMetaData.getSQLKeywords(). + + - Fixed an issue where statement comments set via Connection.setStatementComment() + weren't represented in autoGenerateTestcaseScript=true output. + + - Added ability to include the current java thread dump in the exception message + given for deadlock/wait lock timeout exceptions, enable with + "includeThreadDumpInDeadlockExceptions=true" in your JDBC url. + + - Added ability to include current thread name as a statement comment visible + in MySQL's "SHOW PROCESSLIST" and Innodb deadlock diagnostics, enable with + "includeThreadNamesAsStatementComment=true". + + - Added an SLF4J logging adapter. Enable by adding setting the connection + property "logger" to "Slf4JLogger" and placing the appropriate bridge + from SLF4J to the logging framework of choice in your CLASSPATH. As with + other Connector/J logging adapters, the log category name used by the + driver is "MySQL". See http://www.slf4j.org/manual.html for more details. + +12-06-10 - Version 5.1.14 + + - Fix for Bug#58728, NPE in com.mysql.jdbc.jdbc2.optional.StatementWrappe.getResultSet() + if rs is null. Regression test case added to Statement regression tests. + + - Fix for Bug#58751, DatabaseMetadata.getIndexInfo() CARDINALITY now clamped + to Integer.MAX_VALUE. + + - Fix for BUG#58590 + - Testsuite.Simple.DateTest, MetadataTest, NumbersTest and StatementsTest cleaned and fixed. + + - Testsuite.simple, ConenctionTest & DataSourceTest are up to date. Major rework on + ConnectionTest.testDeadlockDetection (Sveta) and testUseCompress. + + - Testsuite.simple, CallableStatementTest & CharsetTests are up to date. + + - Testsuite.regression SubqueriesRegressionTest and StringRegressionTest are up to date. + + - Testsuite.regression MicroPerformanceRegressionTest, NumbersRegressionTest, PooledConnectionRegressionTest, + ResultSetRegressionTest are up to date. + + - Testsuite.regression.MetaDataRegressionTest up to date. + + - Typo in StatementRegressionTest.testLikeWithBackslashes fixed. StatementRegressionTest + is up to date. + + - Fix for Bug#58232 - CallableStatement fails to fetch OUT parameter against 5.5 server + + - Testsuite.regression.Connection, tests for BUG#45419 refined by Todd so not to cause failures. + + - Testsuite.regression.CallableStatement, tests for BUG#26959 failing against 5.5+ server. + + - Bringing testsuite.regression.CachedRowsetTest up to date. + + - Bringing BLOBregression tests up to date. + + - Fix for Bug#58042 - Statements test failure not handled. + + - Fix for Bug#57850 - Refresh SELECT statement doesn't use correct data type. + Added Field.valueNeedsQuoting (private final boolean) and protected boolean getvalueNeedsQuoting(). + UpdatableResultSet refresher and updater call upon this value now. + + - Removing commented source in fix for Bug#57697 + - Fix for Bug#57697 - Metadata getTables() was not checking for table_name already been quoted. + - Fix for Bug#57694 - 3byte UTF8 can not be used with 5.5.3+ server. + - Fix for Bug#57701 - StatementsTest.testBatchRewriteErrors() failing on new servers. + + - Fix for Bug#54756 - Cannot retrieve data from ResultSet by column name from a Sphinx daemon. + We were relying only on "server version string" passed. Now, determining + server version is done via protocol flags too, where applicable. + + - Fix for Bug#57022 - cannot execute a store procedure with output parameters, + database parameter was ignored in db.sp notation. The fix is to "sanitize" + db.sp call just like in patch for noAccessToProcedureBodies. BaseTestCase + extended with createDatabase and dropDatabase. Regression test added. + + - Fix for Bug#57262 - "useOldUTF8Behavior" behavior was broken since 5.1.3, + now explicitly sets connection character set to latin1 ("SET NAMES latin1") + during connection post-handshake process. + + - Patch for problem where "noAccessToProcedureBodies=true" was causing + "underprivileged" user not to have access to procedures created by him. + + - Patch for Bug#56305, unhandled NPE in DatabaseMetaData.java when calling + wrong-cased function without access to mysql.proc. Although simple by + itself, some more enhancements were needed for everything to function + properly. So, along with catching potential NPE due to server bug, a + guard against calling JDBC functions with db_name.proc_name notation was + also added. Necessary changes added to StringUtils.java too. + + - Added ability to load-balance while auto-commit is enabled. This + introduces two new properties: + + loadBalanceAutoCommitStatementThreshold defines the number of matching + statements which will trigger the driver to (potentially) swap physical + server connections, + + loadBalanceAutoCommitStatementRegex defines the regular expression + against which statements must match. The default values (0 and blank, + respectively) retain the previously-established behavior that + connections with auto-commit enabled are never balanced. Feature + request documented in Bug#55723. + + - Minor fix in getProcedureColumns() DisplaySize for Bug#51712. Fix for + Bug#41269 is not complete without this. getColumnDisplaySize on a + ResultSet already consisting of metadata is now functional thanks to + Bogdan. + + - Minor fix for Bug#55217, return 4 as a result of DataBaseMetadata.getJDBCMajorVersion() as per manual. + + - Added support for hosts specified in the URL of the form: + address=(key=value), supported keys are: + + (protocol=tcp or pipe (for named pipes on Windows) + (path=[] for named pipes) + (host=[]) for TCP connections + (port=[]) for TCP connections + + An example would be: + + jdbc:mysql://address=(protocol=tcp)(host=localhost)(port=3306)(user=test)/db + + Any other parameters are treated as host-specific properties that follow + the conventions of the JDBC URL properties. This now allows per-host + overrides of any configuration property for multi-host connections + (failover, loadbalance, replication). We do recommend that the overrides + are limited to user, password, network timeouts and statement and + metadata cache sizes. Unexpected behavior may be observed with other + per-host overrides. + + - Fix for Bug#56099 - Added support for JDBC4-specific functionality when + using load-balanced connections. + + - Fix for Bug#56200 - Added diagnostic information to SQLException message + thrown when a closed load-balanced connection is reused. This + information will identify the conditions which caused the connection to + be closed. + + - Fix for Bug#56429 - When using Connector/J configured for failover + (jdbc:mysql://host1,host2,... URLs), the non-primary servers re-balance + and spawned new idle connections when the transactions on the master were + committed or rolled-back, eventually exceeding max_connections. It was + also discovered that session state (autocommit, isolation level, catalog) + wasn't being copied from the primary connection to secondary + connections correctly because of the same changes that caused this bug, + and this was fixed as well. + + - Fix for Bug#56706 - Ensure read-only state is synchronized when new + load-balanced connections are selected. + + - Fixed Bug#56955 - Connection properties "trustCertificateKeyStoreType" + and "clientCertificateKeyStoreType" have invalid defaults, therefore + connections that specify "useSSL" will sometimes fail with exceptions + from JSSE unless "JKS" has been specified for both of these properties. + The default value for these properties is now "JKS", and thus it no + longer has to be specified. + + - Fixed Bug#56979 - Improper connection closing logic leads to TIME_WAIT + sockets on server + + - Fixed Bug#57380 - DatabaseMetaData.supportsMultipleResultSets() now returns + true when connected to a 4.1 version or later server. + + - Fixed Bug#58706 - Failover connections didn't honor "failOverReadOnly=false", and in some + situations would not fall back. + + - Removed logging integrations with log4j and apache-commons-logging due to license + incompatibility. Replacing with SLF4J integration in next release. + +06-24-10 - Version 5.1.13 + + - Minor fix in previous patch for Bug#51904. Function ConnectionImpl.setCatalog() was passed quoted argument thus breaking with "...for the right syntax to use near 'test``'" + + - Fix for Bug#51912 - Passing NULL as cat. param to getProcedureColumns with !nullCatalogMeansCurrent + + - Fix for Bug#52167 - Can't parse parameter list with special characters inside + + - Fix for Bug#51904 - getProcedureColumns() always returns PROCEDURE_CAT result column as NULL + + - Fix for Bug#51712 - Display Size is always 0 for columns returned by getProcedureColumns() + + - Fix for Bug#51908 - db variable might have end up unassigned when calling + getProcedureColumns()/Functions(). This is a followup on code changes made + for Bug#51022. + + - Fixed Bug#51266 - jdbc:mysql:loadbalance:// would stick to the first + host in the list in some cases, especially exacerbated if the host was + down. + + - Replaced URLs of the form jdbc:mysql://host-1,host-2 with a composite of + a normal connection and a jdbc:mysql:loadbalance:// connection for more + robustness and cleaner code. + + - Fixed BUG#51643 - Connections using jdbc:mysql:loadbalance:// would + have statements (and prepared statements) that did not have their connections + changed upon commit()/rollback(), and thus applications that held statement + instances past commit()/rollback() could have data written to or read from + un-intended connections. + + - Fixed BUG#51666 - StatementInterceptors were never "un-safed" after connection + establishment, causing interceptors which returned result sets pre/post execution + would not work. + + - Fixed BUG#51783 - Load-balanced connections could throw a SQLException + incorrectly on commit() or rollback(). This was not caused by failures in commit + or rollback, but rather by the possibility that the newly-selected physical + connection was stale. Added logic to catch and retry if this happens, up to + the number of hosts specified for load-balancing. Also added new property, + loadBalanceValidateConnectionOnSwapServer, which controls whether to explicitly + ping the selected host (otherwise, the host is presumed to be up, and will only + be noticed if auto-commit or transaction isolation state needs to be set and + fails). + + - Added loadBalancePingTimeout property to allow a specific timeout to be set + for each ping executed against the servers. This ping is executed when the + physical connections are rebalanced (commit/rollback or communication exception), + or when a query starting with (exactly) "/* ping */" is executed. The latter + causes each open underlying physical connection to be pinged. + + - Fixed BUG#51776 - Connection.rollback() could swallow exceptions incorrectly. + + - Fixed BUG#52231 - Differences in definitions of which SQLExceptions trigger + a failover event could result in failure to try more than a single host in + certain situations. + + - Fixed BUG#52534 - Performance regression using load-balanced connection. + + - More aggressively purge the statement timeout timers after they've been cancelled to + trade time for memory. This purge only happens if statement timeouts are in use. + + - Added management of running load-balanced connections. Statistics can be obtained, + and hosts added/dropped via com.mysql.jdbc.ConnectionGroupManager or the JMX + implementation. This functionality is enabled by setting the new paramenter, + loadBalanceConnectionGroup to the name of the logical grouping of connections. + All load-balanced connections sharing the same loadBalanceConnectionGroup value, + regardless of how the application creates them, will be managed together. To + enable JMX-based management, set loadBalanceEnableJMX=true and ensure that remote + JMX is enabled in the JRE (eg, use -Dcom.sun.management.jmxremote). + + - Added loadBalanceExceptionChecker property, which takes a fully-qualified class + name implementing com.mysql.jdbc.LoadBalancedExceptionChecker interface. This + allows custom evaluation of SQLExceptions thrown to determine whether they should + trigger failover to an alternate host in load-balanced deployments. The default + is com.mysql.jdbc.StandardLoadBalanceExceptionChecker. + + - Added two new properties which allow more flexibility in determining which + SQLExceptions should trigger failover in a load-balanced deployment. The new + loadBalanceSQLStateFailover property takes a comma-delimited list of SQLState + codes which are compared to the SQLState of the SQLException (matching done + with trailing wildcard), while loadBalanceSQLExceptionSubclassFailover takes + a comma-delimited list of fully-qualified class/interface names, against + which the SQLException is checked to determine if it is an instance of any. + Matches trigger failover to an alternate host. + + - Fixed Bug#51704 - Re-written batched statements don't honor escape processing + flag of their creator. + + - Fixed Bug#43576 - Sometimes not able to register OUT parameters for + CallableStatements. + + - Fixed Bug#54175 - Driver doesn't support utf8mb4 for servers 5.5.2 and newer. The + driver now auto-detects servers configured with character_set_server=utf8mb4 or + treats the Java encoding "utf-8" passed via "characterEncoding=..." as utf8mb4 in + the "SET NAMES=" calls it makes when establishing the connection. + +02-18-10 - Version 5.1.12 + + - NO_INDEX_USED and NO_GOOD_INDEX used were only being set when profileSQL + was set to "true", and in some cases their values were reversed. + + - Fix for Bug#51022 - conn.getMetaData().getProcedures("schema",null,"%"); + returns all stored procedures from all databases and not only for given + one. + + - Fixed Bug#50538 - ${svn.revno} shows up in DBMD.getDriverVersion(). + + - Removed usage of timestamp nanoseconds in PreparedStatement.setTimestamp(), + as long as Bug#50774 exists in the server and there's no real support + for nanos/micros in TIMESTAMPs, avoid the performance regression usage of + them causes. + + +01-20-10 - Version 5.1.11 + + - Fix for BUG#50288 - NullPointerException possible during invalidateCurrentConnection() for load-balanced + connections. + + - Fix for BUG#49745 - deleteRow() for updatable result sets can cause full table scan because escaped hex + values are used for primary key identifiers. + + - Fix for BUG#49607 - Provide Connection context in ExceptionInterceptor. + + - Fix for BUG#48605 - Ping leaves closed connections in liveConnections, causing subsequent Exceptions when + that connection is used. + + - Fix for BUG#48442 - Load-balanced Connection object returns inconsistent results for hashCode() and equals() + dependent upon state of underlying connections. + + - Fix for BUG#48172 - Batch rewrite requires space immediately after "VALUES" + + - Statement Interceptors didn't completely intercept server-side prepared statements. + + - Fix for BUG#48486 Cannot use load balanced connections with MysqlConnectionPoolDataSource. + + - Fix for Bug#32525 - "noDatetimeStringSync" doesn't work for server-side prepared statements. Now it does. + + - Hooked up exception interceptors so they get called now. + + - Rev'd the statement interceptor interface to pass on some server flags, warning counts and errors. See + the com.mysql.jdbc.StatementInteceptorsV2 interface for more details. The driver will create adaptors to + transparently convert older implementations to the newer interface at runtime. + + - Statement Interceptors are now enabled at connection instantiation, but + can not return result sets (they will be ignored) until the connection + has bootstrapped itself. If during the init() method your interceptor + requires access to the connection itself, it should ensure that methods + that might throw exceptions if the connection is closed should handle + this in a robust manner. + + - "Replication" connections (those with URLs that start with + jdbc:mysql:replication) now use a jdbc:mysql:loadbalance connection + under the hood for the slave "pool". This also means that one can set + load balancing properties such as "loadBalanceBlacklistTimeout" and + "loadBalanceStrategy" to choose a mechanism for balancing the load and + failover/fault tolerance strategy for the slave pool. This work was done + in order to fix Bug#49537. + + - Fixed Bug#36565 - permgen leak from java.util.Timer. Unfortunately no great + fix exists that lets us keep the timer shared amongst connection instances, so + instead it's lazily created if need be per-instance, and torn down when the + connection is closed. + + - Fixed BUG#49700 - Connections from ConnectionPoolDataSource don't + maintain any values set with "sesssionVariables=...". This was a bug + in Connection.changeUser()/resetServerState(), we now resubmit the + session variables during the execution of these methods. + +09-22-09 - Version 5.1.10 + + - Fix for BUG#47494 - Non standard port numbers in the URL are not honored. + +09-16-09 - Version 5.1.9 + + - The driver has been OSGi-ified. The bundle symbolic name is "com.mysql.jdbc", see META-INF/MANIFEST.MF to see + what interfaces we export. + + - Fixed BUG#45040, adding missing tags from SVN import to BZR branch for + 5.1. + + - Fix for a variant of Bug#41484 - ResultSet.find*(String) failed when using cached result set + metadata. + + - Fixed BUG#46637 - When the driver encounters an error condition that causes it to create a + CommunicationsException, it tries to build a friendly error message that helps diagnose + what is wrong. However, if there has been no network packets received from the server, + the error message contains bogus information like: + + "The last packet successfully received from the server was 1,249,932,468,916 milliseconds ago. + The last packet sent successfully to the server was 0 milliseconds ago." + + Now the error message states that it has never received any packets from the server in this + scenario. + + - Added a new option, "queryTimeoutKillsConnection", when set to "true" will cause timeouts set + by Statement.setQueryTimeout() to forcibly kill the connection, not just the query. + + - Fixed BUG#32216, "PORT" property filled in by Driver.parseURL() not always present. The driver + will now always fill in the "PORT" (using 3306 if not specified) property, and the "HOST" property + (using "localhost" if not specified) when parseURL() is called. The driver also parses a list of hosts + into HOST.n and PORT.n properties as well as adding a property "NUM_HOSTS" for the number of hosts + it has found. If a list of hosts is passed to the driver, "HOST" and "PORT" will be set to the + values given by "HOST.1" and "PORT.1" respectively. This change has centralized and cleaned up a large + swath of code used to generate lists of hosts, both for load-balanced and fault tolerant connections and + their tests. + + - Fixed the ResultSet side of BUG#23584 - Calendar discared when retrieving dates from server-side prepared + statements. The other cases of this bug were fixed when "useLegacyDatetimeCode=false" became the default. + + - Fixed Bug#44324 - Data truncation exceptions did not return the vendor error code from the server. Note that + the vendor error code is not hard-coded to 1265 as in the bug report, because the server returns different + error codes for different types of truncations, and we did not want to mask those. + + - Fixed Bug#27431 - ResultSet.deleteRow() advances the cursor. The driver now places the cursor on the prior + row in the result set, or before the start of the result set if the result set is empty after the deletion. + + - Fixed Bug#43759 - ResultSet.deleteRow() generates corrupt DELETE statement for primary keys with binary + data. + + - Fixed Bug#46925 - Suspendable XA connections were not pinned to the XID for the global transaction, leading + to failure when attempting to suspend/resume/commit from different logical XA connections. + + - Fixed Bug#44508 - DatabaseMetadata.getSuperTypes() returns result set with incorrect column names. + + - Fixed Bug#46788 - Batched prepared statements with ON DUPLICATE KEY UPDATE are rewritten incorrectly when + when there are parameters as part of the UPDATE clause. Statements of this form can not be rewritten + as multi-value INSERTs so they are rewritten into multi-statements instead. + +07-16-09 - Version 5.1.8 + - Fixed BUG#44588 - Fixed error message for connection exceptions when + streaming result sets are used. + + - Modified/fixed test cases using UnreliableSocketFactory. + + - Fixed BUG#43421 - Made doPing() global blacklist-aware, so that it does not + throw Exceptions when at least a single load-balanced server is available. + + - Fixed BUG#43071 - Specifying ASCII encoding for converting seed String to + byte array; allowing system default encoding to be used causes auth failures + on EBCDIC platforms. + + - Fixed BUG#43070 - traceProtocol parameter isn't configured early enough to + capture handshake protocol. + + - Fixed BUG#41161 - PreparedStatement.addBatch() doesn't check for all parameters + being set, which leads to a NullPointerException when calling executeBatch() and + rewriting batched statements into multi-value or multi-statement statements. + + - Fixed BUG#42055 - ConcurrentModificationException possible when removing items + from global blacklist. + + - Fixed Bug #42309 - Statement.getGeneratedKeys() returns 2 keys when + using ON DUPLICATE KEY UPDATE + + - Fixed some quoting of substituted parameter issues in localized error messages. + + - Added a version check around getting the variable 'auto_increment_increment' for + servers < 5.0.2, which quiets down a warning message that the driver would log + when connecting to MySQL-4.1 or older. + + - The driver will automatically disable elideSetAutoCommit and useLocalTransactionState + if it detects a MySQL server version older than 6.0.10 with the query cache enabled, due + to Bug#36326 which can cause the server to report bogus transaction state. + + - Fixed a performance regression (Bug#41532) in rewritten batched inserts when "ON DUPLICATE KEY" + was present. + + Fixes include an improvement to token searching in the statement, and the ability for the driver + to rewrite prepared statements that include "ON DUPLICATE KEY UPDATE" into multi-valued inserts as + long as there is no use of LAST_INSERT_ID() in the update clause (as this would render + getGeneratedKey() values incorrect). + + - Fixed Bug#44056 - Statement.getGeneratedKeys() retains result set instances until statement is closed, + thus causing memory leaks for long-lived statements, or statements used in tight loops. + + - Fixed issues with server-side prepared statement batch re-writing caused by the fix to Bug#41532. + Rewriting of batched statements now works the same between normal prepared statements and server-side + prepared statements. + + - Fixed Bug#44862 - getBestRowIdentifier does not return resultset as per JDBC API specifications + + - Fixed Bug#44683 - getVersionColumns does not return resultset as per JDBC API specifications + + - Fixed Bug#44865 - getColumns does not return resultset as per JDBC API specifications + + - Fixed Bug#44868 - getTypeInfo does not return resultset as per JDBC API specifications + + - Fixed Bug#44869 - getIndexInfo does not return resultset as per JDBC API specifications + + - Fixed Bug#44867 - getImportedKeys/exportedKeys/crossReference doesn't have correct type for DEFERRABILITY + + - Fixed Bug#41730 - SQL Injection when using U+00A5 and SJIS + + - Fixed Bug#43196 - Statement.getGeneratedKeys() doesn't return values for UNSIGNED BIGINTS with values > Long.MAX_VALUE. + Unfortunately, because the server doesn't tell clients what TYPE the auto increment value is, the driver can't consistently + return BigIntegers for the result set returned from getGeneratedKeys(), it will only return them if the value is > Long.MAX_VALUE. + If your application needs this consistency, it will need to check the class of the return value from .getObject() on the + ResultSet returned by Statement.getGeneratedKeys() and if it's not a BigInteger, create one based on the java.lang.Long that + is returned. + + - Fixed Bug#38387 - "functionsNeverReturnBlobs=true" now works for SQL functions that return binary/binary collation VAR_STRINGS. + + - Fixed Bug#45171 - Connection.serverPrepareStatement() returns wrong default result set types + + - Fixed Bug #43714 - useInformationSchema with + DatabaseMetaData.getExportedKeys() throws exception + + - Fixed Bug #42253 - multiple escaped quotes cause exception from + EscapeProcessor + + - Fixed Bug #41566 - Quotes within comments not correctly ignored by + statement parser + + - Fixed Bug #41269 - DatabaseMetadata.getProcedureColumns() returns + wrong value for column length + + - Fixed Bug #40439 - Error rewriting batched statement if table name + ends with "values". + + - Fixed Bug #41484 Accessing fields by name after the ResultSet is closed throws + NullPointerException. + + - Fixed Bug #39426 - executeBatch passes most recent PreparedStatement params + to StatementInterceptor + + - Support use of INFORMATION_SCHEMA.PARAMETERS when "useInformationSchema" is set "true" and the view exists + for DatabaseMetaData.getProcedureColumns() and getFunctionColumns(). + + - When "logSlowQueries" is set to "true", and the driver has made a connection to a server that has suport + for the SERVER_QUERY_WAS_SLOW flag in the protocol, the query will be logged if the server indicates the + query has passed the slow query threshold. + + - Added new property, "maxAllowedPacket" to set maximum allowed packet size to + send to server. + +10-22-08 - Version 5.1.7 + - Fixed BUG#33861 - Added global blacklist for LoadBalancingConnectionProxy and + implemented in RandomBalanceStrategy and BestResponseTimeBalanceStrategy. + Added new property, "loadBalanceBlacklistTimeout", to control how long a + server lives in the global blacklist. + + - Fixed BUG#38782 - Possible IndexOutOfBoundsException in random load balancing + strategy. + + - Fixed BUG#39784 - invalidateCurrentConnection() does not manage global blacklist + when handling connection exceptions. + + - Fixed BUG#40031 - Adding support for CallableStatement.execute() to call + stored procedures that are defined as NO SQL or SQL READ DATA when failed + over to a read-only slave with replication driver. + + - Fixed BUG#35170- ResultSet.isAfterLast() doesn't work with for + streaming result sets. + + - Fixed BUG#35199 - Parse error for metadata in stored function. + + - Fixed BUG#35415 - When result set is from views without access to underlying + columns and is opened with CONCUR_UPDATABLE, don't throw SQLExceptions when + checking updatability due to access permissions, instead return + CONCUR_READONLY from getConcurrency. + + - Fixed BUG#35666 - NullPointerException when using "logSlowQueries=true" with + server-side prepared statements enabled. + + - Fixed BUG#35660 - Calling equals() on connections created with "jdbc:mysql:loadbalance:" + URLs did not have the same behavior as "plain" connections. The behavior we use + is the implementation in java.lang.Object, load-balanced connections just happened + to be using a java.lang.reflect.Proxy which required some custom behavior in + equals() to make it work the same as "plain" connections. + + Note that there is no *specified* equals contract for JDBC connections in the + JDBC specification itself, but the test makes sure that our implementation is + at least consistent. + + - Fixed BUG#35810 - Properties set in URLs and then passed to DataSources via setUrl() + did not take effect in certain circumstances. This also fixes related bugs BUG#13261 and + BUG#35753. + + - Fixed BUG#36051 - ResultSet.getTime() won't accept value of '24' for hours component of + a java.sql.Time. + + - Fixed BUG#36830 - DBMD.getColumns() doesn't return correct COLUMN_SIZE for SET columns. The + logic wasn't accounting for the ","s in the column size. + + - Fixed BUG#35610, BUG#35150- ResultSet.findColumn() and ResultSet.get...(String) doesn't allow + column names to be used, and isn't congruent with ResultSetMetadata.getColumnName(). + + By default, we follow the JDBC Specification here, in that the 4.0 behavior + is correct. Calling programs should use ResultSetMetaData.getColumnLabel() to dynamically determine + the correct "name" to pass to ResultSet.findColumn() or ResultSet.get...(String) whether or not the + query specifies an alias via "AS" for the column. ResultSetMetaData.getColumnName() will return the + actual name of the column, if it exists, and this name can *not* be used as input to ResultSet.findColumn() + or ResultSet.get...(String). + + The JDBC-3.0 (and earlier) specification has a bug, but you can get the buggy behavior + (allowing column names *and* labels to be used for ResultSet.findColumn() and get...(String)) by setting + "useColumnNamesInFindColumn" to "true". + + - Fixed BUG#35489 - Prepared statements from pooled connections cause NPE when closed() under JDBC-4.0. + + - Added connection property "useLocalTransactionState" which configures if the driver use the in-transaction + state provided by the MySQL protocol to determine if a commit() or rollback() should actually be sent to the database. + (disabled by default). + + - Use socket timeouts for JDBC-4.0's Connection.isValid(int timeout) instead of timer tasks, for scalability. As a side effect + internally, any communications with the database can use a timeout different than the configured timeout, but this isn't currently + used. + + - The number and position of columns for "SHOW INNODB STATUS" changed in MySQL-5.1, which caused the + "includeInnodbStatusInDeadlockExceptions" feature to not show data about the deadlock. + + - Implemented support of INFORMATION_SCHEMA for DatabaseMetadata.getTables() (views there are available as "SYSTEM TABLE"), and thus + also made INFORMATION_SCHEMA tables available via DatabaseMetadata.getColumns(). + + - Fixed BUG#39352, "INSERT ... ON DUPLICATE KEY UPDATE" doesn't return "0" for un-affected rows. This requires the driver to not + send the "CLIENT_FOUND_ROWS" flag to the server when it connects if the connection property "useAffectedRows" is set to "true", + which breaks JDBC-compliance, but currently there is no other way to get correct return values from the server. + + - Fixed BUG#38747 - ResultSets in "streaming" mode throw an exception when closed when the connection is set as "read-only". + + - Fixed BUG#37570 - Can't use non-latin1 passwords. Added connection property "passwordCharacterEncoding". Leaving this set to + the default value (null), uses the platform character set, which works for ISO8859_1 (i.e. "latin1") passwords. For passwords + in other character encodings, the encoding will have to be specified with this property, as it's not possible for the driver to + auto-detect this. + + - Fixed BUG#39911 - We don't retrieve nanos correctly when -parsing- a string for a TIMESTAMP. MySQL itself doesn't support micros + or nanos in timestamp values, but if they're stored as strings, historically we try and parse the nanos portion as well. + Unfortunately we -interpreted- them as micros. This fix includes correcting that behavior, and setting the milliseconds portion of + such TIMESTAMPs to a correct value as well. + + - Fixed BUG#39962 - ResultSet.findColumn() is slow for applications that call it too often (we're looking at -you- Hibernate). We're + using TreeMaps to get case-insensitive comparisons (required for JDBC compliance), but they can be slower than hash maps, so using the + approach Alex Burgel points out in this bug seems to help. + + - Fixed BUG#39956 - Statement.getGeneratedKeys() doesn't respect the 'auto_increment_increment' value. We now grab the *session-scoped* + value, and use that. Beware that using "cacheServerConfig=true" will cause us to cache this value, so new connections won't see changes + that are applied via something like "init-sql". + + - Fixed BUG#39611 - ReplicationConnection never sends queries to last host in slave list. + + - Fixed BUG#34185 - Statement.getGeneratedKeys() does not raise exception when statement was not + created with Statement.RETURN_GENERATED_KEYS flags. + + - Using autoGenerateTestcaseScript=true now logs all statements, regardless or not if they cause errors when processed by MySQL. + A "clock" value (millis since epoch) was added in the comment that is pre-pended with the idea that it can then be used + when post-processing output to sequence things correctly for a multi-threaded testcase, or to replay the test case with the + correct think times. + +03-06-08 - Version 5.1.6 + + - JDBC-4.0-ized XAConnections and datasources. + + - Fixed BUG#31790 MysqlValidConnectionChecker + doesn't properly handle ReplicationConnection + + - Fixed Bug#20491 - DatabaseMetadata.getColumns() doesn't + return correct column names if connection character set + isn't UTF-8. (There was a server-side component of this that + was fixed late in the 5.0 development cycle, it seems, this + is the last piece that fixes some loose ends in the JDBC + driver). This fix touches *all* metadata information coming + from the MySQL server itself. + + - Fixed MysqlIO.nextRowFast() to only attempt to read server + warning counts and status if talking to a 4.1 or newer server + (fixes a hang when reading data from 4.0 servers). + + - Made profiler event handling extensible via the "profilerEventHandler" + connection property. + + - Fixed Bug#31823 - CallableStatement.setNull() on a stored function would + throw an ArrayIndexOutOfBounds when setting the last parameter to null when calling setNull(). + + - Added SSL-related configuration property "verifyServerCertificate". If set to "false", the driver won't verify + the server's certificate when "useSSL" is set to "true". + + When using this feature, the keystore parameters should be specified by the + "clientCertificateKeyStore*" properties, rather than system properties, as the JSSE doesn't + make it straightforward to have a non-verifying trust store and the "default" key store. + + - Fixed ResultSetMetadata.getColumnName() for result sets returned from + Statement.getGeneratedKeys() - it was returning null instead of + "GENERATED_KEY" as in 5.0.x. + + - More applicable fix for the "random" load balance strategy in the face + of node non-responsive, it re-tries a *different* random node, rather + than waiting for the node to recover (for BUG#31053) + + - Fixed BUG#32577 - no way to store two timestamp/datetime values that happens + over the DST switchover, as the hours end up being the same when sent as + the literal that MySQL requires. + + Note that to get this scenario to work with MySQL (since it doesn't support + per-value timezones), you need to configure your server (or session) to be in UTC, + and tell the driver not to use the legacy date/time code by setting + "useLegacyDatetimeCode" to "false". This will cause the driver to always convert + to/from the server and client timezone consistently. + + This bug fix also fixes BUG#15604, by adding entirely new date/time handling + code that can be switched on by "useLegacyDatetimeCode" being set to "false" as + a JDBC configuration property. For Connector/J 5.1.x, the default is "true", + in trunk and beyond it will be "false" (i.e. the old date/time handling code, warts + and all will be deprecated). + + - Fixed BUG#32877 - Load balancing connection using best response time would incorrectly + "stick" to hosts that were down when the connection was first created. + + We solve this problem with a black list that is used during the picking of new hosts. + If the black list ends up including all configured hosts, the driver will retry for + a configurable number of times (the "retriesAllDown" configuration property, with a default + of 120 times), sleeping 250ms between attempts to pick a new connection. + + We've also went ahead and made the balancing strategy extensible. To create a new strategy, + implement the interface com.mysql.jdbc.BalanceStrategy (which also includes our standard + "extension" interface), and tell the driver to use it by passing in the + class name via the "loadBalanceStrategy" configuration property. + + - Fixed BUG#30508 - ResultSet returned by Statement.getGeneratedKeys() is not closed + automatically when statement that created it is closed. + + - Added two new connection properties, "selfDestructOnPingSecondsLifetime" and + "selfDestructOnPingMaxOperations" designed to control overall connection lifetime + (useful to reclaim resources on the server side) for connection pools that don't have such a + facility. + + The driver will consult the values of these properties when a ping is sent, either through + calling Connection.ping(), issuing the "ping marker" query (any query that starts with + "/* ping */"), or when using JDBC-4.0, calling Connection.isValid(). + + If the connection has issued too many operations, or is too old, the driver will + throw a SQLException with the SQLState of "08S01" at the time of the ping, which + will cause the connection to be invalidated with most pools in use today. + + - Fixed issue where driver could send invalid server-side prepared statement + IDs to the server when the driver was setup to do auto-reconnect as the + connection could get set up enough to start sending queries on one thread, + while the thread that "noticed" the connection was down hasn't completed + re-preparing all of the server-side prepared statements that were open when + the connection died. + + Potentially fixes cause for bug 28934. Potentially fixes other possible race + conditions where one thread that has created a connection "shares" it with other + threads if the connection is reconnected due to auto-reconnect functionality. + + - Fixed BUG#33823 - Public interface ResultSetInternalMethods with reference to + non-public class com.mysql.jdbc.CachedResultSetMetaData. + + - For any SQLException caused by another Throwable, besides dumping the message or stack + trace as a string into the message, set the underlying Throwable as the cause for + the SQLException, making it accessible via getCause(). + + - Fixed BUG#34093 - Statements with batched values do not return correct values for + getGeneratedKeys() when "rewriteBatchedStatements" is set to "true", and the + statement has an "ON DUPLICATE KEY UPDATE" clause. + + - Fixed BUG#31192 - Encoding Issue retrieving serverVersion in MysqlIO in the + method doHandshake when encoding doesn't contain ASCII characters in the "standard" + place (i.e. ebcdic). + + - Fixed issue where META-INF in the binary .jar file wasn't packed correctly, + leading to failure of the JDBC-4.0 SPI mechanism. + + - CallableStatements that aren't really stored procedure or stored function calls can + now be used, for tools such as Oracle JDeveloper ADF that issue statements such as + DDL through CallableStatements. + + - Fixed BUG#34518 - Statements using cursor fetch leaked internal prepared statements + until connection was closed. The internal prepared statement is now held open while + the result set is open, and closed by the result set itself being closed. + + - Fixed BUG#34677 - Blob.truncate() wouldn't take "0" as an argument. + + - CommunicationExceptions now carry information about the last time a packet + was received from the MySQL server, as well as when the last packet was sent + to one, in an effort to make it easier to debug communications errors caused + by network timeouts. + + - Reverted a change to DatabaseMetadata.getColumns() from 5.0, where + getColumns() would report NULL for COLUMN_SIZE for TIME, DATE, DATETIME + and TIMESTAMP types. It now reports the column size, in the + DatabaseMetadata implementations that use "SHOW" commands, and the + INFORMATION_SCHEMA. + + - Fixed Bug#34762 - RowDataStatic does't always set the metadata in + ResultSetRow, which can lead to failures when unpacking DATE, + TIME, DATETIME and TIMESTAMP types when using absolute, relative, + and previous result set navigation methods. + + - Fixed BUG#34703 - Connection.isValid() invalidates connection after + timeout, even if connection is actually valid. + + - Fixed BUG#34194 - ResultSetMetaData.getColumnTypeName() returns + "UNKNOWN" for GEOMETRY type. + + - Fixed BUG#33162 - NullPointerException instead of SQLException + thrown for ResultSet.getTimestamp() when not positioned on a + row. + + - The ConnectionLifecycleInterceptor interface now has callback methods for + transaction initiation (transactionBegun()), and completion + (transactionCompleted()), as reported by the *server* (i.e. + calling Connection.setAutoCommit(false) will not trigger + transactionBegun() being called, however the first statement + which causes a transaction to start on the server will cause + transactionBegun() to be called *after* the statement has been processed + on the server). + + - Fixed Bug#34913 - ResultSet.getTimestamp() returns incorrect + values for month/day of TIMESTAMPs when using server-side + prepared statements (not enabled by default). + + - Fixed BUG#34937 - MysqlConnectionPoolDataSource does not support + ReplicationConnection. Notice that we implemented com.mysql.jdbc.Connection + for ReplicationConnection, however, only accessors from ConnectionProperties + are implemented (not the mutators), and they return values from the currently + active connection. All other methods from com.mysql.jdbc.Connection are + implemented, and operate on the currently active connection, with the exception of + resetServerState() and changeUser(). + + - Connections created with jdbc:mysql:replication:// URLs now force + roundRobinLoadBalance=true on the slaves, and round-robin loadbalancing + now uses a "random" choice to more evenly distribute load across slave + servers, especially in connection pools. Connections that are configured + with "roundRobinLoadBalance=true" no longer set the failover state, + as it's assumed that we're not attempting to fall-back to a master + server. This fixes BUG#34963. + +10-09-07 - Version 5.1.5 + + - Released instead of 5.1.4 to pickup patch for BUG#31053 + from 5.0.8. + +10-09-07 - Version 5.1.4 + + - Added "autoSlowLog" configuration property, overrides + "slowQueryThreshold*" properties, driver determines slow + queries by those that are slower than 5 * stddev of the mean + query time (outside the 96% percentile). + + - Fixed BUG#28256 - When connection is in read-only mode, + queries that are wrapped in parentheses incorrectly identified + as DML. + +09-07-07 - Version 5.1.3 RC + + - Setting "useBlobToStoreUTF8OutsideBMP" to "true" tells the + driver to treat [MEDIUM/LONG/TINY]BLOB columns as [LONG]VARCHAR + columns holding text encoded in UTF-8 that has characters + outside the BMP (4-byte encodings), which MySQL server + can't handle natively. + + Set "utf8OutsideBmpExcludedColumnNamePattern" to a regex so that + column names matching the given regex will still be treated + as BLOBs The regex must follow the patterns used for the + java.util.regex package. The default is to exclude no columns, + and include all columns. + + Set "utf8OutsideBmpIncludedColumnNamePattern" to specify exclusion + rules to "utf8OutsideBmpExcludedColumnNamePattern". The regex must + follow the patterns used for the java.util.regex package. + + - New methods on com.mysql.jdbc.Statement: setLocalInfileInputStream() + and getLocalInfileInputStream(). + + setLocalInfileInputStream() sets an InputStream instance that will be used to send data + to the MySQL server for a "LOAD DATA LOCAL INFILE" statement + rather than a FileInputStream or URLInputStream that represents + the path given as an argument to the statement. + + This stream will be read to completion upon execution of a + "LOAD DATA LOCAL INFILE" statement, and will automatically + be closed by the driver, so it needs to be reset + before each call to execute*() that would cause the MySQL + server to request data to fulfill the request for + "LOAD DATA LOCAL INFILE". + + If this value is set to NULL, the driver will revert to using + a FileInputStream or URLInputStream as required. + + getLocalInfileInputStream() returns the InputStream instance that will be used to send + data in response to a "LOAD DATA LOCAL INFILE" statement. + + This method returns NULL if no such stream has been set + via setLocalInfileInputStream(). + + - The driver now connects with an initial character set + of "utf-8" solely for the purposes of authentication to + allow usernames and database names in any character set to + be used in the JDBC URL. + + - Errors encountered during Statement/PreparedStatement/CallableStatement.executeBatch() + when "rewriteBatchStatements" has been set to "true" now return + BatchUpdateExceptions according to the setting of "continueBatchOnError". + + If "continueBatchOnError" is set to "true", the update counts for the + "chunk" that were sent as one unit will all be set to EXECUTE_FAILED, but + the driver will attempt to process the remainder of the batch. You can determine which + "chunk" failed by looking at the update counts returned in the BatchUpdateException. + + If "continueBatchOnError" is set to "false", the update counts returned + will contain the failed "chunk", and stop with the failed chunk, with all + counts for the failed "chunk" set to EXECUTE_FAILED. + + Since MySQL doesn't return multiple error codes for multiple-statements, or + for multi-value INSERT/REPLACE, it is the application's responsibility to handle + determining which item(s) in the "chunk" actually failed. + + - Statement.setQueryTimeout()s now affect the entire batch for batched + statements, rather than the individual statements that make up the batch. + +06-29-07 - Version 5.1.2 Beta + + - Setting the configuration property "rewriteBatchedStatements" + to "true" will now cause the driver to rewrite batched prepared + statements with more than 3 parameter sets in a batch into + multi-statements (separated by ";") if they are not plain + (i.e. without SELECT or ON DUPLICATE KEY UPDATE clauses) INSERT + or REPLACE statements. + +06-22-07 - Version 5.1.1 Alpha + + - Pulled vendor-extension methods of Connection implementation out + into an interface to support java.sql.Wrapper functionality from + ConnectionPoolDataSource. The vendor extensions are javadoc'd in + the com.mysql.jdbc.Connection interface. + + For those looking further into the driver implementation, it is not + an API that is used for plugability of implementations inside our driver + (which is why there are still references to ConnectionImpl throughout the + code). + + Incompatible change: Connection.serverPrepare(String) has been re-named + to Connection.serverPrepareStatement() for consistency with + Connection.clientPrepareStatement(). + + We've also added server and client prepareStatement() methods that cover + all of the variants in the JDBC API. + + - Similar to Connection, we pulled out vendor extensions to Statement + into an interface named "com.mysql.Statement", and moved the Statement + class into com.mysql.StatementImpl. The two methods (javadoc'd in + "com.mysql.Statement" are enableStreamingResults(), which already existed, + and disableStreamingResults() which sets the statement instance back to + the fetch size and result set type it had before enableStreamingResults() + was called. + + - Added experimental support for statement "interceptors" via the + com.mysql.jdbc.StatementInterceptor interface, examples are + in com/mysql/jdbc/interceptors. + + Implement this interface to be placed "in between" query execution, so that + you can influence it. (currently experimental). + + StatementInterceptors are "chainable" when configured by the user, the + results returned by the "current" interceptor will be passed on to the next + on in the chain, from left-to-right order, as specified by the user in the + JDBC configuration property "statementInterceptors". + + See the sources (fully javadoc'd) for com.mysql.jdbc.StatementInterceptor + for more details until we iron out the API and get it documented in the + manual. + + - Externalized the descriptions of connection properties. + + - The data (and how it's stored) for ResultSet rows are now behind an + interface which allows us (in some cases) to allocate less memory + per row, in that for "streaming" result sets, we re-use the packet + used to read rows, since only one row at a time is ever active. + + - Made it possible to retrieve prepared statement parameter bindings + (to be used in StatementInterceptors, primarily). + + - Row navigation now causes any streams/readers open on the result set + to be closed, as in some cases we're reading directly from a shared network + packet and it will be overwritten by the "next" row. + + - Setting "rewriteBatchedStatements" to "true" now causes CallableStatements + with batched arguments to be re-written in the form "CALL (...); CALL (...); ..." + to send the batch in as few client-server round trips as possible. + + - Driver now picks appropriate internal row representation (whole row in one + buffer, or individual byte[]s for each column value) depending on heuristics, + including whether or not the row has BLOB or TEXT types and the overall + row-size. The threshold for row size that will cause the driver to + use a buffer rather than individual byte[]s is configured by the + configuration property "largeRowSizeThreshold", which has a default + value of 2KB. + +04-11-07 - Version 5.1.0 Alpha + + - Bumped JDBC Specification version number in jar-file manifest. + + - Re-worked Ant buildfile to build JDBC-4.0 classes separately, as well + as support building under Eclipse (since Eclipse can't mix/match JDKs). + + To build, you must set JAVA_HOME to J2SDK-1.4.2 or Java-5, and set + the following properties on your Ant commandline: + + com.mysql.jdbc.java6.javac - full path to your Java-6 javac executable + com.mysql.jdbc.java6.rtjar - full path to your Java-6 rt.jar file + + - New feature - driver will automatically adjust session variable + "net_write_timeout" when it determines its been asked for a "streaming" + result, and resets it to the previous value when the result set + has been consumed. (configuration property is named + "netTimeoutForStreamingResults", value has unit of seconds, + the value '0' means the driver will not try and adjust this value). + + - Added support for JDBC-4.0 categorized SQLExceptions. + + - Refactored CommunicationsException into a JDBC3 version, and a JDBC4 + version (which extends SQLRecoverableException, now that it exists). + + This change means that if you were catching + com.mysql.jdbc.CommunicationsException in your applications instead + of looking at the SQLState class of "08", and are moving to Java 6 + (or newer), you need to change your imports to that exception + to be com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, as + the old class will not be instantiated for communications link-related + errors under Java 6. + + - Added support for JDBC-4.0's client information. The backend storage + of information provided via Connection.setClientInfo() and retrieved + by Connection.getClientInfo() is pluggable by any class that implements + the com.mysql.jdbc.JDBC4ClientInfoProvider interface and has a no-args + constructor. + + The implementation used by the driver is configured using the + "clientInfoProvider" configuration property (with a default of value + of "com.mysql.jdbc.JDBC4CommentClientInfoProvider", an implementation + which lists the client info as a comment prepended to every query + sent to the server). + + This functionality is only available when using Java-6 or newer. + + - Added support for JDBC-4.0's SQLXML interfaces. + + - Added support for JDBC-4.0's Wrapper interface. + + - Added support for JDBC-4.0's NCLOB, and NCHAR/NVARCHAR types. + +nn-nn-07 - Version 5.0.9 + + - Driver now calls SocketFactory.afterHandshake() at appropriate time. + +10-09-07 - Version 5.0.8 + + - Fixed BUG#30550, executeBatch() would fail with an ArithmeticException + and/or NullPointerException when the batch had zero members and + "rewriteBatchedStatements" was set to "true" for the connection. + + - Added two configuration parameters (both default to "false") + + * blobsAreStrings - Should the driver always treat BLOBs as Strings + specifically to work around dubious metadata returned + by the server for GROUP BY clauses? + + * functionsNeverReturnBlobs - Should the driver always treat data from + functions returning BLOBs as Strings - + specifically to work around dubious metadata + returned by the server for GROUP BY clauses? + + - Fixed BUG#29106 - Connection checker for JBoss didn't use same method parameters + via reflection, causing connections to always seem "bad". + + - Fixed BUG#30664 - Note that this fix only works for MySQL server + versions 5.0.25 and newer, since earlier versions didn't consistently + return correct metadata for functions, and thus results from + subqueries and functions were indistinguishable from each other, + leading to type-related bugs. + + - Fixed BUG#28972 - DatabaseMetaData.getTypeInfo() for the types DECIMAL + and NUMERIC will return a precision of 254 for server versions older than + 5.0.3, 64 for versions 5.0.3-5.0.5 and 65 for versions newer than 5.0.5. + + - Fixed BUG#29852 - Closing a load-balanced connection would cause a + ClassCastException. + + - Fixed BUG#27867 - Schema objects with identifiers other than + the connection character aren't retrieved correctly in + ResultSetMetadata. + + - Fixed BUG#28689 - CallableStatement.executeBatch() doesn't work when + connection property "noAccessToProcedureBodies" has been set to "true". + + The fix involves changing the behavior of "noAccessToProcedureBodies",in + that the driver will now report all paramters as "IN" paramters + but allow callers to call registerOutParameter() on them without throwing + an exception. + + - Fixed BUG#27182 - Connection.getServerCharacterEncoding() doesn't work + for servers with version >= 4.1. + + - Fixed BUG#27915 - DatabaseMetaData.getColumns() doesn't + contain SCOPE_* or IS_AUTOINCREMENT columns. + + - Fixed BUG#30851, NPE with null column values when + "padCharsWithSpace" is set to "true". + + - Specifying a "validation query" in your connection pool + that starts with "/* ping */" _exactly_ will cause the driver to + instead send a ping to the server and return a fake result set (much + lighter weight), and when using a ReplicationConnection or a LoadBalancedConnection, + will send the ping across all active connections. + + - Fixed Bug#30892 setObject(int, Object, int, int) delegate in + PreparedStatmentWrapper delegates to wrong method. + + - XAConnections now start in auto-commit mode (as per JDBC-4.0 specification + clarification). + + - Fixed Bug#27412 - cached metadata with PreparedStatement.execute() + throws NullPointerException. + + - Driver will now fall back to sane defaults for max_allowed_packet and + net_buffer_length if the server reports them incorrectly (and will log + this situation at WARN level, since it's actually an error condition). + + - Fixed BUG#27916 - UNSIGNED types not reported via DBMD.getTypeInfo(), and + capitalization of type names is not consistent between DBMD.getColumns(), + RSMD.getColumnTypeName() and DBMD.getTypeInfo(). + + This fix also ensures that the precision of UNSIGNED MEDIUMINT + and UNSIGNED BIGINT is reported correctly via DBMD.getColumns(). + + - Fixed BUG#31053 - Connections established using URLs of the form + "jdbc:mysql:loadbalance://" weren't doing failover if they tried to + connect to a MySQL server that was down. The driver now attempts + connections to the next "best" (depending on the load balance strategy + in use) server, and continues to attempt connecting to the next "best" + server every 250 milliseconds until one is found that is up and running + or 5 minutes has passed. + + If the driver gives up, it will throw the last-received SQLException. + +07-19-07 - Version 5.0.7 + + - Setting the configuration parameter "useCursorFetch" to "true" for + MySQL-5.0+ enables the use of cursors that allow Connector/J to save + memory by fetching result set rows in chunks (where the chunk size + is set by calling setFetchSize() on a Statement or ResultSet) by + using fully-materialized cursors on the server. + + The driver will will now automatically set "useServerPrepStmts" to + "true" when "useCursorFetch" has been set to "true", since the feature + requires server-side prepared statements in order to function. + + - Fixed BUG#28469 - PreparedStatement.getMetaData() for statements + containing leading one-line comments is not returned correctly. + + As part of this fix, we also overhauled detection of DML for + executeQuery() and SELECTs for executeUpdate() in plain and + prepared statements to be aware of the same types of comments. + + - Added configuration property "useNanosForElapsedTime" - for + profiling/debugging functionality that measures elapsed time, + should the driver try to use nanoseconds resolution if available + (requires JDK >= 1.5)? + + - Added configuration property "slowQueryThresholdNanos" - if + "useNanosForElapsedTime" is set to "true", and this property + is set to a non-zero value the driver will use this threshold + (in nanosecond units) to determine if a query was slow, instead + of using millisecond units. + + Note, that if "useNanosForElapsedTime" is set to "true", and this + property is set to "0" (or left default), then elapsed times will + still be measured in nanoseconds (if possible), but the slow query + threshold will be converted from milliseconds to nanoseconds, and thus + have an upper bound of approximately 2000 millesconds (as that threshold + is represented as an integer, not a long). + + - Added configuration properties to allow tuning of TCP/IP socket + parameters: + + "tcpNoDelay" - Should the driver set SO_TCP_NODELAY (disabling the + Nagle Algorithm, default "true")? + + "tcpKeepAlive" - Should the driver set SO_KEEPALIVE (default "true")? + + "tcpRcvBuf" - Should the driver set SO_RCV_BUF to the given value? + The default value of '0', means use the platform default + value for this property. + + "tcpSndBuf" - Should the driver set SO_SND_BUF to the given value? + The default value of '0', means use the platform default + value for this property. + + "tcpTrafficClass" - Should the driver set traffic class or + type-of-service fields? See the documentation + for java.net.Socket.setTrafficClass() for more + information. + + - Give more information in EOFExceptions thrown out of MysqlIO (how many + bytes the driver expected to read, how many it actually read, say that + communications with the server were unexpectedly lost). + + - Setting "useDynamicCharsetInfo" to "false" now causes driver to use + static lookups for collations as well (makes + ResultSetMetadata.isCaseSensitive() much more efficient, which leads + to performance increase for ColdFusion, which calls this method for + every column on every table it sees, it appears). + + - Driver detects when it is running in a ColdFusion MX server (tested + with version 7), and uses the configuration bundle "coldFusion", + which sets useDynamicCharsetInfo to "false" (see previous entry), and + sets useLocalSessionState and autoReconnect to "true". + + - Fixed BUG#28851 - parser in client-side prepared statements + eats character following '/' if it's not a multi-line comment. + + - Fixed BUG#28956 - parser in client-side prepared statements + runs to end of statement, rather than end-of-line for '#' comments. + + Also added support for '--' single-line comments. + + - Don't send any file data in response to LOAD DATA LOCAL INFILE + if the feature is disabled at the client side. This is to prevent + a malicious server or man-in-the-middle from asking the client for + data that the client is not expecting. Thanks to Jan Kneschke for + discovering the exploit and Andrey "Poohie" Hristov, Konstantin Osipov + and Sergei Golubchik for discussions about implications and possible + fixes. This fixes BUG 29605 for JDBC. + + - Added new debugging functionality - Setting configuration property + "includeInnodbStatusInDeadlockExceptions" to "true" will cause the driver + to append the output of "SHOW ENGINE INNODB STATUS" to deadlock-related + exceptions, which will enumerate the current locks held inside InnoDB. + +05-15-07 - Version 5.0.6 + + - Fixed BUG#25545 - Client options not sent correctly when using SSL, + leading to stored procedures not being able to return results. Thanks + to Don Cohen for the bug report, testcase and patch. + + - Fixed BUG#26592 - PreparedStatement is not closed in + BlobFromLocator.getBytes(). + + - Fixed BUG#25624 - Whitespace surrounding storage/size specifiers in + stored procedure parameters declaration causes NumberFormatException to + be thrown when calling stored procedure on JDK-1.5 or newer, as the Number + classes in JDK-1.5+ are whitespace intolerant. + + - Fixed BUG#26173 - When useCursorFetch=true, sometimes server would return + new, more exact metadata during the execution of the server-side prepared + statement that enables this functionality, which the driver ignored (using + the original metadata returned during prepare()), causing corrupt reading + of data due to type mismatch when the actual rows were returned. + + - Fixed BUG#26959 - comments in DDL of stored procedures/functions confuse + procedure parser, and thus metadata about them can not be created, leading to + inability to retrieve said metadata, or execute procedures that have certain + comments in them. + + - Give better error message when "streaming" result sets, and the connection + gets clobbered because of exceeding net_write_timeout on the server. (which is + basically what the error message says too). + + - Fixed BUG#26789 - fast date/time parsing doesn't take into + account 00:00:00 as a legal value. + + - Fixed BUG#27317 - ResultSet.get*() with a column index < 1 returns + misleading error message. + + - Fixed BUG#25517 - Statement.setMaxRows() is not effective on result + sets materialized from cursors. + + - New configuration property, "enableQueryTimeouts" (default "true"). + When enabled, query timeouts set via Statement.setQueryTimeout() use a + shared java.util.Timer instance for scheduling. Even if the timeout + doesn't expire before the query is processed, there will be + memory used by the TimerTask for the given timeout which won't be + reclaimed until the time the timeout would have expired if it + hadn't been cancelled by the driver. High-load environments + might want to consider disabling this functionality. (this configuration + property is part of the "maxPerformance" configuration bundle). + + - Fixed BUG#27400 - CALL /* ... */ some_proc() doesn't work. As a side effect + of this fix, you can now use /* */ and # comments when preparing statements using + client-side prepared statement emulation. + + If the comments happen to contain parameter markers '?', they will be treated + as belonging to the comment (i.e. not recognized) rather than being a parameter + of the statement. + + Note that the statement when sent to the server will contain the comments + as-is, they're not stripped during the process of preparing the PreparedStatement + or CallableStatement. + + - Fixed BUG#25328 - BIT(> 1) is returned as java.lang.String from ResultSet.getObject() + rather than byte[]. + + - Fixed BUG#25715 - CallableStatements with OUT/INOUT parameters that + are "binary" (blobs, bits, (var)binary, java_object) have extra 7 bytes + (which happens to be the _binary introducer!) + + - Added configuration property "padCharsWithSpace" (defaults to "false"). If set + to "true", and a result set column has the CHAR type and the value does not + fill the amount of characters specified in the DDL for the column, the driver + will pad the remaining characters with space (for ANSI compliance). + + - Fixed BUG#27655 - Connection.getTransactionIsolation() uses + "SHOW VARIABLES LIKE" which is very inefficient on MySQL-5.0+ + + - Added configuration property "useDynamicCharsetInfo". If set to "false" + (the default), the driver will use a per-connection cache of character set + information queried from the server when necessary, or when set to "true", + use a built-in static mapping that is more efficient, but isn't aware of + custom character sets or character sets implemented after the release of + the JDBC driver. + + Note: this only affects the "padCharsWithSpace" configuration property and the + ResultSetMetaData.getColumnDisplayWidth() method. + + - More intelligent initial packet sizes for the "shared" packets are used + (512 bytes, rather than 16K), and initial packets used during handshake are + now sized appropriately as to not require reallocation. + + - Fixed issue where calling getGeneratedKeys() on a prepared statement after + calling execute() didn't always return the generated keys (executeUpdate() + worked fine however). + + - Fixed issue where a failed-over connection would let an application call + setReadOnly(false), when that call should be ignored until the connection + is reconnected to a writable master unless "failoverReadOnly" had been set + to "false". + + - Fixed BUG#28085 - Generate more useful error messages for diagnostics + when the driver thinks a result set isn't updatable. (Thanks to Ashley Martens + for the patch). + + - Driver will now use INSERT INTO ... VALUES (DEFAULT) form of statement + for updatable result sets for ResultSet.insertRow(), rather than + pre-populating the insert row with values from DatabaseMetaData.getColumns() + (which results in a "SHOW FULL COLUMNS" on the server for every result + set). If an application requires access to the default values before + insertRow() has been called, the JDBC URL should be configured with + "populateInsertRowWithDefaultValues" set to "true". + + This fix specifically targets performance issues with ColdFusion and the + fact that it seems to ask for updatable result sets no matter what the + application does with them. + + - com.mysql.jdbc.[NonRegistering]Driver now understands URLs of the format + "jdbc:mysql:replication://" and "jdbc:mysql:loadbalance://" which will + create a ReplicationConnection (exactly like when + using [NonRegistering]ReplicationDriver) and an experimenal load-balanced + connection designed for use with SQL nodes in a MySQL Cluster/NDB environment, + respectively. + + In an effort to simplify things, we're working on deprecating multiple + drivers, and instead specifying different core behavior based upon JDBC URL + prefixes, so watch for [NonRegistering]ReplicationDriver to eventually + disappear, to be replaced with com.mysql.jdbc[NonRegistering]Driver with + the new URL prefix. + + - Added an experimental load-balanced connection designed for use with SQL nodes + in a MySQL Cluster/NDB environment (This is not for master-slave replication. + For that, we suggest you look at ReplicationConnection or "lbpool"). + + If the JDBC URL starts with "jdbc:mysql:loadbalance://host-1,host-2,...host-n", + the driver will create an implementation of java.sql.Connection that load + balances requests across a series of MySQL JDBC connections to the given hosts, + where the balancing takes place after transaction commit. + + Therefore, for this to work (at all), you must use transactions, even if only + reading data. + + Physical connections to the given hosts will not be created until needed. + + The driver will invalidate connections that it detects have had + communication errors when processing a request. A new connection to the + problematic host will be attempted the next time it is selected by the load + balancing algorithm. + + There are two choices for load balancing algorithms, which may be specified + by the "loadBalanceStrategy" JDBC URL configuration property: + + * "random" - the driver will pick a random host for each request. This tends + to work better than round-robin, as the randomness will somewhat account for + spreading loads where requests vary in response time, while round-robin + can sometimes lead to overloaded nodes if there are variations in response times + across the workload. + + * "bestResponseTime" - the driver will route the request to the host that had + the best response time for the previous transaction. + + - When "useLocalSessionState" is set to "true" and connected to a MySQL-5.0 or + later server, the JDBC driver will now determine whether an actual "commit" or + "rollback" statement needs to be sent to the database when Connection.commit() + or Connection.rollback() is called. + + This is especially helpful for high-load situations with connection pools that + always call Connection.rollback() on connection check-in/check-out because it + avoids a round-trip to the server. + +03-01-07 - Version 5.0.5 + + - Fixed BUG#23645 - Some collations/character sets reported as "unknown" + (specifically cias variants of existing character sets), and inability to override + the detected server character set. + + - Performance enhancement of initial character set configuration, driver + will only send commands required to configure connection character set + session variables if the current values on the server do not match + what is required. + + - Fixed BUG#24360 .setFetchSize() breaks prepared SHOW and other commands. + + - Fixed BUG#24344 - useJDBCCompliantTimezoneShift with server-side prepared + statements gives different behavior than when using client-side prepared + statements. (this is now fixed if moving from server-side prepared statements + to client-side prepared statements by setting "useSSPSCompatibleTimezoneShift" to + true", as the driver can't tell if this is a new deployment that never used + server-side prepared statements, or if it is an existing deployment that is + switching to client-side prepared statements from server-side prepared statements. + + - Fixed BUG#23304 - DBMD using "show" and DBMD using information_schema do + not return results consistent with each other. (note this fix only + addresses the inconsistencies, not the issue that the driver is + treating schemas differently than some users expect. We will revisit + this behavior when there is full support for schemas in MySQL). + + - Fixed BUG#25073 - rewriting batched statements leaks internal statement + instances, and causes a memory leak. + + - Fixed issue where field-level for metadata from DatabaseMetaData when using + INFORMATION_SCHEMA didn't have references to current connections, + sometimes leading to NullPointerExceptions when intropsecting them via + ResultSetMetaData. + + - Fixed BUG#25025 - Client-side prepared statement parser gets confused by + in-line (/* ... */) comments and therefore can't rewrite batched statements + or reliably detect type of statements when they're used. + + - Fixed BUG#24065 - Better error message when server doesn't return enough + information to determine stored procedure/function parameter types. + + - Fixed BUG#21438 - Driver sending nanoseconds to server for timestamps when + using server-side prepared statements, when server expects microseconds. + + - Fixed BUG#25514 - Timer instance used for Statement.setQueryTimeout() + created per-connection, rather than per-VM, causing memory leak + + - Fixed BUG#25009 - Results from updates not handled correctly in + multi-statement queries, leading to erroneous "Result is from UPDATE" + exceptions. + + - Fixed BUG#25047 - StringUtils.indexOfIgnoreCaseRespectQuotes() isn't + case-insensitive on the first character of the target. This bug broke + rewriteBatchedStatements functionality when prepared statements don't + use upper-case for the VALUES clause in their statements. + + - Fixed BUG#21480 - Some exceptions thrown out of StandardSocketFactory + were needlessly wrapped, obscurring their true cause, especially when + using socket timeouts. + + - Fixed BUG#23303 - DatabaseMetaData.getSchemas() doesn't return a + TABLE_CATALOG column. + + - Fixed BUG#25399 - EscapeProcessor gets confused by multiple + backslashes. We now push the responsibility of syntax errors back + on to the server for most escape sequences. + + - Fixed BUG#25379 - INOUT parameters in CallableStatements get + doubly-escaped. + + - Removed non-short-circuited logical ORs from "if" statements. + + - Re-worked stored procedure parameter parser to be more robust. Driver no + longer requires "BEGIN" in stored procedure definition, but does have + requirement that if a stored function begins with a label directly after the + "returns" clause, that the label is not a quoted identifier. + - Reverted back to internal character conversion routines for single-byte + character sets, as the ones internal to the JVM are using much more CPU + time than our internal implementation. + + - Changed cached result set metadata (when using + "cacheResultSetMetadata=true") to be cached per-connection rather + than per-statement as previously implemented. + + - Use a java.util.TreeMap to map column names to ordinal indexes for + ResultSet.findColumn() instead of a HashMap. This allows us to have + case-insensitive lookups (required by the JDBC specification) without + resorting to the many transient object instances needed to support this + requirement with a normal HashMap with either case-adjusted keys, or + case-insensitive keys. (In the worst case scenario for lookups of a 1000 + column result set, TreeMaps are about half as fast wall-clock time as + a HashMap, however in normal applications their use gives many orders + of magnitude reduction in transient object instance creation which pays + off later for CPU usage in garbage collection). + + - Avoid static synchronized code in JVM class libraries for dealing with + default timezones. + + - Fixed cases where ServerPreparedStatements weren't using cached metadata + when "cacheResultSetMetadata=true" was configured. + + - Use faster datetime parsing for ResultSets that come from plain or + non-server-side prepared statements. (Enable old implementation with + "useFastDateParsing=false" as a configuration parameter). + + - Fixed BUG#24794 - DatabaseMetaData.getSQLKeywords() doesn't return + all reserved words for current MySQL version. The current fix/implementation + returns keywords for MySQL-5.1, and doesn't distinguish between different + versions of the server. + + - When using cached metadata, skip field-level metadata packets coming from + the server, rather than reading them and discarding them without creating + com.mysql.jdbc.Field instances. + + - Fixed BUG#25836 - Statement execution which timed out doesn't always + throw MySQLTimeoutException. + + - Throw exceptions encountered during timeout to thread + calling Statement.execute*(), rather than RuntimeException. + + - Added configuration property "localSocketAddress",which is the hostname or + IP address given to explicitly configure the interface that the driver will + bind the client side of the TCP/IP connection to when connecting. + + - Take "localSocketAddress" property into account when creating instances + of CommunicationsException when the underyling exception is a + java.net.BindException, so that a friendlier error message is given with + a little internal diagnostics. + + - Fixed some NPEs when cached metadata was used with UpdatableResultSets. + + - The "rewriteBatchedStatements" feature can now be used with server-side + prepared statements. + + - Fixed BUG#26326 - Connection property "socketFactory" wasn't exposed via + correctly named mutator/accessor, causing data source implementations that + use JavaBean naming conventions to set properties to fail to set the property + (and in the case of SJAS, fail silently when trying to set this parameter). + + - Fixed BUG#25787 - java.util.Date should be serialized for + PreparedStatement.setObject(). + + We've added a new configuration option "treatUtilDateAsTimestamp", which is + false by default, as (1) We already had specific behavior to treat + java.util.Date as a java.sql.Timestamp because it's useful to many folks, + and (2) that behavior will very likely be required for drivers JDBC-post-4.0. + + - Fixed BUG#22628 - Driver.getPropertyInfo() throws NullPointerException for + URL that only specifies host and/or port. + + - Fixed BUG#21267, ParameterMetaData throws NullPointerException when + prepared SQL actually has a syntax error. Added + "generateSimpleParameterMetadata" configuration property, which when set + to "true" will generate metadata reflecting VARCHAR for every parameter + (the default is "false", which will cause an exception to be thrown if no + parameter metadata for the statement is actually available). + + - When extracting foreign key information from "SHOW CREATE TABLE " in + DatabaseMetaData, ignore exceptions relating to tables being missing + (which could happen for cross-reference or imported-key requests, as + the list of tables is generated first, then iterated). + + - Fixed logging of XA commands sent to server, it's now configurable + via "logXaCommands" property (defaults to "false"). + + - Fixed issue where XADataSources couldn't be bound into JNDI, + as the DataSourceFactory didn't know how to create instances + of them. + + - Fixed issue where XADataSources couldn't be bound into JNDI, + as the DataSourceFactory didn't know how to create instances + of them. + + - Usage advisor will now issue warnings for result sets with large numbers + of rows (size configured by "resultSetSizeThreshold" property, default + value is 100). + +10-20-06 - Version 5.0.4 + + - Fixed BUG#21379 - column names don't match metadata in cases + where server doesn't return original column names (column functions) + thus breaking compatibility with applications that expect 1-1 mappings + between findColumn() and rsmd.getColumnName(), usually manifests itself + as "Can't find column ('')" exceptions. + + - Fixed BUG#21544 - When using information_schema for metadata, + COLUMN_SIZE for getColumns() is not clamped to range of + java.lang.Integer as is the case when not using + information_schema, thus leading to a truncation exception that + isn't present when not using information_schema. + + - Fixed configuration property "jdbcCompliantTruncation" was not + being used for reads of result set values. + + - Fixed BUG#22024 - Newlines causing whitespace to span confuse + procedure parser when getting parameter metadata for stored + procedures. + + - Driver now supports {call sp} (without "()" if procedure has no + arguments). + + - Fixed BUG#22359 - Driver was using milliseconds for + Statement.setQueryTimeout() when specification says argument is + to be in seconds. + + - Workaround for server crash when calling stored procedures + via a server-side prepared statement (driver now detects + prepare(stored procedure) and substitutes client-side prepared + statement), addresses BUG#22297. + + - Added new _ci collations to CharsetMapping, fixing + Bug#22456 - utf8_unicode_ci not working. + + - Fixed BUG#22290 - Driver issues truncation on write exception when + it shouldn't (due to sending big decimal incorrectly to server with + server-side prepared statement). + + - Fixed BUG#22613 - DBMD.getColumns() does not return expected + COLUMN_SIZE for the SET type, now returns length of largest possible + set disregarding whitespace or the "," delimitters to be consistent + with the ODBC driver. + + - Driver now sends numeric 1 or 0 for client-prepared statement + setBoolean() calls instead of '1' or '0'. + + - DatabaseMetaData correctly reports true for supportsCatalog*() + methods. + +07-26-06 - Version 5.0.3 + + - Fixed BUG#20650 - Statement.cancel() causes NullPointerException + if underlying connection has been closed due to server failure. + + - Added configuration option "noAccessToProcedureBodies" which will + cause the driver to create basic parameter metadata for + CallableStatements when the user does not have access to procedure + bodies via "SHOW CREATE PROCEDURE" or selecting from mysql.proc + instead of throwing an exception. The default value for this option + is "false". + +07-11-06 - Version 5.0.2-beta (5.0.1 not released due to packaging error) + + - Fixed BUG#17401 - Can't use XAConnection for local transactions when + no global transaction is in progress. + + - Fixed BUG#18086 - Driver fails on non-ASCII platforms. The driver + was assuming that the platform character set would be a superset + of MySQL's "latin1" when doing the handshake for authentication, + and when reading error messages. We now use Cp1252 for all strings + sent to the server during the handshake phase, and a hard-coded mapping + of the "language" server variable to the character set that + is used for error messages. + + - Fixed BUG#19169 - ConnectionProperties (and thus some + subclasses) are not serializable, even though some J2EE containers + expect them to be. + + - Fixed BUG#20242 - MysqlValidConnectionChecker for JBoss doesn't + work with MySQLXADataSources. + + - Better caching of character set converters (per-connection) + to remove a bottleneck for multibyte character sets. + + - Added connection/datasource property "pinGlobalTxToPhysicalConnection" + (defaults to "false"). When set to "true", when using XAConnections, the + driver ensures that operations on a given XID are always routed to the + same physical connection. This allows the XAConnection to support + "XA START ... JOIN" after "XA END" has been called, and is also a + workaround for transaction managers that don't maintain thread affinity + for a global transaction (most either always maintain thread affinity, + or have it as a configuration option). + + - MysqlXaConnection.recover(int flags) now allows combinations of + XAResource.TMSTARTRSCAN and TMENDRSCAN. To simulate the "scanning" + nature of the interface, we return all prepared XIDs for TMSTARTRSCAN, + and no new XIDs for calls with TMNOFLAGS, or TMENDRSCAN when not in + combination with TMSTARTRSCAN. This change was made for API compliance, + as well as integration with IBM WebSphere's transaction manager. + +12-23-05 - Version 5.0.0-beta + + - XADataSource implemented (ported from 3.2 branch which won't be + released as a product). Use + "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" as your datasource + class name in your application server to utilize XA transactions + in MySQL-5.0.10 and newer. + + - PreparedStatement.setString() didn't work correctly when + sql_mode on server contained NO_BACKSLASH_ESCAPES, and no characters + that needed escaping were present in the string. + + - Attempt detection of the MySQL type "BINARY" (it's an alias, so this isn't + always reliable), and use the java.sql.Types.BINARY type mapping for it. + + - Moved -bin-g.jar file into separate "debug" subdirectory to avoid confusion. + + - Don't allow .setAutoCommit(true), or .commit() or .rollback() on an XA-managed + connection as-per the JDBC specification. + + - If the connection "useTimezone" is set to "true", then also respect timezone + conversions in escape-processed string literals (e.g. "{ts ...}" and + "{t ...}"). + + - Return original column name for RSMD.getColumnName() if the column was aliased, + alias name for .getColumnLabel() (if aliased), and original table name + for .getTableName(). Note this only works for MySQL-4.1 and newer, as + older servers don't make this information available to clients. + + - Setting "useJDBCCompliantTimezoneShift=true" (it's not the default) + causes the driver to use GMT for _all_ TIMESTAMP/DATETIME timezones, + and the current VM timezone for any other type that refers to timezones. + This feature can not be used when "useTimezone=true" to convert between + server and client timezones. + + - Add one level of indirection of internal representation of CallableStatement + parameter metadata to avoid class not found issues on JDK-1.3 for + ParameterMetadata interface (which doesn't exist prior to JDBC-3.0). + + - Added unit tests for XADatasource, as well as friendlier exceptions + for XA failures compared to the "stock" XAException (which has no + messages). + + - Fixed BUG#14279 - Idle timeouts cause XAConnections to whine about rolling + themselves back + + - Added support for Connector/MXJ integration via url subprotocol + "jdbc:mysql:mxj://....". + + - Moved all SQLException constructor usage to a factory in SQLError + (ground-work for JDBC-4.0 SQLState-based exception classes). + + - Removed Java5-specific calls to BigDecimal constructor (when + result set value is '', (int)0 was being used as an argument + in-directly via method return value. This signature doesn't exist + prior to Java5.) + + - Moved all SQLException creation to a factory method in SQLError, + groundwork for JDBC-4.0 SQLState class-based exceptions. + + - Added service-provider entry to META-INF/services/java.sql.Driver + for JDBC-4.0 support. + + - Return "[VAR]BINARY" for RSMD.getColumnTypeName() when that is actually + the type, and it can be distinguished (MySQL-4.1 and newer). + + - When fix for BUG#14562 was merged from 3.1.12, added functionality + for CallableStatement's parameter metadata to return correct + information for .getParameterClassName(). + + - Fuller synchronization of Connection to avoid deadlocks when + using multithreaded frameworks that multithread a single connection + (usually not recommended, but the JDBC spec allows it anyways), + part of fix to BUG#14972). + + - Implementation of Statement.cancel() and Statement.setQueryTimeout(). + Both require MySQL-5.0.0 or newer server, require a separate connection + to issue the "KILL QUERY" command, and in the case of setQueryTimeout() + creates an additional thread to handle the timeout functionality. + + Note: Failures to cancel the statement for setQueryTimeout() may manifest + themselves as RuntimeExceptions rather than failing silently, as there + is currently no way to unblock the thread that is executing the query being + cancelled due to timeout expiration and have it throw the exception + instead. + + - Removed dead code in com.mysql.jdbc.Connection. + + - Made construction of com.mysql.jdbc.Field (result set metadata) + instances more efficient for non-string types by not doing + character set initialization, or detection of type changes due to + temporary tables. + + - Removed redundant code in com.mysql.jdbc.MysqlIO. + + - Removed work done for BUG#14652, and instead loosened synchronization + to solve a number of deadlock issues in BUG#18719, BUG#18367, BUG#17709 + and BUG#15067. New strategy basically makes Connection instances threadsafe + and thus shareable across threads, and anything else threadsafe, but not + necessarily shareable across threads due to JDBC API interactions that + can cause non-obvious behavior and/or deadlock scenarios to occur since + the API is not designed to be used from multiple threads at once. + + Therefore, unless external synchronization is provided, clients should + not allow multiple threads to share a given statement or result set. Examples + of issues with the API itself not being multi-thread suitable include, + but are not limited to race conditions between modifiers and execution and + retrieval methods on statements and result sets that are not synchronizable + such as ResultSet.get*() and traversal methods, or Statement.execute*() closing + result sets without effectively making the driver itself serializable across the + board. + + These changes should not have any effect on "normal" J(2)EE use cases + where only one thread ever uses a connection instance and the objects created by + it. + + - Use a java.util.Timer to schedule cancellation of queries via + Statement.setQueryTimeout() rather than one thread per potential cancellation. + + A new thread will be used to actually cancel a running query, as there's potential + for a cancel request to block other cancel requests if all run from the + same thread. + +nn-nn-07 - Version 3.1.15 + + - Fixed BUG#23281 - Downed slave caused round-robin load balance to + not cycle back to first host in list. + + - Disabled use of server-side prepared statements by default. + + - Handle YYYY-MM-DD hh:mm:ss format of timestamp in + ResultSet.getTimeFromString(). + + - Fixed BUG#24840 - character encoding of "US-ASCII" doesn't map correctly + for 4.1 or newer + + - Added Implementation-Vendor-Id attribute to jar manifest per request + in BUG#15641. + + - C3P0 >= version 0.9.1 passes non-proxied connections to + MysqlConnectionTester, thus it began throwing ClassCastExceptions. + MysqlConnectionTester now checks if it has a plain Connection and uses + that if possible. Thanks to Brian Skrab for the fix. + +10-19-06 - Version 3.1.14 + + - Fixed BUG#20479 - Updatable result set throws ClassCastException + when there is row data and moveToInsertRow() is called. + + - Fixed BUG#20485 - Updatable result set that contains + a BIT column fails when server-side prepared statements are used. + + - Fixed BUG#16987 - Memory leak with profileSQL=true. + + - Fixed BUG#19726 - Connection fails to localhost when using + timeout and IPv6 is configured. + + - Fixed BUG#16791 - NullPointerException in MysqlDataSourceFactory + due to Reference containing RefAddrs with null content. + + - Fixed BUG#20306 - ResultSet.getShort() for UNSIGNED TINYINT + returns incorrect values when using server-side prepared statements. + + - Fixed BUG#20687 - Can't pool server-side prepared statements, exception + raised when re-using them. + + - Fixed BUG#21062 - ResultSet.getSomeInteger() doesn't work for BIT(>1). + + - Fixed BUG#18880 - ResultSet.getFloatFromString() can't retrieve + values near Float.MIN/MAX_VALUE. + + - Fixed BUG#20888 - escape of quotes in client-side prepared + statements parsing not respected. Patch covers more than bug report, + including NO_BACKSLASH_ESCAPES being set, and stacked quote characters + forms of escaping (i.e. '' or ""). + + - Fixed BUG#19993 - ReplicationDriver does not always round-robin load + balance depending on URL used for slaves list. + + - Fixed calling toString() on ResultSetMetaData for driver-generated + (i.e. from DatabaseMetaData method calls, or from getGeneratedKeys()) + result sets would raise a NullPointerException. + + - Fixed Bug#21207 - Driver throws NPE when tracing prepared statements that + have been closed (in asSQL()). + + - Removed logger autodectection altogether, must now specify logger + explitly if you want to use a logger other than one that logs + to STDERR. + + - Fixed BUG#22290 - Driver issues truncation on write exception when + it shouldn't (due to sending big decimal incorrectly to server with + server-side prepared statement). + + - Driver now sends numeric 1 or 0 for client-prepared statement + setBoolean() calls instead of '1' or '0'. + + - Fixed bug where driver would not advance to next host if + roundRobinLoadBalance=true and the last host in the list is down. + + - Fixed BUG#18258 - DatabaseMetaData.getTables(), columns() with bad + catalog parameter threw exception rather than return empty result + set (as required by spec). + + - Check and store value for continueBatchOnError property in constructor + of Statements, rather than when executing batches, so that Connections + closed out from underneath statements don't cause NullPointerExceptions + when it's required to check this property. + + - Fixed bug when calling stored functions, where parameters weren't + numbered correctly (first parameter is now the return value, subsequent + parameters if specified start at index "2"). + + - Fixed BUG#21814 - time values outside valid range silently wrap. + +05-26-06 - Version 3.1.13 + + - Fixed BUG#15464 - INOUT parameter does not store IN value. + + - Fixed BUG#14609 - Exception thrown for new decimal type when + using updatable result sets. + + - Fixed BUG#15544, no "dos" character set in MySQL > 4.1.0 + + - Fixed BUG#15383 - PreparedStatement.setObject() serializes + BigInteger as object, rather than sending as numeric value + (and is thus not complementary to .getObject() on an UNSIGNED + LONG type). + + - Fixed BUG#11874 - ResultSet.getShort() for UNSIGNED TINYINT + returned wrong values. + + - Fixed BUG#15676 - lib-nodist directory missing from + package breaks out-of-box build + + - Fixed BUG#15854 - DBMD.getColumns() returns wrong type for BIT. + + - Fixed BUG#16169 - ResultSet.getNativeShort() causes stack overflow error + via recurisve calls. + + - Fixed BUG#14938 - Unable to initialize character set mapping tables. + Removed reliance on .properties files to hold this information, as it + turns out to be too problematic to code around class loader hierarchies + that change depending on how an application is deployed. Moved information + back into the CharsetMapping class. + + - Fixed BUG#16841 - updatable result set doesn't return AUTO_INCREMENT + values for insertRow() when multiple column primary keys are used. (the + driver was checking for the existence of single-column primary keys and + an autoincrement value > 0 instead of a straightforward isAutoIncrement() + check). + + - Fixed BUG#17099 - Statement.getGeneratedKeys() throws NullPointerException + when no query has been processed. + + - Fixed BUG#13469 - Driver tries to call methods that don't exist on older and + newer versions of Log4j. The fix is not trying to auto-detect presense of log4j, + too many different incompatible versions out there in the wild to do this reliably. + + If you relied on autodetection before, you will need to add + "logger=com.mysql.jdbc.log.Log4JLogger" to your JDBC URL to enable Log4J usage, + or alternatively use the new "CommonsLogger" class to take care of this. + + - Added support for Apache Commons logging, use "com.mysql.jdbc.log.CommonsLogger" + as the value for the "logger" configuration property. + + - LogFactory now prepends "com.mysql.jdbc.log" to log class name if it can't be + found as-specified. This allows you to use "short names" for the built-in log + factories, for example "logger=CommonsLogger" instead of + "logger=com.mysql.jdbc.log.CommonsLogger". + + - Fixed BUG#15570 - ReplicationConnection incorrectly copies state, + doesn't transfer connection context correctly when transitioning between + the same read-only states. + + - Fixed BUG#18041 - Server-side prepared statements don't cause + truncation exceptions to be thrown when truncation happens. + + - Added performance feature, re-writing of batched executes for + Statement.executeBatch() (for all DML statements) and + PreparedStatement.executeBatch() (for INSERTs with VALUE clauses + only). Enable by using "rewriteBatchedStatements=true" in your JDBC URL. + + - Fixed BUG#17898 - registerOutParameter not working when some + parameters pre-populated. Still waiting for feedback from JDBC experts + group to determine what correct parameter count from getMetaData() + should be, however. + + - Fixed BUG#17587 - clearParameters() on a closed prepared statement + causes NPE. + + - Map "latin1" on MySQL server to CP1252 for MySQL > 4.1.0. + + - Added additional accessor and mutator methods on ConnectionProperties + so that DataSource users can use same naming as regular URL properties. + + - Fixed BUG#18740 - Data truncation and getWarnings() only returns last + warning in set. + + - Improved performance of retrieving BigDecimal, Time, Timestamp and Date + values from server-side prepared statements by creating fewer short-lived + instances of Strings when the native type is not an exact match for + the requested type. Fixes BUG#18496 for BigDecimals. + + - Fixed BUG#18554 - Aliased column names where length of name > 251 + are corrupted. + + - Fixed BUG#17450 - ResultSet.wasNull() not always reset + correctly for booleans when done via conversion for server-side + prepared statements. + + - Fixed BUG#16277 - Invalid classname returned for + RSMD.getColumnClassName() for BIGINT type. + + - Fixed case where driver wasn't reading server status correctly when + fetching server-side prepared statement rows, which in some cases + could cause warning counts to be off, or multiple result sets to not + be read off the wire. + + - Driver now aware of fix for BIT type metadata that went into + MySQL-5.0.21 for server not reporting length consistently (bug + number 13601). + + - Fixed BUG#19282 - ResultSet.wasNull() returns incorrect value + when extracting native string from server-side prepared statement + generated result set. + +11-30-05 - Version 3.1.12 + + - Fixed client-side prepared statement bug with embedded ? inside + quoted identifiers (it was recognized as a placeholder, when it + was not). + + - Don't allow executeBatch() for CallableStatements with registered + OUT/INOUT parameters (JDBC compliance). + + - Fall back to platform-encoding for URLDecoder.decode() when + parsing driver URL properties if the platform doesn't have a + two-argument version of this method. + + - Fixed BUG#14562 - Java type conversion may be incorrect for + mediumint. + + - Added configuration property "useGmtMillisForDatetimes" which + when set to true causes ResultSet.getDate(), .getTimestamp() to + return correct millis-since GMT when .getTime() is called on + the return value (currently default is "false" for legacy + behavior). + + - Fixed DatabaseMetaData.stores*Identifiers(): + + * if lower_case_table_names=0 (on server): + + storesLowerCaseIdentifiers() returns false + storesLowerCaseQuotedIdentifiers() returns false + storesMixedCaseIdentifiers() returns true + storesMixedCaseQuotedIdentifiers() returns true + storesUpperCaseIdentifiers() returns false + storesUpperCaseQuotedIdentifiers() returns true + + * if lower_case_table_names=1 (on server): + + storesLowerCaseIdentifiers() returns true + storesLowerCaseQuotedIdentifiers() returns true + storesMixedCaseIdentifiers() returns false + storesMixedCaseQuotedIdentifiers() returns false + storesUpperCaseIdentifiers() returns false + storesUpperCaseQuotedIdentifiers() returns true + + - Fixed BUG#14815 - DatabaseMetaData.getColumns() doesn't + return TABLE_NAME correctly. + + - Fixed BUG#14909 - escape processor replaces quote character + in quoted string with string delimiter. + + - Fixed BUG#12975 - OpenOffice expects + DBMD.supportsIntegrityEnhancementFacility() to return "true" + if foreign keys are supported by the datasource, even though + this method also covers support for check constraints, + which MySQL _doesn't_ have. Setting the configuration property + "overrideSupportsIntegrityEnhancementFacility" to "true" causes + the driver to return "true" for this method. + + - Added "com.mysql.jdbc.testsuite.url.default" system property to + set default JDBC url for testsuite (to speed up bug resolution + when I'm working in Eclipse). + + - Fixed BUG#14938 - Unable to initialize character set mapping + tables (due to J2EE classloader differences). + + - Fixed BUG#14972 - Deadlock while closing server-side prepared + statements from multiple threads sharing one connection. + + - Fixed BUG#12230 - logSlowQueries should give better info. + + - Fixed BUG#13775 - Extraneous sleep on autoReconnect. + + - Fixed BUG#15024 - Driver incorrectly closes streams passed as + arguments to PreparedStatements. Reverts to legacy behavior by + setting the JDBC configuration property "autoClosePStmtStreams" + to "true" (also included in the 3-0-Compat configuration "bundle"). + + - Fixed BUG#13048 - maxQuerySizeToLog is not respected. Added logging of + bound values for execute() phase of server-side prepared statements + when profileSQL=true as well. + + - Fixed BUG#15065 - Usage advisor complains about unreferenced + columns, even though they've been referenced. + + - Don't increase timeout for failover/reconnect (BUG#6577) + + - Process escape tokens in Connection.prepareStatement(...), fix + for BUG#15141. You can disable this behavior by setting + the JDBC URL configuration property "processEscapeCodesForPrepStmts" + to "false". + + - Fixed BUG#13255 - Reconnect during middle of executeBatch() + should not occur if autoReconnect is enabled. + +10-07-05 - Version 3.1.11 + + - Fixed BUG#11629 - Spurious "!" on console when character + encoding is "utf8". + + - Fixed statements generated for testcases missing ";" for + "plain" statements. + + - Fixed BUG#11663 - Incorrect generation of testcase scripts + for server-side prepared statements. + + - Fixed regression caused by fix for BUG#11552 that caused driver + to return incorrect values for unsigned integers when those + integers where within the range of the positive signed type. + + - Moved source code to svn repo. + + - Fixed BUG#11797 - Escape tokenizer doesn't respect stacked single quotes + for escapes. + + - GEOMETRY type not recognized when using server-side prepared statements. + + - Fixed BUG#11879 -- ReplicationConnection won't switch to slave, throws + "Catalog can't be null" exception. + + - Fixed BUG#12218, properties shared between master and slave with + replication connection. + + - Fixed BUG#10630, Statement.getWarnings() fails with NPE if statement + has been closed. + + - Only get char[] from SQL in PreparedStatement.ParseInfo() when needed. + + - Fixed BUG#12104 - Geometry types not handled with server-side prepared + statements. + + - Fixed BUG#11614 - StringUtils.getBytes() doesn't work when using + multibyte character encodings and a length in _characters_ is + specified. + + - Fixed BUG#11798 - Pstmt.setObject(...., Types.BOOLEAN) throws exception. + + - Fixed BUG#11976 - maxPerformance.properties mis-spells + "elideSetAutoCommits". + + - Fixed BUG#11575 -- DBMD.storesLower/Mixed/UpperIdentifiers() + reports incorrect values for servers deployed on Windows. + + - Fixed BUG#11190 - ResultSet.moveToCurrentRow() fails to work when + preceeded by a call to ResultSet.moveToInsertRow(). + + - Fixed BUG#11115, VARBINARY data corrupted when using server-side + prepared statements and .setBytes(). + + - Fixed BUG#12229 - explainSlowQueries hangs with server-side + prepared statements. + + - Fixed BUG#11498 - Escape processor didn't honor strings demarcated + with double quotes. + + - Lifted restriction of changing streaming parameters with server-side + prepared statements. As long as _all_ streaming parameters were set + before execution, .clearParameters() does not have to be called. + (due to limitation of client/server protocol, prepared statements + can not reset _individual_ stream data on the server side). + + - Reworked Field class, *Buffer, and MysqlIO to be aware of field + lengths > Integer.MAX_VALUE. + + - Updated DBMD.supportsCorrelatedQueries() to return true for versions > + 4.1, supportsGroupByUnrelated() to return true and + getResultSetHoldability() to return HOLD_CURSORS_OVER_COMMIT. + + - Fixed BUG#12541 - Handling of catalog argument in + DatabaseMetaData.getIndexInfo(), which also means changes to the following + methods in DatabaseMetaData: + + - getBestRowIdentifier() + - getColumns() + - getCrossReference() + - getExportedKeys() + - getImportedKeys() + - getIndexInfo() + - getPrimaryKeys() + - getProcedures() (and thus indirectly getProcedureColumns()) + - getTables() + + The "catalog" argument in all of these methods now behaves in the following + way: + + - Specifying NULL means that catalog will not be used to filter the + results (thus all databases will be searched), unless you've + set "nullCatalogMeansCurrent=true" in your JDBC URL properties. + + - Specifying "" means "current" catalog, even though this isn't quite + JDBC spec compliant, it's there for legacy users. + + - Specifying a catalog works as stated in the API docs. + + - Made Connection.clientPrepare() available from "wrapped" connections + in the jdbc2.optional package (connections built by + ConnectionPoolDataSource instances). + + - Added Connection.isMasterConnection() for clients to be able to determine + if a multi-host master/slave connection is connected to the first host + in the list. + + - Fixed BUG#12753 - Tokenizer for "=" in URL properties was causing + sessionVariables=.... to be parameterized incorrectly. + + - Fixed BUG#11781, foreign key information that is quoted is + parsed incorrectly when DatabaseMetaData methods use that + information. + + - The "sendBlobChunkSize" property is now clamped to "max_allowed_packet" + with consideration of stream buffer size and packet headers to avoid + PacketTooBigExceptions when "max_allowed_packet" is similar in size + to the default "sendBlobChunkSize" which is 1M. + + - CallableStatement.clearParameters() now clears resources associated + with INOUT/OUTPUT parameters as well as INPUT parameters. + + - Fixed BUG#12417 - Connection.prepareCall() is database name + case-sensitive (on Windows systems). + + - Fixed BUG#12752 - Cp1251 incorrectly mapped to win1251 for + servers newer than 4.0.x. + + - Fixed BUG#12970 - java.sql.Types.OTHER returned for + BINARY and VARBINARY columns when using + DatabaseMetaData.getColumns(). + + - ServerPreparedStatement.getBinding() now checks if the statement + is closed before attempting to reference the list of parameter + bindings, to avoid throwing a NullPointerException. + + - Fixed BUG#13277 - ResultSetMetaData from + Statement.getGeneratedKeys() caused NullPointerExceptions to be + thrown whenever a method that required a connection reference + was called. + + - Removed support for java.nio I/O. Too many implementations + turned out to be buggy, and there was no performance difference + since MySQL is a blocking protocol anyway. + +06-23-05 - Version 3.1.10-stable + + - Fixed connecting without a database specified raised an exception + in MysqlIO.changeDatabaseTo(). + + - Initial implemention of ParameterMetadata for + PreparedStatement.getParameterMetadata(). Only works fully + for CallableStatements, as current server-side prepared statements + return every parameter as a VARCHAR type. + + - Fixed BUG#11552 - Server-side prepared statements return incorrect + values for unsigned TINYINT, SMALLINT, INT and Long. + + - Fixed BUG#11540 - Incorrect year conversion in setDate(..) for + system that use B.E. year in default locale. + +06-22-05 - Version 3.1.9-stable + + - Overhaul of character set configuration, everything now + lives in a properties file. + + - Driver now correctly uses CP932 if available on the server + for Windows-31J, CP932 and MS932 java encoding names, + otherwise it resorts to SJIS, which is only a close + approximation. Currently only MySQL-5.0.3 and newer (and + MySQL-4.1.12 or .13, depending on when the character set + gets backported) can reliably support any variant of CP932. + + - Fixed BUG#9064 - com.mysql.jdbc.PreparedStatement.ParseInfo + does unnecessary call to toCharArray(). + + - Fixed Bug#10144 - Memory leak in ServerPreparedStatement if + serverPrepare() fails. + + - Actually write manifest file to correct place so it ends up + in the binary jar file. + + - Added "createDatabaseIfNotExist" property (default is "false"), + which will cause the driver to ask the server to create the + database specified in the URL if it doesn't exist. You must have + the appropriate privileges for database creation for this to + work. + + - Fixed BUG#10156 - Unsigned SMALLINT treated as signed for ResultSet.getInt(), + fixed all cases for UNSIGNED integer values and server-side prepared statements, + as well as ResultSet.getObject() for UNSIGNED TINYINT. + + - Fixed BUG#10155, double quotes not recognized when parsing + client-side prepared statements. + + - Made enableStreamingResults() visible on + com.mysql.jdbc.jdbc2.optional.StatementWrapper. + + - Made ServerPreparedStatement.asSql() work correctly so auto-explain + functionality would work with server-side prepared statements. + + - Made JDBC2-compliant wrappers public in order to allow access to + vendor extensions. + + - Cleaned up logging of profiler events, moved code to dump a profiler + event as a string to com.mysql.jdbc.log.LogUtils so that third + parties can use it. + + - DatabaseMetaData.supportsMultipleOpenResults() now returns true. The + driver has supported this for some time, DBMD just missed that fact. + + - Fixed BUG#10310 - Driver doesn't support {?=CALL(...)} for calling + stored functions. This involved adding support for function retrieval + to DatabaseMetaData.getProcedures() and getProcedureColumns() as well. + + - Fixed BUG#10485, SQLException thrown when retrieving YEAR(2) + with ResultSet.getString(). The driver will now always treat YEAR types + as java.sql.Dates and return the correct values for getString(). + Alternatively, the "yearIsDateType" connection property can be set to + "false" and the values will be treated as SHORTs. + + - The datatype returned for TINYINT(1) columns when "tinyInt1isBit=true" + (the default) can be switched between Types.BOOLEAN and Types.BIT + using the new configuration property "transformedBitIsBoolean", which + defaults to "false". If set to "false" (the default), + DatabaseMetaData.getColumns() and ResultSetMetaData.getColumnType() + will return Types.BOOLEAN for TINYINT(1) columns. If "true", + Types.BOOLEAN will be returned instead. Irregardless of this configuration + property, if "tinyInt1isBit" is enabled, columns with the type TINYINT(1) + will be returned as java.lang.Boolean instances from + ResultSet.getObject(..), and ResultSetMetaData.getColumnClassName() + will return "java.lang.Boolean". + + - Fixed BUG#10496 - SQLException is thrown when using property + "characterSetResults" with cp932 or eucjpms. + + - Reorganized directory layout, sources now in "src" folder, + don't pollute parent directory when building, now output goes + to "./build", distribution goes to "./dist". + + - Added support/bug hunting feature that generates .sql test + scripts to STDERR when "autoGenerateTestcaseScript" is set + to "true". + + - Fixed BUG#10850 - 0-length streams not sent to server when + using server-side prepared statements. + + - Setting "cachePrepStmts=true" now causes the Connection to also + cache the check the driver performs to determine if a prepared + statement can be server-side or not, as well as caches server-side + prepared statements for the lifetime of a connection. As before, + the "prepStmtCacheSize" parameter controls the size of these + caches. + + - Try to handle OutOfMemoryErrors more gracefully. Although not + much can be done, they will in most cases close the connection + they happened on so that further operations don't run into + a connection in some unknown state. When an OOM has happened, + any further operations on the connection will fail with a + "Connection closed" exception that will also list the OOM exception + as the reason for the implicit connection close event. + + - Don't send COM_RESET_STMT for each execution of a server-side + prepared statement if it isn't required. + + - Driver detects if you're running MySQL-5.0.7 or later, and does + not scan for "LIMIT ?[,?]" in statements being prepared, as the + server supports those types of queries now. + + - Fixed BUG#11115, Varbinary data corrupted when using server-side + prepared statements and ResultSet.getBytes(). + + - Connection.setCatalog() is now aware of the "useLocalSessionState" + configuration property, which when set to true will prevent + the driver from sending "USE ..." to the server if the requested + catalog is the same as the current catalog. + + - Added the following configuration bundles, use one or many via + the "useConfigs" configuration property: + + * maxPerformance -- maximum performance without being reckless + * solarisMaxPerformance -- maximum performance for Solaris, + avoids syscalls where it can + * 3-0-Compat -- Compatibility with Connector/J 3.0.x functionality + + - Added "maintainTimeStats" configuration property (defaults to "true"), + which tells the driver whether or not to keep track of the last query time + and the last successful packet sent to the server's time. If set to + false, removes two syscalls per query. + + - Fixed BUG#11259, autoReconnect ping causes exception on connection + startup. + + - Fixed BUG#11360 Connector/J dumping query into SQLException twice + + - Fixed PreparedStatement.setClob() not accepting null as a parameter. + + - Fixed BUG#11411 - Production package doesn't include JBoss integration + classes. + + - Removed nonsensical "costly type conversion" warnings when using + usage advisor. + +04-14-05 - Version 3.1.8-stable + + - Fixed DatabaseMetaData.getTables() returning views when they were + not asked for as one of the requested table types. + + - Added support for new precision-math DECIMAL type in MySQL >= 5.0.3. + + - Fixed ResultSet.getTime() on a NULL value for server-side prepared + statements throws NPE. + + - Made Connection.ping() a public method. + + - Fixed Bug#8868, DATE_FORMAT() queries returned as BLOBs from getObject(). + + - ServerPreparedStatements now correctly 'stream' BLOB/CLOB data to the + server. You can configure the threshold chunk size using the + JDBC URL property 'blobSendChunkSize' (the default is one megabyte). + + - BlobFromLocator now uses correct identifier quoting when generating + prepared statements. + + - Server-side session variables can be preset at connection time by + passing them as a comma-delimited list for the connection property + 'sessionVariables'. + + - Fixed regression in ping() for users using autoReconnect=true. + + - Fixed BUG#9040 - PreparedStatement.addBatch() doesn't work with server-side + prepared statements and streaming BINARY data. + + - Fixed BUG#8800 - DBMD.supportsMixedCase*Identifiers() returns wrong + value on servers running on case-sensitive filesystems. + + - Fixed BUG#9206, can not use 'UTF-8' for characterSetResults + configuration property. + + - Fixed BUG#9236, a continuation of BUG#8868, where functions used in queries + that should return non-string types when resolved by temporary tables suddenly + become opaque binary strings (work-around for server limitation). Also fixed + fields with type of CHAR(n) CHARACTER SET BINARY to return correct/matching + classes for RSMD.getColumnClassName() and ResultSet.getObject(). + + - Fixed BUG#8792 - DBMD.supportsResultSetConcurrency() not returning + true for forward-only/read-only result sets (we obviously support this). + + - Fixed BUG#8803, 'DATA_TYPE' column from DBMD.getBestRowIdentifier() + causes ArrayIndexOutOfBoundsException when accessed (and in fact, didn't + return any value). + + - Check for empty strings ('') when converting char/varchar column data to numbers, + throw exception if 'emptyStringsConvertToZero' configuration property is set + to 'false' (for backwards-compatibility with 3.0, it is now set to 'true' + by default, but will most likely default to 'false' in 3.2). + + - Fixed BUG#9320 - PreparedStatement.getMetaData() inserts blank row in database + under certain conditions when not using server-side prepared statements. + + - Connection.canHandleAsPreparedStatement() now makes 'best effort' to distinguish + LIMIT clauses with placeholders in them from ones without in order to have fewer + false positives when generating work-arounds for statements the server cannot + currently handle as server-side prepared statements. + + - Fixed build.xml to not compile log4j logging if log4j not available. + + - Added support for the c3p0 connection pool's (http://c3p0.sf.net/) + validation/connection checker interface which uses the lightweight + 'COM_PING' call to the server if available. To use it, configure your + c3p0 connection pool's 'connectionTesterClassName' property to use + 'com.mysql.jdbc.integration.c3p0.MysqlConnectionTester'. + + - Better detection of LIMIT inside/outside of quoted strings so that + the driver can more correctly determine whether a prepared statement + can be prepared on the server or not. + + - Fixed BUG#9319 - Stored procedures with same name in + different databases confuse the driver when it tries to determine + parameter counts/types. + + - Added finalizers to ResultSet and Statement implementations to be JDBC + spec-compliant, which requires that if not explicitly closed, these + resources should be closed upon garbage collection. + + - Fixed BUG#9682 - Stored procedures with DECIMAL parameters with + storage specifications that contained "," in them would fail. + + - PreparedStatement.setObject(int, Object, int type, int scale) now + uses scale value for BigDecimal instances. + + - Fixed BUG#9704 - Statement.getMoreResults() could throw NPE when + existing result set was .close()d. + + - The performance metrics feature now gathers information about + number of tables referenced in a SELECT. + + - The logging system is now automatically configured. If the value has + been set by the user, via the URL property "logger" or the system + property "com.mysql.jdbc.logger", then use that, otherwise, autodetect + it using the following steps: + + Log4j, if it's available, + Then JDK1.4 logging, + Then fallback to our STDERR logging. + + - Fixed BUG#9778, DBMD.getTables() shouldn't return tables if views + are asked for, even if the database version doesn't support views. + + - Fixed driver not returning 'true' for '-1' when ResultSet.getBoolean() + was called on result sets returned from server-side prepared statements. + + - Added a Manifest.MF file with implementation information to the .jar + file. + + - More tests in Field.isOpaqueBinary() to distinguish opaque binary (i.e. + fields with type CHAR(n) and CHARACTER SET BINARY) from output of + various scalar and aggregate functions that return strings. + + - Fixed BUG#9917 - Should accept null for catalog (meaning use current) + in DBMD methods, even though it's not JDBC-compliant for legacy's sake. + Disable by setting connection property "nullCatalogMeansCurrent" to "false" + (which will be the default value in C/J 3.2.x). + + - Fixed BUG#9769 - Should accept null for name patterns in DBMD (meaning "%"), + even though it isn't JDBC compliant, for legacy's sake. Disable by setting + connection property "nullNamePatternMatchesAll" to "false" (which will be + the default value in C/J 3.2.x). + +02-18-05 - Version 3.1.7-stable + + + - Fixed BUG#7686, Timestamp key column data needed "_binary'" + stripped for UpdatableResultSet.refreshRow(). + + - Fixed BUG#7715 - Timestamps converted incorrectly to strings + with Server-side prepared statements and updatable result sets. + + - Detect new sql_mode variable in string form (it used to be + integer) and adjust quoting method for strings appropriately. + + - Added 'holdResultsOpenOverStatementClose' property (default is + false), that keeps result sets open over statement.close() or new + execution on same statement (suggested by Kevin Burton). + + - Fixed BUG#7952 -- Infinite recursion when 'falling back' to master + in failover configuration. + + - Disable multi-statements (if enabled) for MySQL-4.1 versions prior + to version 4.1.10 if the query cache is enabled, as the server + returns wrong results in this configuration. + + - Fixed duplicated code in configureClientCharset() that prevented + useOldUTF8Behavior=true from working properly. + + - Removed 'dontUnpackBinaryResults' functionality, the driver now + always stores results from server-side prepared statements as-is + from the server and unpacks them on demand. + + - Fixed BUG#8096 where emulated locators corrupt binary data + when using server-side prepared statements. + + - Fixed synchronization issue with + ServerPreparedStatement.serverPrepare() that could cause + deadlocks/crashes if connection was shared between threads. + + - By default, the driver now scans SQL you are preparing via all + variants of Connection.prepareStatement() to determine if it is a + supported type of statement to prepare on the server side, and if + it is not supported by the server, it instead prepares it as a + client-side emulated prepared statement (BUG#4718). You can + disable this by passing 'emulateUnsupportedPstmts=false' in your + JDBC URL. + + - Remove _binary introducer from parameters used as in/out + parameters in CallableStatement. + + - Always return byte[]s for output parameters registered as *BINARY. + + - Send correct value for 'boolean' "true" to server for + PreparedStatement.setObject(n, "true", Types.BIT). + + - Fixed bug with Connection not caching statements from + prepareStatement() when the statement wasn't a server-side + prepared statement. + + - Choose correct 'direction' to apply time adjustments when both + client and server are in GMT timezone when using + ResultSet.get(..., cal) and PreparedStatement.set(...., cal). + + - Added 'dontTrackOpenResources' option (default is false, to be + JDBC compliant), which helps with memory use for non-well-behaved + apps (i.e applications which don't close Statements when they + should). + + - Fixed BUG#8428 - ResultSet.getString() doesn't maintain format + stored on server, bug fix only enabled when 'noDatetimeStringSync' + property is set to 'true' (the default is 'false'). + + - Fixed NPE in ResultSet.realClose() when using usage advisor and + result set was already closed. + + - Fixed BUG#8487 - PreparedStatements not creating streaming result + sets. + + - Don't pass NULL to String.valueOf() in + ResultSet.getNativeConvertToString(), as it stringifies it (i.e. + returns "null"), which is not correct for the method in question. + + - Fixed BUG#8484 - ResultSet.getBigDecimal() throws exception + when rounding would need to occur to set scale. The driver now + chooses a rounding mode of 'half up' if non-rounding + BigDecimal.setScale() fails. + + - Added 'useLocalSessionState' configuration property, when set to + 'true' the JDBC driver trusts that the application is well-behaved + and only sets autocommit and transaction isolation levels using + the methods provided on java.sql.Connection, and therefore can + manipulate these values in many cases without incurring + round-trips to the database server. + + - Added enableStreamingResults() to Statement for connection pool + implementations that check Statement.setFetchSize() for + specification-compliant values. Call Statement.setFetchSize(>=0) + to disable the streaming results for that statement. + + - Added support for BIT type in MySQL-5.0.3. The driver will treat + BIT(1-8) as the JDBC standard BIT type (which maps to + java.lang.Boolean), as the server does not currently send enough + information to determine the size of a bitfield when < 9 bits are + declared. BIT(>9) will be treated as VARBINARY, and will return + byte[] when getObject() is called. + +12-23-04 - Version 3.1.6-stable + + - Fixed hang on SocketInputStream.read() with Statement.setMaxRows() and + multiple result sets when driver has to truncate result set directly, + rather than tacking a 'LIMIT n' on the end of it. + + - Fixed BUG#7026 - DBMD.getProcedures() doesn't respect catalog parameter. + + - Respect bytes-per-character for RSMD.getPrecision(). + +12-02-04 - Version 3.1.5-gamma + + - Fix comparisons made between string constants and dynamic strings that + are either toUpperCase()d or toLowerCase()d to use Locale.ENGLISH, as + some locales 'override' case rules for English. Also use + StringUtils.indexOfIgnoreCase() instead of .toUpperCase().indexOf(), + avoids creating a very short-lived transient String instance. + + - Fixed BUG#5235 - Server-side prepared statements did not honor + 'zeroDateTimeBehavior' property, and would cause class-cast + exceptions when using ResultSet.getObject(), as the all-zero string + was always returned. + + - Fixed batched updates with server prepared statements weren't looking if + the types had changed for a given batched set of parameters compared + to the previous set, causing the server to return the error + 'Wrong arguments to mysql_stmt_execute()'. + + - Handle case when string representation of timestamp contains trailing '.' + with no numbers following it. + + - Fixed BUG#5706 - Inefficient detection of pre-existing string instances + in ResultSet.getNativeString(). + + - Don't throw exceptions for Connection.releaseSavepoint(). + + - Use a per-session Calendar instance by default when decoding dates + from ServerPreparedStatements (set to old, less performant behavior by + setting property 'dynamicCalendars=true'). + + - Added experimental configuration property 'dontUnpackBinaryResults', + which delays unpacking binary result set values until they're asked for, + and only creates object instances for non-numerical values (it is set + to 'false' by default). For some usecase/jvm combinations, this is + friendlier on the garbage collector. + + - Fixed BUG#5729 - UNSIGNED BIGINT unpacked incorrectly from + server-side prepared statement result sets. + + - Fixed BUG#6225 - ServerSidePreparedStatement allocating short-lived + objects un-necessarily. + + - Removed un-wanted new Throwable() in ResultSet constructor due to bad + merge (caused a new object instance that was never used for every result + set created) - Found while profiling for BUG#6359. + + - Fixed too-early creation of StringBuffer in EscapeProcessor.escapeSQL(), + also return String when escaping not needed (to avoid unnecssary object + allocations). Found while profiling for BUG#6359. + + - Use null-safe-equals for key comparisons in updatable result sets. + + - Fixed BUG#6537, SUM() on Decimal with server-side prepared statement ignores + scale if zero-padding is needed (this ends up being due to conversion to DOUBLE + by server, which when converted to a string to parse into BigDecimal, loses all + 'padding' zeros). + + - Use DatabaseMetaData.getIdentifierQuoteString() when building DBMD + queries. + + - Use 1MB packet for sending file for LOAD DATA LOCAL INFILE if that + is < 'max_allowed_packet' on server. + + - Fixed BUG#6399, ResultSetMetaData.getColumnDisplaySize() returns incorrect + values for multibyte charsets. + + - Make auto-deserialization of java.lang.Objects stored in BLOBs + configurable via 'autoDeserialize' property (defaults to 'false'). + + - Re-work Field.isOpaqueBinary() to detect 'CHAR(n) CHARACTER SET BINARY' + to support fixed-length binary fields for ResultSet.getObject(). + + - Use our own implementation of buffered input streams to get around + blocking behavior of java.io.BufferedInputStream. Disable this with + 'useReadAheadInput=false'. + + - Fixed BUG#6348, failing to connect to the server when one of the + addresses for the given host name is IPV6 (which the server does + not yet bind on). The driver now loops through _all_ IP addresses + for a given host, and stops on the first one that accepts() a + socket.connect(). + +09-04-04 - Version 3.1.4-beta + + - Fixed BUG#4510 - connector/j 3.1.3 beta does not handle integers + correctly (caused by changes to support unsigned reads in + Buffer.readInt() -> Buffer.readShort()). + + - Added support in DatabaseMetaData.getTables() and getTableTypes() + for VIEWs which are now available in MySQL server version 5.0.x. + + - Fixed BUG#4642 -- ServerPreparedStatement.execute*() sometimes + threw ArrayIndexOutOfBoundsException when unpacking field metadata. + + - Optimized integer number parsing, enable 'old' slower integer parsing + using JDK classes via 'useFastIntParsing=false' property. + + - Added 'useOnlyServerErrorMessages' property, which causes message text + in exceptions generated by the server to only contain the text sent by + the server (as opposed to the SQLState's 'standard' description, followed + by the server's error message). This property is set to 'true' by default. + + - Fixed BUG#4689 - ResultSet.wasNull() does not work for primatives if a + previous null was returned. + + - Track packet sequence numbers if enablePacketDebug=true, and throw an + exception if packets received out-of-order. + + - Fixed BUG#4482, ResultSet.getObject() returns wrong type for strings + when using prepared statements. + + - Calling MysqlPooledConnection.close() twice (even though an application + error), caused NPE. Fixed. + + - Fixed BUG#5012 -- ServerPreparedStatements dealing with return of + DECIMAL type don't work. + + - Fixed BUG#5032 -- ResultSet.getObject() doesn't return + type Boolean for pseudo-bit types from prepared statements on 4.1.x + (shortcut for avoiding extra type conversion when using binary-encoded + result sets obscurred test in getObject() for 'pseudo' bit type) + + - You can now use URLs in 'LOAD DATA LOCAL INFILE' statements, and the + driver will use Java's built-in handlers for retreiving the data and + sending it to the server. This feature is not enabled by default, + you must set the 'allowUrlInLocalInfile' connection property to 'true'. + + - The driver is more strict about truncation of numerics on + ResultSet.get*(), and will throw a SQLException when truncation is + detected. You can disable this by setting 'jdbcCompliantTruncation' to + false (it is enabled by default, as this functionality is required + for JDBC compliance). + + - Added three ways to deal with all-zero datetimes when reading them from + a ResultSet, 'exception' (the default), which throws a SQLException + with a SQLState of 'S1009', 'convertToNull', which returns NULL instead of + the date, and 'round', which rounds the date to the nearest closest value + which is '0001-01-01'. + + - Fixed ServerPreparedStatement to read prepared statement metadata off + the wire, even though it's currently a placeholder instead of using + MysqlIO.clearInputStream() which didn't work at various times because + data wasn't available to read from the server yet. This fixes sporadic + errors users were having with ServerPreparedStatements throwing + ArrayIndexOutOfBoundExceptions. + + - Use com.mysql.jdbc.Message's classloader when loading resource bundle, + should fix sporadic issues when the caller's classloader can't locate + the resource bundle. + +07-07-04 - Version 3.1.3-beta + + - Mangle output parameter names for CallableStatements so they + will not clash with user variable names. + + - Added support for INOUT parameters in CallableStatements. + + - Fix for BUG#4119, null bitmask sent for server-side prepared + statements was incorrect. + + - Use SQL Standard SQL states by default, unless 'useSqlStateCodes' + property is set to 'false'. + + - Added packet debuging code (see the 'enablePacketDebug' property + documentation). + + - Added constants for MySQL error numbers (publicly-accessible, + see com.mysql.jdbc.MysqlErrorNumbers), and the ability to + generate the mappings of vendor error codes to SQLStates + that the driver uses (for documentation purposes). + + - Externalized more messages (on-going effort). + + - Fix for BUG#4311 - Error in retrieval of mediumint column with + prepared statements and binary protocol. + + - Support new timezone variables in MySQL-4.1.3 when + 'useTimezone=true' + + - Support for unsigned numerics as return types from prepared statements. + This also causes a change in ResultSet.getObject() for the 'bigint unsigned' + type, which used to return BigDecimal instances, it now returns instances + of java.lang.BigInteger. + +06-09-04 - Version 3.1.2-alpha + + - Fixed stored procedure parameter parsing info when size was + specified for a parameter (i.e. char(), varchar()). + + - Enabled callable statement caching via 'cacheCallableStmts' + property. + + - Fixed case when no output parameters specified for a + stored procedure caused a bogus query to be issued + to retrieve out parameters, leading to a syntax error + from the server. + + - Fixed case when no parameters could cause a NullPointerException + in CallableStatement.setOutputParameters(). + + - Removed wrapping of exceptions in MysqlIO.changeUser(). + + - Fixed sending of split packets for large queries, enabled nio + ability to send large packets as well. + + - Added .toString() functionality to ServerPreparedStatement, + which should help if you're trying to debug a query that is + a prepared statement (it shows SQL as the server would process). + + - Added 'gatherPerformanceMetrics' property, along with properties + to control when/where this info gets logged (see docs for more + info). + + - ServerPreparedStatements weren't actually de-allocating + server-side resources when .close() was called. + + - Added 'logSlowQueries' property, along with property + 'slowQueriesThresholdMillis' to control when a query should + be considered 'slow'. + + - Correctly map output parameters to position given in + prepareCall() vs. order implied during registerOutParameter() - + fixes BUG#3146. + + - Correctly detect initial character set for servers >= 4.1.0 + + - Cleaned up detection of server properties. + + - Support placeholder for parameter metadata for server >= 4.1.2 + + - Fix for BUG#3539 getProcedures() does not return any procedures in + result set + + - Fix for BUG#3540 getProcedureColumns() doesn't work with wildcards + for procedure name + + - Fixed BUG#3520 -- DBMD.getSQLStateType() returns incorrect value. + + - Added 'connectionCollation' property to cause driver to issue + 'set collation_connection=...' query on connection init if default + collation for given charset is not appropriate. + + - Fixed DatabaseMetaData.getProcedures() when run on MySQL-5.0.0 (output of + 'show procedure status' changed between 5.0.1 and 5.0.0. + + - Fixed BUG#3804 -- getWarnings() returns SQLWarning instead of DataTruncation + + - Don't enable server-side prepared statements for server version 5.0.0 or 5.0.1, + as they aren't compatible with the '4.1.2+' style that the driver uses (the driver + expects information to come back that isn't there, so it hangs). + + +02-14-04 - Version 3.1.1-alpha + + - Fixed bug with UpdatableResultSets not using client-side + prepared statements. + + - Fixed character encoding issues when converting bytes to + ASCII when MySQL doesn't provide the character set, and + the JVM is set to a multibyte encoding (usually affecting + retrieval of numeric values). + + - Unpack 'unknown' data types from server prepared statements + as Strings. + + - Implemented long data (Blobs, Clobs, InputStreams, Readers) + for server prepared statements. + + - Implemented Statement.getWarnings() for MySQL-4.1 and newer + (using 'SHOW WARNINGS'). + + - Default result set type changed to TYPE_FORWARD_ONLY + (JDBC compliance). + + - Centralized setting of result set type and concurrency. + + - Re-factored how connection properties are set and exposed + as DriverPropertyInfo as well as Connection and DataSource + properties. + + - Support for NIO. Use 'useNIO=true' on platforms that support + NIO. + + - Support for SAVEPOINTs (MySQL >= 4.0.14 or 4.1.1). + + - Support for mysql_change_user()...See the changeUser() method + in com.mysql.jdbc.Connection. + + - Reduced number of methods called in average query to be more + efficient. + + - Prepared Statements will be re-prepared on auto-reconnect. Any errors + encountered are postponed until first attempt to re-execute the + re-prepared statement. + + - Ensure that warnings are cleared before executing queries + on prepared statements, as-per JDBC spec (now that we support + warnings). + + - Support 'old' profileSql capitalization in ConnectionProperties. + This property is deprecated, you should use 'profileSQL' if possible. + + - Optimized Buffer.readLenByteArray() to return shared empty byte array + when length is 0. + + - Allow contents of PreparedStatement.setBlob() to be retained + between calls to .execute*(). + + - Deal with 0-length tokens in EscapeProcessor (caused by callable + statement escape syntax). + + - Check for closed connection on delete/update/insert row operations in + UpdatableResultSet. + + - Fix support for table aliases when checking for all primary keys in + UpdatableResultSet. + + - Removed useFastDates connection property. + + - Correctly initialize datasource properties from JNDI Refs, including + explicitly specified URLs. + + - DatabaseMetaData now reports supportsStoredProcedures() for + MySQL versions >= 5.0.0 + + - Fixed stack overflow in Connection.prepareCall() (bad merge). + + - Fixed IllegalAccessError to Calendar.getTimeInMillis() in DateTimeValue + (for JDK < 1.4). + + - Fix for BUG#1673, where DatabaseMetaData.getColumns() is not + returning correct column ordinal info for non '%' column name patterns. + + - Merged fix of datatype mapping from MySQL type 'FLOAT' to + java.sql.Types.REAL from 3.0 branch. + + - Detect collation of column for RSMD.isCaseSensitive(). + + - Fixed sending of queries > 16M. + + - Added named and indexed input/output parameter support to CallableStatement. + MySQL-5.0.x or newer. + + - Fixed NullPointerException in ServerPreparedStatement.setTimestamp(), + as well as year and month descrepencies in + ServerPreparedStatement.setTimestamp(), setDate(). + + - Added ability to have multiple database/JVM targets for compliance + and regression/unit tests in build.xml. + + - Fixed NPE and year/month bad conversions when accessing some + datetime functionality in ServerPreparedStatements and their + resultant result sets. + + - Display where/why a connection was implicitly closed (to + aid debugging). + + - CommunicationsException implemented, that tries to determine + why communications was lost with a server, and displays + possible reasons when .getMessage() is called. + + - Fixed BUG#2359, NULL values for numeric types in binary + encoded result sets causing NullPointerExceptions. + + - Implemented Connection.prepareCall(), and DatabaseMetaData. + getProcedures() and getProcedureColumns(). + + - Reset 'long binary' parameters in ServerPreparedStatement when + clearParameters() is called, by sending COM_RESET_STMT to the + server. + + - Merged prepared statement caching, and .getMetaData() support + from 3.0 branch. + + - Fixed off-by-1900 error in some cases for + years in TimeUtil.fastDate/TimeCreate() when unpacking results + from server-side prepared statements. + + - Fixed BUG#2502 -- charset conversion issue in getTables(). + + - Implemented multiple result sets returned from a statement + or stored procedure. + + - Fixed BUG#2606 -- Server side prepared statements not returning + datatype 'YEAR' correctly. + + - Enabled streaming of result sets from server-side prepared + statements. + + - Fixed BUG#2623 -- Class-cast exception when using + scrolling result sets and server-side prepared statements. + + - Merged unbuffered input code from 3.0. + + - Fixed ConnectionProperties that weren't properly exposed + via accessors, cleaned up ConnectionProperties code. + + - Fixed BUG#2671, NULL fields not being encoded correctly in + all cases in server side prepared statements. + + - Fixed rare buffer underflow when writing numbers into buffers + for sending prepared statement execution requests. + + - Use DocBook version of docs for shipped versions of drivers. + + +02-18-03 - Version 3.1.0-alpha + + - Added 'requireSSL' property. + + - Added 'useServerPrepStmts' property (default 'false'). The + driver will use server-side prepared statements when the + server version supports them (4.1 and newer) when this + property is set to 'true'. It is currently set to 'false' + by default until all bind/fetch functionality has been + implemented. Currently only DML prepared statements are + implemented for 4.1 server-side prepared statements. + + - Track open Statements, close all when Connection.close() + is called (JDBC compliance). + +06-22-05 - Version 3.0.17-ga + + - Fixed BUG#5874, Timestamp/Time conversion goes in the wrong 'direction' + when useTimeZone='true' and server timezone differs from client timezone. + + - Fixed BUG#7081, DatabaseMetaData.getIndexInfo() ignoring 'unique' + parameter. + + - Support new protocol type 'MYSQL_TYPE_VARCHAR'. + + - Added 'useOldUTF8Behavoior' configuration property, which causes + JDBC driver to act like it did with MySQL-4.0.x and earlier when + the character encoding is 'utf-8' when connected to MySQL-4.1 or + newer. + + - Fixed BUG#7316 - Statements created from a pooled connection were + returning physical connection instead of logical connection when + getConnection() was called. + + - Fixed BUG#7033 - PreparedStatements don't encode Big5 (and other + multibyte) character sets correctly in static SQL strings. + + - Fixed BUG#6966, connections starting up failed-over (due to down master) + never retry master. + + - Fixed BUG#7061, PreparedStatement.fixDecimalExponent() adding extra + '+', making number unparseable by MySQL server. + + - Fixed BUG#7686, Timestamp key column data needed "_binary'" stripped for + UpdatableResultSet.refreshRow(). + + - Backported SQLState codes mapping from Connector/J 3.1, enable with + 'useSqlStateCodes=true' as a connection property, it defaults to + 'false' in this release, so that we don't break legacy applications (it + defaults to 'true' starting with Connector/J 3.1). + + - Fixed BUG#7601, PreparedStatement.fixDecimalExponent() adding extra + '+', making number unparseable by MySQL server. + + - Escape sequence {fn convert(..., type)} now supports ODBC-style types + that are prepended by 'SQL_'. + + - Fixed duplicated code in configureClientCharset() that prevented + useOldUTF8Behavior=true from working properly. + + - Handle streaming result sets with > 2 billion rows properly by fixing + wraparound of row number counter. + + - Fixed BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as + aliases for sjis. + + - Fixed BUG#6549 (while fixing #7607), adding 'CP943' to aliases for + sjis. + + - Fixed BUG#8064, which requires hex escaping of binary data when using + multibyte charsets with prepared statements. + + - Fixed BUG#8812, NON_UNIQUE column from DBMD.getIndexInfo() returned + inverted value. + + - Workaround for server BUG#9098 - default values of CURRENT_* for + DATE/TIME/TIMESTAMP/TIMESTAMP columns can't be distinguished from + 'string' values, so UpdatableResultSet.moveToInsertRow() generates + bad SQL for inserting default values. + + - Fixed BUG#8629 - 'EUCKR' charset is sent as 'SET NAMES euc_kr' which + MySQL-4.1 and newer doesn't understand. + + - DatabaseMetaData.supportsSelectForUpdate() returns correct value based + on server version. + + - Use hex escapes for PreparedStatement.setBytes() for double-byte charsets + including 'aliases' Windows-31J, CP934, MS932. + + - Added support for the "EUC_JP_Solaris" character encoding, which maps + to a MySQL encoding of "eucjpms" (backported from 3.1 branch). This only + works on servers that support eucjpms, namely 5.0.3 or later. + +11-15-04 - Version 3.0.16-ga + + - Re-issue character set configuration commands when re-using pooled + connections and/or Connection.changeUser() when connected to MySQL-4.1 + or newer. + + - Fixed ResultSetMetaData.isReadOnly() to detect non-writable columns + when connected to MySQL-4.1 or newer, based on existence of 'original' + table and column names. + + - Fixed BUG#5664, ResultSet.updateByte() when on insert row + throws ArrayOutOfBoundsException. + + - Fixed DatabaseMetaData.getTypes() returning incorrect (i.e. non-negative) + scale for the 'NUMERIC' type. + + - Fixed BUG#6198, off-by-one bug in Buffer.readString(string). + + - Made TINYINT(1) -> BIT/Boolean conversion configurable via 'tinyInt1isBit' + property (default 'true' to be JDBC compliant out of the box). + + - Only set 'character_set_results' during connection establishment if + server version >= 4.1.1. + + - Fixed regression where useUnbufferedInput was defaulting to 'false'. + + - Fixed BUG#6231, ResultSet.getTimestamp() on a column with TIME in it + fails. + +09-04-04 - Version 3.0.15-ga + + - Fixed BUG#4010 - StringUtils.escapeEasternUnicodeByteStream is still + broken for GBK + + - Fixed BUG#4334 - Failover for autoReconnect not using port #'s for any + hosts, and not retrying all hosts. (WARN: This required a change to + the SocketFactory connect() method signature, which is now + + public Socket connect(String host, int portNumber, Properties props), + + therefore any third-party socket factories will have to be changed + to support this signature. + + - Logical connections created by MysqlConnectionPoolDataSource will + now issue a rollback() when they are closed and sent back to the pool. + If your application server/connection pool already does this for you, you + can set the 'rollbackOnPooledClose' property to false to avoid the + overhead of an extra rollback(). + + - Removed redundant calls to checkRowPos() in ResultSet. + + - Fixed BUG#4742, 'DOUBLE' mapped twice in DBMD.getTypeInfo(). + + - Added FLOSS license exemption. + + - Fixed BUG#4808, calling .close() twice on a PooledConnection causes NPE. + + - Fixed BUG#4138 and BUG#4860, DBMD.getColumns() returns incorrect JDBC + type for unsigned columns. This affects type mappings for all numeric + types in the RSMD.getColumnType() and RSMD.getColumnTypeNames() methods + as well, to ensure that 'like' types from DBMD.getColumns() match up + with what RSMD.getColumnType() and getColumnTypeNames() return. + + - 'Production' - 'GA' in naming scheme of distributions. + + - Fix for BUG#4880, RSMD.getPrecision() returning 0 for non-numeric types + (should return max length in chars for non-binary types, max length + in bytes for binary types). This fix also fixes mapping of + RSMD.getColumnType() and RSMD.getColumnTypeName() for the BLOB types based + on the length sent from the server (the server doesn't distinguish between + TINYBLOB, BLOB, MEDIUMBLOB or LONGBLOB at the network protocol level). + + - Fixed BUG#5022 - ResultSet should release Field[] instance in .close(). + + - Fixed BUG#5069 -- ResultSet.getMetaData() should not return + incorrectly-initialized metadata if the result set has been closed, but + should instead throw a SQLException. Also fixed for getRow() and + getWarnings() and traversal methods by calling checkClosed() before + operating on instance-level fields that are nullified during .close(). + + - Parse new timezone variables from 4.1.x servers. + + - Use _binary introducer for PreparedStatement.setBytes() and + set*Stream() when connected to MySQL-4.1.x or newer to avoid + misinterpretation during character conversion. + +05-28-04 - Version 3.0.14-production + + - Fixed URL parsing error + +05-27-04 - Version 3.0.13-production + + - Fixed BUG#3848 - Using a MySQLDatasource without server name fails + + - Fixed BUG#3920 - "No Database Selected" when using + MysqlConnectionPoolDataSource. + + - Fixed BUG#3873 - PreparedStatement.getGeneratedKeys() method returns only + 1 result for batched insertions + +05-18-04 - Version 3.0.12-production + + - Add unsigned attribute to DatabaseMetaData.getColumns() output + in the TYPE_NAME column. + + - Added 'failOverReadOnly' property, to allow end-user to configure + state of connection (read-only/writable) when failed over. + + - Backported 'change user' and 'reset server state' functionality + from 3.1 branch, to allow clients of MysqlConnectionPoolDataSource + to reset server state on getConnection() on a pooled connection. + + - Don't escape SJIS/GBK/BIG5 when using MySQL-4.1 or newer. + + - Allow 'url' parameter for MysqlDataSource and MysqlConnectionPool + DataSource so that passing of other properties is possible from + inside appservers. + + - Map duplicate key and foreign key errors to SQLState of + '23000'. + + - Backport documentation tooling from 3.1 branch. + + - Return creating statement for ResultSets created by + getGeneratedKeys() (BUG#2957) + + - Allow java.util.Date to be sent in as parameter to + PreparedStatement.setObject(), converting it to a Timestamp + to maintain full precision (BUG#3103). + + - Don't truncate BLOBs/CLOBs when using setBytes() and/or + setBinary/CharacterStream() (BUG#2670). + + - Dynamically configure character set mappings for field-level + character sets on MySQL-4.1.0 and newer using 'SHOW COLLATION' + when connecting. + + - Map 'binary' character set to 'US-ASCII' to support DATETIME + charset recognition for servers >= 4.1.2 + + - Use 'SET character_set_results" during initialization to allow any + charset to be returned to the driver for result sets. + + - Use charsetnr returned during connect to encode queries before + issuing 'SET NAMES' on MySQL >= 4.1.0. + + - Add helper methods to ResultSetMetaData (getColumnCharacterEncoding() + and getColumnCharacterSet()) to allow end-users to see what charset + the driver thinks it should be using for the column. + + - Only set character_set_results for MySQL >= 4.1.0. + + - Fixed BUG#3511, StringUtils.escapeSJISByteStream() not covering all + eastern double-byte charsets correctly. + + - Renamed StringUtils.escapeSJISByteStream() to more appropriate + escapeEasternUnicodeByteStream(). + + - Fixed BUG#3554 - Not specifying database in URL caused MalformedURL + exception. + + - Auto-convert MySQL encoding names to Java encoding names if used + for characterEncoding property. + + - Added encoding names that are recognized on some JVMs to fix case + where they were reverse-mapped to MySQL encoding names incorrectly. + + - Use junit.textui.TestRunner for all unit tests (to allow them to be + run from the command line outside of Ant or Eclipse). + + - Fixed BUG#3557 - UpdatableResultSet not picking up default values + for moveToInsertRow(). + + - Fixed BUG#3570 - inconsistent reporting of column type. The server + still doesn't return all types for *BLOBs *TEXT correctly, so the + driver won't return those correctly. + + - Fixed BUG#3520 -- DBMD.getSQLStateType() returns incorrect value. + + - Fixed regression in PreparedStatement.setString() and eastern character + encodings. + + - Made StringRegressionTest 4.1-unicode aware. + +02-19-04 - Version 3.0.11-stable + + - Trigger a 'SET NAMES utf8' when encoding is forced to 'utf8' _or_ + 'utf-8' via the 'characterEncoding' property. Previously, only the + Java-style encoding name of 'utf-8' would trigger this. + + - AutoReconnect time was growing faster than exponentially (BUG#2447). + + - Fixed failover always going to last host in list (BUG#2578) + + - Added 'useUnbufferedInput' parameter, and now use it by default + (due to JVM issue + http://developer.java.sun.com/developer/bugParade/bugs/4401235.html) + + - Detect 'on/off' or '1','2','3' form of lower_case_table_names on + server. + + - Return 'java.lang.Integer' for TINYINT and SMALLINT types from + ResultSetMetaData.getColumnClassName() (fix for BUG#2852). + + - Return 'java.lang.Double' for FLOAT type from ResultSetMetaData. + getColumnClassName() (fix for BUG#2855). + + - Return '[B' instead of java.lang.Object for BINARY, VARBINARY and + LONGVARBINARY types from ResultSetMetaData.getColumnClassName() + (JDBC compliance). + +01-13-04 - Version 3.0.10-stable + + - Don't count quoted id's when inside a 'string' in PreparedStatement + parsing (fix for BUG#1511). + + - 'Friendlier' exception message for PacketTooLargeException + (BUG#1534). + + - Backported fix for aliased tables and UpdatableResultSets in + checkUpdatability() method from 3.1 branch. + + - Fix for ArrayIndexOutOfBounds exception when using Statement.setMaxRows() + (BUG#1695). + + - Fixed BUG#1576, dealing with large blobs and split packets not being + read correctly. + + - Fixed regression of Statement.getGeneratedKeys() and REPLACE statements. + + - Fixed BUG#1630, subsequent call to ResultSet.updateFoo() causes NPE if + result set is not updatable. + + - Fix for 4.1.1-style auth with no password. + + - Fix for BUG#1731, Foreign Keys column sequence is not consistent in + DatabaseMetaData.getImported/Exported/CrossReference(). + + - Fix for BUG#1775 - DatabaseMetaData.getSystemFunction() returning + bad function 'VResultsSion'. + + - Fix for BUG#1592 -- cross-database updatable result sets + are not checked for updatability correctly. + + - DatabaseMetaData.getColumns() should return Types.LONGVARCHAR for + MySQL LONGTEXT type. + + - ResultSet.getObject() on TINYINT and SMALLINT columns should return + Java type 'Integer' (BUG#1913) + + - Added 'alwaysClearStream' connection property, which causes the driver + to always empty any remaining data on the input stream before + each query. + + - Added more descriptive error message 'Server Configuration Denies + Access to DataSource', as well as retrieval of message from server. + + - Autoreconnect code didn't set catalog upon reconnect if it had been + changed. + + - Implement ResultSet.updateClob(). + + - ResultSetMetaData.isCaseSensitive() returned wrong value for CHAR/VARCHAR + columns. + + - Fix for BUG#1933 -- Connection property "maxRows" not honored. + + - Fix for BUG#1925 -- Statements being created too many times in + DBMD.extractForeignKeyFromCreateTable(). + + - Fix for BUG#1914 -- Support escape sequence {fn convert ... } + + - Fix for BUG#1958 -- ArrayIndexOutOfBounds when parameter number == + number of parameters + 1. + + - Fix for BUG#2006 -- ResultSet.findColumn() should use first matching + column name when there are duplicate column names in SELECT query + (JDBC-compliance). + + - Removed static synchronization bottleneck from + PreparedStatement.setTimestamp(). + + - Removed static synchronization bottleneck from instance factory + method of SingleByteCharsetConverter. + + - Enable caching of the parsing stage of prepared statements via + the 'cachePrepStmts', 'prepStmtCacheSize' and 'prepStmtCacheSqlLimit' + properties (disabled by default). + + - Speed up parsing of PreparedStatements, try to use one-pass whenever + possible. + + - Fixed security exception when used in Applets (applets can't + read the system property 'file.encoding' which is needed + for LOAD DATA LOCAL INFILE). + + - Use constants for SQLStates. + + - Map charset 'ko18_ru' to 'ko18r' when connected to MySQL-4.1.0 or + newer. + + - Ensure that Buffer.writeString() saves room for the \0. + + - Fixed exception 'Unknown character set 'danish' on connect w/ JDK-1.4.0 + + - Fixed mappings in SQLError to report deadlocks with SQLStates of '41000'. + + - 'maxRows' property would affect internal statements, so check it for all + statement creation internal to the driver, and set to 0 when it is not. + +10-07-03 - Version 3.0.9-stable + + - Faster date handling code in ResultSet and PreparedStatement (no longer + uses Date methods that synchronize on static calendars). + + - Fixed test for end of buffer in Buffer.readString(). + + - Fixed ResultSet.previous() behavior to move current + position to before result set when on first row + of result set (bugs.mysql.com BUG#496) + + - Fixed Statement and PreparedStatement issuing bogus queries + when setMaxRows() had been used and a LIMIT clause was present + in the query. + + - Fixed BUG#661 - refreshRow didn't work when primary key values + contained values that needed to be escaped (they ended up being + doubly-escaped). + + - Support InnoDB contraint names when extracting foreign key info + in DatabaseMetaData BUG#517 and BUG#664 + (impl. ideas from Parwinder Sekhon) + + - Backported 4.1 protocol changes from 3.1 branch (server-side SQL + states, new field info, larger client capability flags, + connect-with-database, etc). + + - Fix UpdatableResultSet to return values for getXXX() when on + insert row (BUG#675). + + - The insertRow in an UpdatableResultSet is now loaded with + the default column values when moveToInsertRow() is called + (BUG#688) + + - DatabaseMetaData.getColumns() wasn't returning NULL for + default values that are specified as NULL. + + - Change default statement type/concurrency to TYPE_FORWARD_ONLY + and CONCUR_READ_ONLY (spec compliance). + + - Don't try and reset isolation level on reconnect if MySQL doesn't + support them. + + - Don't wrap SQLExceptions in RowDataDynamic. + + - Don't change timestamp TZ twice if useTimezone==true (BUG#774) + + - Fixed regression in large split-packet handling (BUG#848). + + - Better diagnostic error messages in exceptions for 'streaming' + result sets. + + - Issue exception on ResultSet.getXXX() on empty result set (wasn't + caught in some cases). + + - Don't hide messages from exceptions thrown in I/O layers. + + - Don't fire connection closed events when closing pooled connections, or + on PooledConnection.getConnection() with already open connections (BUG#884). + + - Clip +/- INF (to smallest and largest representative values for the type in + MySQL) and NaN (to 0) for setDouble/setFloat(), and issue a warning on the + statement when the server does not support +/- INF or NaN. + + - Fix for BUG#879, double-escaping of '\' when charset is SJIS or GBK and '\' + appears in non-escaped input. + + - When emptying input stream of unused rows for 'streaming' result sets, + have the current thread yield() every 100 rows in order to not monopolize + CPU time. + + - Fixed BUG#1099, DatabaseMetaData.getColumns() getting confused about the + keyword 'set' in character columns. + + - Fixed deadlock issue with Statement.setMaxRows(). + + - Fixed CLOB.truncate(), BUG#1130 + + - Optimized CLOB.setChracterStream(), BUG#1131 + + - Made databaseName, portNumber and serverName optional parameters + for MysqlDataSourceFactory (BUG#1246) + + - Fix for BUG#1247 -- ResultSet.get/setString mashing char 127 + + - Backported auth. changes for 4.1.1 and newer from 3.1 branch. + + - Added com.mysql.jdbc.util.BaseBugReport to help creation of testcases + for bug reports. + + - Added property to 'clobber' streaming results, by setting the + 'clobberStreamingResults' property to 'true' (the default is 'false'). + This will cause a 'streaming' ResultSet to be automatically + closed, and any oustanding data still streaming from the server to + be discarded if another query is executed before all the data has been + read from the server. + +05-23-03 - Version 3.0.8-stable + + - Allow bogus URLs in Driver.getPropertyInfo(). + + - Return list of generated keys when using multi-value INSERTS + with Statement.getGeneratedKeys(). + + - Use JVM charset with filenames and 'LOAD DATA [LOCAL] INFILE' + + - Fix infinite loop with Connection.cleanup(). + + - Changed Ant target 'compile-core' to 'compile-driver', and + made testsuite compilation a separate target. + + - Fixed result set not getting set for Statement.executeUpdate(), + which affected getGeneratedKeys() and getUpdateCount() in + some cases. + + - Unicode character 0xFFFF in a string would cause the driver to + throw an ArrayOutOfBoundsException (Bug #378) + + - Return correct amount of generated keys when using 'REPLACE' + statements. + + - Fix problem detecting server character set in some cases. + + - Fix row data decoding error when using _very_ large packets. + + - Optimized row data decoding. + + - Issue exception when operating on an already-closed + prepared statement. + + - Fixed SJIS encoding bug, thanks to Naoto Sato. + + - Optimized usage of EscapeProcessor. + + - Allow multiple calls to Statement.close() + +04-08-03 - Version 3.0.7-stable + + - Fixed MysqlPooledConnection.close() calling wrong event type. + + - Fixed StringIndexOutOfBoundsException in PreparedStatement. + setClob(). + + - 4.1 Column Metadata fixes + + - Remove synchronization from Driver.connect() and + Driver.acceptsUrl(). + + - IOExceptions during a transaction now cause the Connection to + be closed. + + - Fixed missing conversion for 'YEAR' type in ResultSetMetaData. + getColumnTypeName(). + + - Don't pick up indexes that start with 'pri' as primary keys + for DBMD.getPrimaryKeys(). + + - Throw SQLExceptions when trying to do operations on a forcefully + closed Connection (i.e. when a communication link failure occurs). + + - You can now toggle profiling on/off using + Connection.setProfileSql(boolean). + + - Fixed charset issues with database metadata (charset was not + getting set correctly). + + - Updatable ResultSets can now be created for aliased tables/columns + when connected to MySQL-4.1 or newer. + + - Fixed 'LOAD DATA LOCAL INFILE' bug when file > max_allowed_packet. + + - Fixed escaping of 0x5c ('\') character for GBK and Big5 charsets. + + - Fixed ResultSet.getTimestamp() when underlying field is of type DATE. + + - Ensure that packet size from alignPacketSize() does not + exceed MAX_ALLOWED_PACKET (JVM bug) + + - Don't reset Connection.isReadOnly() when autoReconnecting. + +02-18-03 - Version 3.0.6-stable + + - Fixed ResultSetMetaData to return "" when catalog not known. + Fixes NullPointerExceptions with Sun's CachedRowSet. + + - Fixed DBMD.getTypeInfo() and DBMD.getColumns() returning + different value for precision in TEXT/BLOB types. + + - Allow ignoring of warning for 'non transactional tables' during + rollback (compliance/usability) by setting 'ignoreNonTxTables' + property to 'true'. + + - Fixed SQLExceptions getting swallowed on initial connect. + + - Fixed Statement.setMaxRows() to stop sending 'LIMIT' type queries + when not needed (performance) + + - Clean up Statement query/method mismatch tests (i.e. INSERT not + allowed with .executeQuery()). + + - More checks added in ResultSet traversal method to catch + when in closed state. + + - Fixed ResultSetMetaData.isWritable() to return correct value. + + - Add 'window' of different NULL sorting behavior to + DBMD.nullsAreSortedAtStart (4.0.2 to 4.0.10, true, otherwise, + no). + + - Implemented Blob.setBytes(). You still need to pass the + resultant Blob back into an updatable ResultSet or + PreparedStatement to persist the changes, as MySQL does + not support 'locators'. + + - Backported 4.1 charset field info changes from Connector/J 3.1 + +01-22-03 - Version 3.0.5-gamma + + - Fixed Buffer.fastSkipLenString() causing ArrayIndexOutOfBounds + exceptions with some queries when unpacking fields. + + - Implemented an empty TypeMap for Connection.getTypeMap() so that + some third-party apps work with MySQL (IBM WebSphere 5.0 Connection + pool). + + - Added missing LONGTEXT type to DBMD.getColumns(). + + - Retrieve TX_ISOLATION from database for + Connection.getTransactionIsolation() when the MySQL version + supports it, instead of an instance variable. + + - Quote table names in DatabaseMetaData.getColumns(), + getPrimaryKeys(), getIndexInfo(), getBestRowIdentifier() + + - Greatly reduce memory required for setBinaryStream() in + PreparedStatements. + + - Fixed ResultSet.isBeforeFirst() for empty result sets. + + - Added update options for foreign key metadata. + + +01-06-03 - Version 3.0.4-gamma + + - Added quoted identifiers to database names for + Connection.setCatalog. + + - Added support for quoted identifiers in PreparedStatement + parser. + + - Streamlined character conversion and byte[] handling in + PreparedStatements for setByte(). + + - Reduce memory footprint of PreparedStatements by sharing + outbound packet with MysqlIO. + + - Added 'strictUpdates' property to allow control of amount + of checking for 'correctness' of updatable result sets. Set this + to 'false' if you want faster updatable result sets and you know + that you create them from SELECTs on tables with primary keys and + that you have selected all primary keys in your query. + + - Added support for 4.0.8-style large packets. + + - Fixed PreparedStatement.executeBatch() parameter overwriting. + +12-17-02 - Version 3.0.3-dev + + - Changed charsToByte in SingleByteCharConverter to be non-static + + - Changed SingleByteCharConverter to use lazy initialization of each + converter. + + - Fixed charset handling in Fields.java + + - Implemented Connection.nativeSQL() + + - More robust escape tokenizer -- recognize '--' comments, and allow + nested escape sequences (see testsuite.EscapeProcessingTest) + + - DBMD.getImported/ExportedKeys() now handles multiple foreign keys + per table. + + - Fixed ResultSetMetaData.getPrecision() returning incorrect values + for some floating point types. + + - Fixed ResultSetMetaData.getColumnTypeName() returning BLOB for + TEXT and TEXT for BLOB types. + + - Fixed Buffer.isLastDataPacket() for 4.1 and newer servers. + + - Added CLIENT_LONG_FLAG to be able to get more column flags + (isAutoIncrement() being the most important) + + - Because of above, implemented ResultSetMetaData.isAutoIncrement() + to use Field.isAutoIncrement(). + + - Honor 'lower_case_table_names' when enabled in the server when + doing table name comparisons in DatabaseMetaData methods. + + - Some MySQL-4.1 protocol support (extended field info from selects) + + - Use non-aliased table/column names and database names to fullly + qualify tables and columns in UpdatableResultSet (requires + MySQL-4.1 or newer) + + - Allow user to alter behavior of Statement/ + PreparedStatement.executeBatch() via 'continueBatchOnError' property + (defaults to 'true'). + + - Check for connection closed in more Connection methods + (createStatement, prepareStatement, setTransactionIsolation, + setAutoCommit). + + - More robust implementation of updatable result sets. Checks that + _all_ primary keys of the table have been selected. + + - 'LOAD DATA LOCAL INFILE ...' now works, if your server is configured + to allow it. Can be turned off with the 'allowLoadLocalInfile' + property (see the README). + + - Substitute '?' for unknown character conversions in single-byte + character sets instead of '\0'. + + - NamedPipeSocketFactory now works (only intended for Windows), see + README for instructions. + +11-08-02 - Version 3.0.2-dev + + - Fixed issue with updatable result sets and PreparedStatements not + working + + - Fixed ResultSet.setFetchDirection(FETCH_UNKNOWN) + + - Fixed issue when calling Statement.setFetchSize() when using + arbitrary values + + - Fixed incorrect conversion in ResultSet.getLong() + + - Implemented ResultSet.updateBlob(). + + - Removed duplicate code from UpdatableResultSet (it can be inherited + from ResultSet, the extra code for each method to handle updatability + I thought might someday be necessary has not been needed). + + - Fixed "UnsupportedEncodingException" thrown when "forcing" a + character encoding via properties. + + - Fixed various non-ASCII character encoding issues. + + - Added driver property 'useHostsInPrivileges'. Defaults to true. + Affects whether or not '@hostname' will be used in + DBMD.getColumn/TablePrivileges. + + - All DBMD result set columns describing schemas now return NULL + to be more compliant with the behavior of other JDBC drivers + for other databases (MySQL does not support schemas). + + - Added SSL support. See README for information on how to use it. + + - Properly restore connection properties when autoReconnecting + or failing-over, including autoCommit state, and isolation level. + + - Use 'SHOW CREATE TABLE' when possible for determining foreign key + information for DatabaseMetaData...also allows cascade options for + DELETE information to be returned + + - Escape 0x5c character in strings for the SJIS charset. + + - Fixed start position off-by-1 error in Clob.getSubString() + + - Implemented Clob.truncate() + + - Implemented Clob.setString() + + - Implemented Clob.setAsciiStream() + + - Implemented Clob.setCharacterStream() + + - Added com.mysql.jdbc.MiniAdmin class, which allows you to send + 'shutdown' command to MySQL server...Intended to be used when 'embedding' + Java and MySQL server together in an end-user application. + + - Added 'connectTimeout' parameter that allows users of JDK-1.4 and newer + to specify a maxium time to wait to establish a connection. + + - Failover and autoReconnect only work when the connection is in a + autoCommit(false) state, in order to stay transaction safe + + - Added 'queriesBeforeRetryMaster' property that specifies how many + queries to issue when failed over before attempting to reconnect + to the master (defaults to 50) + + - Fixed DBMD.supportsResultSetConcurrency() so that it returns true + for ResultSet.TYPE_SCROLL_INSENSITIVE and ResultSet.CONCUR_READ_ONLY or + ResultSet.CONCUR_UPDATABLE + + - Fixed ResultSet.isLast() for empty result sets (should return false). + + - PreparedStatement now honors stream lengths in setBinary/Ascii/Character + Stream() unless you set the connection property + 'useStreamLengthsInPrepStmts' to 'false'. + + - Removed some not-needed temporary object creation by using Strings + smarter in EscapeProcessor, Connection and DatabaseMetaData classes. + +09-21-02 - Version 3.0.1-dev + + - Fixed ResultSet.getRow() off-by-one bug. + + - Fixed RowDataStatic.getAt() off-by-one bug. + + - Added limited Clob functionality (ResultSet.getClob(), + PreparedStatemtent.setClob(), + PreparedStatement.setObject(Clob). + + - Added socketTimeout parameter to URL. + + - Connection.isClosed() no longer "pings" the server. + + - Connection.close() issues rollback() when getAutoCommit() == false + + - Added "paranoid" parameter...sanitizes error messages removing + "sensitive" information from them (i.e. hostnames, ports, + usernames, etc.), as well as clearing "sensitive" data structures + when possible. + + - Fixed ResultSetMetaData.isSigned() for TINYINT and BIGINT. + + - Charsets now automatically detected. Optimized code for single-byte + character set conversion. + + - Implemented ResultSet.getCharacterStream() + + - Added "LOCAL TEMPORARY" to table types in DatabaseMetaData.getTableTypes() + + - Massive code clean-up to follow Java coding conventions (the time had come) + + +07-31-02 - Version 3.0.0-dev + + - !!! LICENSE CHANGE !!! The driver is now GPL. If you need + non-GPL licenses, please contact me + + - JDBC-3.0 functionality including + Statement/PreparedStatement.getGeneratedKeys() and + ResultSet.getURL() + + - Performance enchancements - driver is now 50-100% faster + in most situations, and creates fewer temporary objects + + - Repackaging...new driver name is "com.mysql.jdbc.Driver", + old name still works, though (the driver is now provided + by MySQL-AB) + + - Better checking for closed connections in Statement + and PreparedStatement. + + - Support for streaming (row-by-row) result sets (see README) + Thanks to Doron. + + - Support for large packets (new addition to MySQL-4.0 protocol), + see README for more information. + + - JDBC Compliance -- Passes all tests besides stored procedure tests + + + - Fix and sort primary key names in DBMetaData (SF bugs 582086 and 582086) + + - Float types now reported as java.sql.Types.FLOAT (SF bug 579573) + + - ResultSet.getTimestamp() now works for DATE types (SF bug 559134) + + - ResultSet.getDate/Time/Timestamp now recognizes all forms of invalid + values that have been set to all zeroes by MySQL (SF bug 586058) + + - Testsuite now uses Junit (which you can get from www.junit.org) + + - The driver now only works with JDK-1.2 or newer. + + - Added multi-host failover support (see README) + + - General source-code cleanup. + + - Overall speed improvements via controlling transient object + creation in MysqlIO class when reading packets + + - Performance improvements in string handling and field + metadata creation (lazily instantiated) contributed by + Alex Twisleton-Wykeham-Fiennes + + +05-16-02 - Version 2.0.14 + + - More code cleanup + + - PreparedStatement now releases resources on .close() (SF bug 553268) + + - Quoted identifiers not used if server version does not support them. Also, + if server started with --ansi or --sql-mode=ANSI_QUOTES then '"' will be + used as an identifier quote, otherwise '`' will be used. + + - ResultSet.getDouble() now uses code built into JDK to be more precise (but slower) + + - LogicalHandle.isClosed() calls through to physical connection + + - Added SQL profiling (to STDERR). Set "profileSql=true" in your JDBC url. + See README for more information. + + - Fixed typo for relaxAutoCommit parameter. + +04-24-02 - Version 2.0.13 + + - More code cleanup. + + - Fixed unicode chars being read incorrectly (SF bug 541088) + + - Faster blob escaping for PrepStmt + + - Added set/getPortNumber() to DataSource(s) (SF bug 548167) + + - Added setURL() to MySQLXADataSource (SF bug 546019) + + - PreparedStatement.toString() fixed (SF bug 534026) + + - ResultSetMetaData.getColumnClassName() now implemented + + - Rudimentary version of Statement.getGeneratedKeys() from JDBC-3.0 + now implemented (you need to be using JDK-1.4 for this to work, I + believe) + + - DBMetaData.getIndexInfo() - bad PAGES fixed (SF BUG 542201) + +04-07-02 - Version 2.0.12 + + - General code cleanup. + + - Added getIdleFor() method to Connection and MysqlLogicalHandle. + + - Relaxed synchronization in all classes, should fix 520615 and 520393. + + - Added getTable/ColumnPrivileges() to DBMD (fixes 484502). + + - Added new types to getTypeInfo(), fixed existing types thanks to + Al Davis and Kid Kalanon. + + - Added support for BIT types (51870) to PreparedStatement. + + - Fixed getRow() bug (527165) in ResultSet + + - Fixes for ResultSet updatability in PreparedStatement. + - Fixed timezone off by 1-hour bug in PreparedStatement (538286, 528785). + + - ResultSet: Fixed updatability (values being set to null + if not updated). + + - DataSources - fixed setUrl bug (511614, 525565), + wrong datasource class name (532816, 528767) + + - Added identifier quoting to all DatabaseMetaData methods + that need them (should fix 518108) + + - Added support for YEAR type (533556) + + - ResultSet.insertRow() should now detect auto_increment fields + in most cases and use that value in the new row. This detection + will not work in multi-valued keys, however, due to the fact that + the MySQL protocol does not return this information. + + - ResultSet.refreshRow() implemented. + + - Fixed testsuite.Traversal afterLast() bug, thanks to Igor Lastric. + +01-27-02 - Version 2.0.11 + + - Fixed missing DELETE_RULE value in + DBMD.getImported/ExportedKeys() and getCrossReference(). + + - Full synchronization of Statement.java. + + - More changes to fix "Unexpected end of input stream" + errors when reading BLOBs. This should be the last fix. + +01-24-02 - Version 2.0.10 + + - Fixed spurious "Unexpected end of input stream" errors in + MysqlIO (bug 507456). + + - Fixed null-pointer-exceptions when using + MysqlConnectionPoolDataSource with Websphere 4 (bug 505839). + +01-13-02 - Version 2.0.9 + + - Ant build was corrupting included jar files, fixed + (bug 487669). + + - Fixed extra memory allocation in MysqlIO.readPacket() + (bug 488663). + + - Implementation of DatabaseMetaData.getExported/ImportedKeys() and + getCrossReference(). + + - Full synchronization on methods modifying instance and class-shared + references, driver should be entirely thread-safe now (please + let me know if you have problems) + + - DataSource implementations moved to org.gjt.mm.mysql.jdbc2.optional + package, and (initial) implementations of PooledConnectionDataSource + and XADataSource are in place (thanks to Todd Wolff for the + implementation and testing of PooledConnectionDataSource with + IBM WebSphere 4). + + - Added detection of network connection being closed when reading packets + (thanks to Todd Lizambri). + + - Fixed quoting error with escape processor (bug 486265). + + - Report batch update support through DatabaseMetaData (bug 495101). + + - Fixed off-by-one-hour error in PreparedStatement.setTimestamp() + (bug 491577). + + - Removed concatenation support from driver (the '||' operator), + as older versions of VisualAge seem to be the only thing that + use it, and it conflicts with the logical '||' operator. You will + need to start mysqld with the "--ansi" flag to use the '||' + operator as concatenation (bug 491680) + + - Fixed casting bug in PreparedStatement (bug 488663). + +11-25-01 - Version 2.0.8 + + - Batch updates now supported (thanks to some inspiration + from Daniel Rall). + + - XADataSource/ConnectionPoolDataSource code (experimental) + + - PreparedStatement.setAnyNumericType() now handles positive + exponents correctly (adds "+" so MySQL can understand it). + + - DatabaseMetaData.getPrimaryKeys() and getBestRowIdentifier() + are now more robust in identifying primary keys (matches + regardless of case or abbreviation/full spelling of Primary Key + in Key_type column). + +10-24-01 - Version 2.0.7 + + - PreparedStatement.setCharacterStream() now implemented + + - Fixed dangling socket problem when in high availability + (autoReconnect=true) mode, and finalizer for Connection will + close any dangling sockets on GC. + + - Fixed ResultSetMetaData.getPrecision() returning one + less than actual on newer versions of MySQL. + + - ResultSet.getBlob() now returns null if column value + was null. + + - Character sets read from database if useUnicode=true + and characterEncoding is not set. (thanks to + Dmitry Vereshchagin) + + - Initial transaction isolation level read from + database (if avaialable) (thanks to Dmitry Vereshchagin) + + - Fixed DatabaseMetaData.supportsTransactions(), and + supportsTransactionIsolationLevel() and getTypeInfo() + SQL_DATETIME_SUB and SQL_DATA_TYPE fields not being + readable. + + - Fixed PreparedStatement generating SQL that would end + up with syntax errors for some queries. + + - Fixed ResultSet.isAfterLast() always returning false. + + - Fixed timezone issue in PreparedStatement.setTimestamp() + (thanks to Erik Olofsson) + + - Captialize type names when "captializeTypeNames=true" + is passed in URL or properties (for WebObjects, thanks + to Anjo Krank) + + - Updatable result sets now correctly handle NULL + values in fields. + + - PreparedStatement.setDouble() now uses full-precision + doubles (reverting a fix made earlier to truncate them). + + - PreparedStatement.setBoolean() will use 1/0 for values + if your MySQL Version >= 3.21.23. + +06-16-01 - Version 2.0.6 + +Fixed PreparedStatement parameter checking + + - Fixed case-sensitive column names in ResultSet.java + +06-13-01 - Version 2.0.5 + + - Fixed ResultSet.getBlob() ArrayIndex out-of-bounds + + - Fixed ResultSetMetaData.getColumnTypeName for TEXT/BLOB + + - Fixed ArrayIndexOutOfBounds when sending large BLOB queries + (Max size packet was not being set) + + - Added ISOLATION level support to Connection.setIsolationLevel() + + - Fixed NPE on PreparedStatement.executeUpdate() when all columns + have not been set. + + - Fixed data parsing of TIMESTAMPs with 2-digit years + + - Added Byte to PreparedStatement.setObject() + + - ResultSet.getBoolean() now recognizes '-1' as 'true' + + - ResultSet has +/-Inf/inf support + + - ResultSet.insertRow() works now, even if not all columns are + set (they will be set to "NULL") + + - DataBaseMetaData.getCrossReference() no longer ArrayIndexOOB + + - getObject() on ResultSet correctly does TINYINT->Byte and + SMALLINT->Short + +12-03-00 - Version 2.0.3 + + - Implemented getBigDecimal() without scale component + for JDBC2. + + - Fixed composite key problem with updateable result sets. + + - Added detection of -/+INF for doubles. + + - Faster ASCII string operations. + + - Fixed incorrect detection of MAX_ALLOWED_PACKET, so sending + large blobs should work now. + + - Fixed off-by-one error in java.sql.Blob implementation code. + + - Added "ultraDevHack" URL parameter, set to "true" to allow + (broken) Macromedia UltraDev to use the driver. + +04-06-00 - Version 2.0.1 + + - Fixed RSMD.isWritable() returning wrong value. + Thanks to Moritz Maass. + + - Cleaned up exception handling when driver connects + + - Columns that are of type TEXT now return as Strings + when you use getObject() + + - DatabaseMetaData.getPrimaryKeys() now works correctly wrt + to key_seq. Thanks to Brian Slesinsky. + + - No escape processing is done on PreparedStatements anymore + per JDBC spec. + + - Fixed many JDBC-2.0 traversal, positioning bugs, especially + wrt to empty result sets. Thanks to Ron Smits, Nick Brook, + Cessar Garcia and Carlos Martinez. + + - Fixed some issues with updatability support in ResultSet when + using multiple primary keys. + +02-21-00 - Version 2.0pre5 + + - Fixed Bad Handshake problem. + +01-10-00 - Version 2.0pre4 + + - Fixes to ResultSet for insertRow() - Thanks to + Cesar Garcia + + - Fix to Driver to recognize JDBC-2.0 by loading a JDBC-2.0 + class, instead of relying on JDK version numbers. Thanks + to John Baker. + + - Fixed ResultSet to return correct row numbers + + - Statement.getUpdateCount() now returns rows matched, + instead of rows actually updated, which is more SQL-92 + like. + +10-29-99 + + - Statement/PreparedStatement.getMoreResults() bug fixed. + Thanks to Noel J. Bergman. + + - Added Short as a type to PreparedStatement.setObject(). + Thanks to Jeff Crowder + + - Driver now automagically configures maximum/preferred packet + sizes by querying server. + + - Autoreconnect code uses fast ping command if server supports + it. + + - Fixed various bugs wrt. to packet sizing when reading from + the server and when alloc'ing to write to the server. + +08-17-99 - Version 2.0pre + + - Now compiles under JDK-1.2. The driver supports both JDK-1.1 + and JDK-1.2 at the same time through a core set of classes. + The driver will load the appropriate interface classes at + runtime by figuring out which JVM version you are using. + + - Fixes for result sets with all nulls in the first row. + (Pointed out by Tim Endres) + + - Fixes to column numbers in SQLExceptions in ResultSet + (Thanks to Blas Rodriguez Somoza) + + - The database no longer needs to specified to connect. + (Thanks to Christian Motschke) + +07-04-99 - Version 1.2b + + - Better Documentation (in progress), in doc/mm.doc/book1.html + + - DBMD now allows null for a column name pattern (not in + spec), which it changes to '%'. + + - DBMD now has correct types/lengths for getXXX(). + + - ResultSet.getDate(), getTime(), and getTimestamp() fixes. + (contributed by Alan Wilken) + + - EscapeProcessor now handles \{ \} and { or } inside quotes + correctly. (thanks to Alik for some ideas on how to fix it) + + - Fixes to properties handling in Connection. + (contributed by Juho Tikkala) + + - ResultSet.getObject() now returns null for NULL columns + in the table, rather than bombing out. + (thanks to Ben Grosman) + + - ResultSet.getObject() now returns Strings for types + from MySQL that it doesn't know about. (Suggested by + Chris Perdue) + + - Removed DataInput/Output streams, not needed, 1/2 number + of method calls per IO operation. + + - Use default character encoding if one is not specified. This + is a work-around for broken JVMs, because according to spec, + EVERY JVM must support "ISO8859_1", but they don't. + + - Fixed Connection to use the platform character encoding + instead of "ISO8859_1" if one isn't explicitly set. This + fixes problems people were having loading the character- + converter classes that didn't always exist (JVM bug). + (thanks to Fritz Elfert for pointing out this problem) + + - Changed MysqlIO to re-use packets where possible to reduce + memory usage. + + - Fixed escape-processor bugs pertaining to {} inside + quotes. + +04-14-99 - Version 1.2a + + - Fixed character-set support for non-Javasoft JVMs + (thanks to many people for pointing it out) + + - Fixed ResultSet.getBoolean() to recognize 'y' & 'n' + as well as '1' & '0' as boolean flags. + (thanks to Tim Pizey) + + - Fixed ResultSet.getTimestamp() to give better performance. + (thanks to Richard Swift) + + - Fixed getByte() for numeric types. + (thanks to Ray Bellis) + + - Fixed DatabaseMetaData.getTypeInfo() for DATE type. + (thanks to Paul Johnston) + + - Fixed EscapeProcessor for "fn" calls. + (thanks to Piyush Shah at locomotive.org) + + - Fixed EscapeProcessor to not do extraneous work if there + are no escape codes. + (thanks to Ryan Gustafson) + + - Fixed Driver to parse URLs of the form "jdbc:mysql://host:port" + (thanks to Richard Lobb) + +03-24-99 - Version 1.1i + + - Fixed Timestamps for PreparedStatements + + - Fixed null pointer exceptions in RSMD and RS + + - Re-compiled with jikes for valid class files (thanks ms!) + +03-08-99 - Version 1.1h + + - Fixed escape processor to deal with un-matched { and } + (thanks to Craig Coles) + + - Fixed escape processor to create more portable (between + DATETIME and TIMESTAMP types) representations so that + it will work with BETWEEN clauses. + (thanks to Craig Longman) + + - MysqlIO.quit() now closes the socket connection. Before, + after many failed connections some OS's would run out + of file descriptors. (thanks to Michael Brinkman) + + - Fixed NullPointerException in Driver.getPropertyInfo. + (thanks to Dave Potts) + + - Fixes to MysqlDefs to allow all *text fields to be + retrieved as Strings. + (thanks to Chris at Leverage) + + - Fixed setDouble in PreparedStatement for large numbers + to avoid sending scientific notation to the database. + (thanks to J.S. Ferguson) + + - Fixed getScale() and getPrecision() in RSMD. + (contrib'd by James Klicman) + + - Fixed getObject() when field was DECIMAL or NUMERIC + (thanks to Bert Hobbs) + + - DBMD.getTables() bombed when passed a null table-name + pattern. Fixed. (thanks to Richard Lobb) + + - Added check for "client not authorized" errors during + connect. (thanks to Hannes Wallnoefer) + +02-19-99 - Version 1.1g + + - Result set rows are now byte arrays. Blobs and Unicode + work bidriectonally now. The useUnicode and encoding + options are implemented now. + + - Fixes to PreparedStatement to send binary set by + setXXXStream to be sent un-touched to the MySQL server. + + - Fixes to getDriverPropertyInfo(). + +12-31-98 - Version 1.1f + + - Changed all ResultSet fields to Strings, this should allow + Unicode to work, but your JVM must be able to convert + between the character sets. This should also make reading + data from the server be a bit quicker, because there is now + no conversion from StringBuffer to String. + + - Changed PreparedStatement.streamToString() to be more + efficient (code from Uwe Schaefer). + + - URL parsing is more robust (throws SQL exceptions on errors + rather than NullPointerExceptions) + + - PreparedStatement now can convert Strings to Time/Date values + via setObject() (code from Robert Currey). + + - IO no longer hangs in Buffer.readInt(), that bug was + introduced in 1.1d when changing to all byte-arrays for + result sets. (Pointed out by Samo Login) + +11-03-98 - Version 1.1b + + - Fixes to DatabaseMetaData to allow both IBM VA and J-Builder + to work. Let me know how it goes. (thanks to Jac Kersing) + + - Fix to ResultSet.getBoolean() for NULL strings + (thanks to Barry Lagerweij) + + - Beginning of code cleanup, and formatting. Getting ready + to branch this off to a parallel JDBC-2.0 source tree. + + - Added "final" modifier to critical sections in MysqlIO and + Buffer to allow compiler to inline methods for speed. + +9-29-98 + + - If object references passed to setXXX() in PreparedStatement are + null, setNull() is automatically called for you. (Thanks for the + suggestion goes to Erik Ostrom) + + - setObject() in PreparedStatement will now attempt to write a + serialized representation of the object to the database for + objects of Types.OTHER and objects of unknown type. + + - Util now has a static method readObject() which given a ResultSet + and a column index will re-instantiate an object serialized in + the above manner. + +9-02-98 - Vesion 1.1 + + - Got rid of "ugly hack" in MysqlIO.nextRow(). Rather than + catch an exception, Buffer.isLastDataPacket() was fixed. + + - Connection.getCatalog() and Connection.setCatalog() + should work now. + + - Statement.setMaxRows() works, as well as setting + by property maxRows. Statement.setMaxRows() overrides + maxRows set via properties or url parameters. + + - Automatic re-connection is available. Because it has + to "ping" the database before each query, it is + turned off by default. To use it, pass in "autoReconnect=true" + in the connection URL. You may also change the number of + reconnect tries, and the initial timeout value via + "maxReconnects=n" (default 3) and "initialTimeout=n" + (seconds, default 2) parameters. The timeout is an + exponential backoff type of timeout, e.g. if you have initial + timeout of 2 seconds, and maxReconnects of 3, then the driver + will timeout 2 seconds, 4 seconds, then 16 seconds between each + re-connection attempt. + +8-24-98 - Version 1.0 + + - Fixed handling of blob data in Buffer.java + + - Fixed bug with authentication packet being + sized too small. + + - The JDBC Driver is now under the LPGL + +8-14-98 - + + - Fixed Buffer.readLenString() to correctly + read data for BLOBS. + + - Fixed PreparedStatement.stringToStream to + correctly read data for BLOBS. + + - Fixed PreparedStatement.setDate() to not + add a day. + (above fixes thanks to Vincent Partington) + + - Added URL parameter parsing (?user=... etc). + + +8-04-98 - Version 0.9d + + - Big news! New package name. Tim Endres from ICE + Engineering is starting a new source tree for + GNU GPL'd Java software. He's graciously given + me the org.gjt.mm package directory to use, so now + the driver is in the org.gjt.mm.mysql package scheme. + I'm "legal" now. Look for more information on Tim's + project soon. + + - Now using dynamically sized packets to reduce + memory usage when sending commands to the DB. + + - Small fixes to getTypeInfo() for parameters, etc. + + - DatabaseMetaData is now fully implemented. Let me + know if these drivers work with the various IDEs + out there. I've heard that they're working with + JBuilder right now. + + - Added JavaDoc documentation to the package. + + - Package now available in .zip or .tar.gz. + +7-28-98 - Version 0.9 + + - Implemented getTypeInfo(). + Connection.rollback() now throws an SQLException + per the JDBC spec. + + - Added PreparedStatement that supports all JDBC API + methods for PreparedStatement including InputStreams. + Please check this out and let me know if anything is + broken. + + - Fixed a bug in ResultSet that would break some + queries that only returned 1 row. + + - Fixed bugs in DatabaseMetaData.getTables(), + DatabaseMetaData.getColumns() and + DatabaseMetaData.getCatalogs(). + + - Added functionality to Statement that allows + executeUpdate() to store values for IDs that are + automatically generated for AUTO_INCREMENT fields. + Basically, after an executeUpdate(), look at the + SQLWarnings for warnings like "LAST_INSERTED_ID = + 'some number', COMMAND = 'your SQL query'". + + If you are using AUTO_INCREMENT fields in your + tables and are executing a lot of executeUpdate()s + on one Statement, be sure to clearWarnings() every + so often to save memory. + +7-06-98 - Version 0.8 + + - Split MysqlIO and Buffer to separate classes. Some + ClassLoaders gave an IllegalAccess error for some + fields in those two classes. Now mm.mysql works in + applets and all classloaders. + + Thanks to Joe Ennis for pointing + out the problem and working on a fix with me. + +7-01-98 - Version 0.7 + + - Fixed DatabaseMetadata problems in getColumns() and + bug in switch statement in the Field constructor. + + Thanks to Costin Manolache for + pointing these out. + +5-21-98 - Version 0.6 + + - Incorporated efficiency changes from + Richard Swift in + MysqlIO.java and ResultSet.java + + - We're now 15% faster than gwe's driver. + + - Started working on DatabaseMetaData. + + The following methods are implemented: + * getTables() + * getTableTypes() + * getColumns + * getCatalogs() diff --git a/mysql-connector-java-5.1.40/COPYING b/mysql-connector-java-5.1.40/COPYING new file mode 100644 index 0000000..ffa372e --- /dev/null +++ b/mysql-connector-java-5.1.40/COPYING @@ -0,0 +1,307 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software + interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as +a special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type 'show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type 'show c' + for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than 'show w' and +'show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program 'Gnomovision' (which makes passes at compilers) written + by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use +the GNU Lesser General Public License instead of this License. diff --git a/mysql-connector-java-5.1.40/README b/mysql-connector-java-5.1.40/README new file mode 100644 index 0000000..bacea06 --- /dev/null +++ b/mysql-connector-java-5.1.40/README @@ -0,0 +1,2251 @@ +MySQL Connector/J 5.1.40 + +This is a release of MySQL Connector/J, Oracle's dual- +license JDBC Driver for MySQL. For the avoidance of +doubt, this particular copy of the software is released +under the version 2 of the GNU General Public License. +MySQL Connector/J is brought to you by Oracle. + +Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + +License information can be found in the COPYING file. + +MySQL FOSS License Exception +We want free and open source software applications under +certain licenses to be able to use the GPL-licensed MySQL +Connector/J (specified GPL-licensed MySQL client libraries) +despite the fact that not all such FOSS licenses are +compatible with version 2 of the GNU General Public License. +Therefore there are special exceptions to the terms and +conditions of the GPLv2 as applied to these client libraries, +which are identified and described in more detail in the +FOSS License Exception at + + +This software is OSI Certified Open Source Software. +OSI Certified is a certification mark of the Open Source Initiative. + +This distribution may include materials developed by third +parties. For license and attribution notices for these +materials, please refer to the documentation that accompanies +this distribution (see the "Licenses for Third-Party Components" +appendix) or view the online documentation at + +A copy of the license/notices is also reproduced below. + +GPLv2 Disclaimer +For the avoidance of doubt, except that if any license choice +other than GPL or LGPL is available it will apply instead, +Oracle elects to use only the General Public License version 2 +(GPLv2) at this time for any software where a choice of GPL +license versions is made available with the language indicating +that GPLv2 or any later version may be used, or where a choice +of which version of the GPL is applied is otherwise unspecified. + +CONTENTS + +* Documentation Location +* Third-Party Component Notices + +DOCUMENTATION LOCATION + +The documentation formerly contained in this file has moved +into the 'doc' directory, where it is available in HTML, PDF +and plaintext forms. + +You may also find the latest copy of the documentation on +the MySQL website at +http://dev.mysql.com/doc/connector-j/en + +*************************************************************** + +Third-Party Component Notices + +**************************************************************** + +%%The following software may be included in this product: +c3p0:JDBC DataSources/Resource Pools + +Use of any of this software is governed by the terms of the license below: + +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +******************************************************************* + +%%The following software may be included in this product: +jboss-common-jdbc-wrapper.jar + +Use of any of this software is governed by the terms of the license below: + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + + Preamble + +14 + +15 + + The licenses for most software are designed to take away your + +16 + +freedom to share and change it. By contrast, the GNU General Public + +17 + +Licenses are intended to guarantee your freedom to share and change + +18 + +free software--to make sure the software is free for all its users. + +19 + +20 + + This license, the Lesser General Public License, applies to some + +21 + +specially designated software packages--typically libraries--of the + +22 + +Free Software Foundation and other authors who decide to use it. You + +23 + +can use it too, but we suggest you first think carefully about whether + +24 + +this license or the ordinary General Public License is the better + +25 + +strategy to use in any particular case, based on the explanations below. + +26 + +27 + + When we speak of free software, we are referring to freedom of use, + +28 + +not price. Our General Public Licenses are designed to make sure that + +29 + +you have the freedom to distribute copies of free software (and charge + +30 + +for this service if you wish); that you receive source code or can get + +31 + +it if you want it; that you can change the software and use pieces of + +32 + +it in new free programs; and that you are informed that you can do + +33 + +these things. + +34 + +35 + + To protect your rights, we need to make restrictions that forbid + +36 + +distributors to deny you these rights or to ask you to surrender these + +37 + +rights. These restrictions translate to certain responsibilities for + +38 + +you if you distribute copies of the library or if you modify it. + +39 + +40 + + For example, if you distribute copies of the library, whether gratis + +41 + +or for a fee, you must give the recipients all the rights that we gave + +42 + +you. You must make sure that they, too, receive or can get the source + +43 + +code. If you link other code with the library, you must provide + +44 + +complete object files to the recipients, so that they can relink them + +45 + +with the library after making changes to the library and recompiling + +46 + +it. And you must show them these terms so they know their rights. + +47 + +48 + + We protect your rights with a two-step method: (1) we copyright the + +49 + +library, and (2) we offer you this license, which gives you legal + +50 + +permission to copy, distribute and/or modify the library. + +51 + +52 + + To protect each distributor, we want to make it very clear that + +53 + +there is no warranty for the free library. Also, if the library is + +54 + +modified by someone else and passed on, the recipients should know + +55 + +that what they have is not the original version, so that the original + +56 + +author's reputation will not be affected by problems that might be + +57 + +introduced by others. + +58 + + + +59 + + Finally, software patents pose a constant threat to the existence of + +60 + +any free program. We wish to make sure that a company cannot + +61 + +effectively restrict the users of a free program by obtaining a + +62 + +restrictive license from a patent holder. Therefore, we insist that + +63 + +any patent license obtained for a version of the library must be + +64 + +consistent with the full freedom of use specified in this license. + +65 + +66 + + Most GNU software, including some libraries, is covered by the + +67 + +ordinary GNU General Public License. This license, the GNU Lesser + +68 + +General Public License, applies to certain designated libraries, and + +69 + +is quite different from the ordinary General Public License. We use + +70 + +this license for certain libraries in order to permit linking those + +71 + +libraries into non-free programs. + +72 + +73 + + When a program is linked with a library, whether statically or using + +74 + +a shared library, the combination of the two is legally speaking a + +75 + +combined work, a derivative of the original library. The ordinary + +76 + +General Public License therefore permits such linking only if the + +77 + +entire combination fits its criteria of freedom. The Lesser General + +78 + +Public License permits more lax criteria for linking other code with + +79 + +the library. + +80 + +81 + + We call this license the "Lesser" General Public License because it + +82 + +does Less to protect the user's freedom than the ordinary General + +83 + +Public License. It also provides other free software developers Less + +84 + +of an advantage over competing non-free programs. These disadvantages + +85 + +are the reason we use the ordinary General Public License for many + +86 + +libraries. However, the Lesser license provides advantages in certain + +87 + +special circumstances. + +88 + +89 + + For example, on rare occasions, there may be a special need to + +90 + +encourage the widest possible use of a certain library, so that it becomes + +91 + +a de-facto standard. To achieve this, non-free programs must be + +92 + +allowed to use the library. A more frequent case is that a free + +93 + +library does the same job as widely used non-free libraries. In this + +94 + +case, there is little to gain by limiting the free library to free + +95 + +software only, so we use the Lesser General Public License. + +96 + +97 + + In other cases, permission to use a particular library in non-free + +98 + +programs enables a greater number of people to use a large body of + +99 + +free software. For example, permission to use the GNU C Library in + +100 + +non-free programs enables many more people to use the whole GNU + +101 + +operating system, as well as its variant, the GNU/Linux operating + +102 + +system. + +103 + +104 + + Although the Lesser General Public License is Less protective of the + +105 + +users' freedom, it does ensure that the user of a program that is + +106 + +linked with the Library has the freedom and the wherewithal to run + +107 + +that program using a modified version of the Library. + +108 + +109 + + The precise terms and conditions for copying, distribution and + +110 + +modification follow. Pay close attention to the difference between a + +111 + +"work based on the library" and a "work that uses the library". The + +112 + +former contains code derived from the library, whereas the latter must + +113 + +be combined with the library in order to run. + +114 + + + +115 + + GNU LESSER GENERAL PUBLIC LICENSE + +116 + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +117 + +118 + + 0. This License Agreement applies to any software library or other + +119 + +program which contains a notice placed by the copyright holder or + +120 + +other authorized party saying it may be distributed under the terms of + +121 + +this Lesser General Public License (also called "this License"). + +122 + +Each licensee is addressed as "you". + +123 + +124 + + A "library" means a collection of software functions and/or data + +125 + +prepared so as to be conveniently linked with application programs + +126 + +(which use some of those functions and data) to form executables. + +127 + +128 + + The "Library", below, refers to any such software library or work + +129 + +which has been distributed under these terms. A "work based on the + +130 + +Library" means either the Library or any derivative work under + +131 + +copyright law: that is to say, a work containing the Library or a + +132 + +portion of it, either verbatim or with modifications and/or translated + +133 + +straightforwardly into another language. (Hereinafter, translation is + +134 + +included without limitation in the term "modification".) + +135 + +136 + + "Source code" for a work means the preferred form of the work for + +137 + +making modifications to it. For a library, complete source code means + +138 + +all the source code for all modules it contains, plus any associated + +139 + +interface definition files, plus the scripts used to control compilation + +140 + +and installation of the library. + +141 + +142 + + Activities other than copying, distribution and modification are not + +143 + +covered by this License; they are outside its scope. The act of + +144 + +running a program using the Library is not restricted, and output from + +145 + +such a program is covered only if its contents constitute a work based + +146 + +on the Library (independent of the use of the Library in a tool for + +147 + +writing it). Whether that is true depends on what the Library does + +148 + +and what the program that uses the Library does. + +149 + + + +150 + + 1. You may copy and distribute verbatim copies of the Library's + +151 + +complete source code as you receive it, in any medium, provided that + +152 + +you conspicuously and appropriately publish on each copy an + +153 + +appropriate copyright notice and disclaimer of warranty; keep intact + +154 + +all the notices that refer to this License and to the absence of any + +155 + +warranty; and distribute a copy of this License along with the + +156 + +Library. + +157 + +158 + + You may charge a fee for the physical act of transferring a copy, + +159 + +and you may at your option offer warranty protection in exchange for a + +160 + +fee. + +161 + + + +162 + + 2. You may modify your copy or copies of the Library or any portion + +163 + +of it, thus forming a work based on the Library, and copy and + +164 + +distribute such modifications or work under the terms of Section 1 + +165 + +above, provided that you also meet all of these conditions: + +166 + +167 + + a) The modified work must itself be a software library. + +168 + +169 + + b) You must cause the files modified to carry prominent notices + +170 + + stating that you changed the files and the date of any change. + +171 + +172 + + c) You must cause the whole of the work to be licensed at no + +173 + + charge to all third parties under the terms of this License. + +174 + +175 + + d) If a facility in the modified Library refers to a function or a + +176 + + table of data to be supplied by an application program that uses + +177 + + the facility, other than as an argument passed when the facility + +178 + + is invoked, then you must make a good faith effort to ensure that, + +179 + + in the event an application does not supply such function or + +180 + + table, the facility still operates, and performs whatever part of + +181 + + its purpose remains meaningful. + +182 + +183 + + (For example, a function in a library to compute square roots has + +184 + + a purpose that is entirely well-defined independent of the + +185 + + application. Therefore, Subsection 2d requires that any + +186 + + application-supplied function or table used by this function must + +187 + + be optional: if the application does not supply it, the square + +188 + + root function must still compute square roots.) + +189 + +190 + +These requirements apply to the modified work as a whole. If + +191 + +identifiable sections of that work are not derived from the Library, + +192 + +and can be reasonably considered independent and separate works in + +193 + +themselves, then this License, and its terms, do not apply to those + +194 + +sections when you distribute them as separate works. But when you + +195 + +distribute the same sections as part of a whole which is a work based + +196 + +on the Library, the distribution of the whole must be on the terms of + +197 + +this License, whose permissions for other licensees extend to the + +198 + +entire whole, and thus to each and every part regardless of who wrote + +199 + +it. + +200 + +201 + +Thus, it is not the intent of this section to claim rights or contest + +202 + +your rights to work written entirely by you; rather, the intent is to + +203 + +exercise the right to control the distribution of derivative or + +204 + +collective works based on the Library. + +205 + +206 + +In addition, mere aggregation of another work not based on the Library + +207 + +with the Library (or with a work based on the Library) on a volume of + +208 + +a storage or distribution medium does not bring the other work under + +209 + +the scope of this License. + +210 + +211 + + 3. You may opt to apply the terms of the ordinary GNU General Public + +212 + +License instead of this License to a given copy of the Library. To do + +213 + +this, you must alter all the notices that refer to this License, so + +214 + +that they refer to the ordinary GNU General Public License, version 2, + +215 + +instead of to this License. (If a newer version than version 2 of the + +216 + +ordinary GNU General Public License has appeared, then you can specify + +217 + +that version instead if you wish.) Do not make any other change in + +218 + +these notices. + +219 + + + +220 + + Once this change is made in a given copy, it is irreversible for + +221 + +that copy, so the ordinary GNU General Public License applies to all + +222 + +subsequent copies and derivative works made from that copy. + +223 + +224 + + This option is useful when you wish to copy part of the code of + +225 + +the Library into a program that is not a library. + +226 + +227 + + 4. You may copy and distribute the Library (or a portion or + +228 + +derivative of it, under Section 2) in object code or executable form + +229 + +under the terms of Sections 1 and 2 above provided that you accompany + +230 + +it with the complete corresponding machine-readable source code, which + +231 + +must be distributed under the terms of Sections 1 and 2 above on a + +232 + +medium customarily used for software interchange. + +233 + +234 + + If distribution of object code is made by offering access to copy + +235 + +from a designated place, then offering equivalent access to copy the + +236 + +source code from the same place satisfies the requirement to + +237 + +distribute the source code, even though third parties are not + +238 + +compelled to copy the source along with the object code. + +239 + +240 + + 5. A program that contains no derivative of any portion of the + +241 + +Library, but is designed to work with the Library by being compiled or + +242 + +linked with it, is called a "work that uses the Library". Such a + +243 + +work, in isolation, is not a derivative work of the Library, and + +244 + +therefore falls outside the scope of this License. + +245 + +246 + + However, linking a "work that uses the Library" with the Library + +247 + +creates an executable that is a derivative of the Library (because it + +248 + +contains portions of the Library), rather than a "work that uses the + +249 + +library". The executable is therefore covered by this License. + +250 + +Section 6 states terms for distribution of such executables. + +251 + +252 + + When a "work that uses the Library" uses material from a header file + +253 + +that is part of the Library, the object code for the work may be a + +254 + +derivative work of the Library even though the source code is not. + +255 + +Whether this is true is especially significant if the work can be + +256 + +linked without the Library, or if the work is itself a library. The + +257 + +threshold for this to be true is not precisely defined by law. + +258 + +259 + + If such an object file uses only numerical parameters, data + +260 + +structure layouts and accessors, and small macros and small inline + +261 + +functions (ten lines or less in length), then the use of the object + +262 + +file is unrestricted, regardless of whether it is legally a derivative + +263 + +work. (Executables containing this object code plus portions of the + +264 + +Library will still fall under Section 6.) + +265 + +266 + + Otherwise, if the work is a derivative of the Library, you may + +267 + +distribute the object code for the work under the terms of Section 6. + +268 + +Any executables containing that work also fall under Section 6, + +269 + +whether or not they are linked directly with the Library itself. + +270 + + + +271 + + 6. As an exception to the Sections above, you may also combine or + +272 + +link a "work that uses the Library" with the Library to produce a + +273 + +work containing portions of the Library, and distribute that work + +274 + +under terms of your choice, provided that the terms permit + +275 + +modification of the work for the customer's own use and reverse + +276 + +engineering for debugging such modifications. + +277 + +278 + + You must give prominent notice with each copy of the work that the + +279 + +Library is used in it and that the Library and its use are covered by + +280 + +this License. You must supply a copy of this License. If the work + +281 + +during execution displays copyright notices, you must include the + +282 + +copyright notice for the Library among them, as well as a reference + +283 + +directing the user to the copy of this License. Also, you must do one + +284 + +of these things: + +285 + +286 + + a) Accompany the work with the complete corresponding + +287 + + machine-readable source code for the Library including whatever + +288 + + changes were used in the work (which must be distributed under + +289 + + Sections 1 and 2 above); and, if the work is an executable linked + +290 + + with the Library, with the complete machine-readable "work that + +291 + + uses the Library", as object code and/or source code, so that the + +292 + + user can modify the Library and then relink to produce a modified + +293 + + executable containing the modified Library. (It is understood + +294 + + that the user who changes the contents of definitions files in the + +295 + + Library will not necessarily be able to recompile the application + +296 + + to use the modified definitions.) + +297 + +298 + + b) Use a suitable shared library mechanism for linking with the + +299 + + Library. A suitable mechanism is one that (1) uses at run time a + +300 + + copy of the library already present on the user's computer system, + +301 + + rather than copying library functions into the executable, and (2) + +302 + + will operate properly with a modified version of the library, if + +303 + + the user installs one, as long as the modified version is + +304 + + interface-compatible with the version that the work was made with. + +305 + +306 + + c) Accompany the work with a written offer, valid for at + +307 + + least three years, to give the same user the materials + +308 + + specified in Subsection 6a, above, for a charge no more + +309 + + than the cost of performing this distribution. + +310 + +311 + + d) If distribution of the work is made by offering access to copy + +312 + + from a designated place, offer equivalent access to copy the above + +313 + + specified materials from the same place. + +314 + +315 + + e) Verify that the user has already received a copy of these + +316 + + materials or that you have already sent this user a copy. + +317 + +318 + + For an executable, the required form of the "work that uses the + +319 + +Library" must include any data and utility programs needed for + +320 + +reproducing the executable from it. However, as a special exception, + +321 + +the materials to be distributed need not include anything that is + +322 + +normally distributed (in either source or binary form) with the major + +323 + +components (compiler, kernel, and so on) of the operating system on + +324 + +which the executable runs, unless that component itself accompanies + +325 + +the executable. + +326 + +327 + + It may happen that this requirement contradicts the license + +328 + +restrictions of other proprietary libraries that do not normally + +329 + +accompany the operating system. Such a contradiction means you cannot + +330 + +use both them and the Library together in an executable that you + +331 + +distribute. + +332 + + + +333 + + 7. You may place library facilities that are a work based on the + +334 + +Library side-by-side in a single library together with other library + +335 + +facilities not covered by this License, and distribute such a combined + +336 + +library, provided that the separate distribution of the work based on + +337 + +the Library and of the other library facilities is otherwise + +338 + +permitted, and provided that you do these two things: + +339 + +340 + + a) Accompany the combined library with a copy of the same work + +341 + + based on the Library, uncombined with any other library + +342 + + facilities. This must be distributed under the terms of the + +343 + + Sections above. + +344 + +345 + + b) Give prominent notice with the combined library of the fact + +346 + + that part of it is a work based on the Library, and explaining + +347 + + where to find the accompanying uncombined form of the same work. + +348 + +349 + + 8. You may not copy, modify, sublicense, link with, or distribute + +350 + +the Library except as expressly provided under this License. Any + +351 + +attempt otherwise to copy, modify, sublicense, link with, or + +352 + +distribute the Library is void, and will automatically terminate your + +353 + +rights under this License. However, parties who have received copies, + +354 + +or rights, from you under this License will not have their licenses + +355 + +terminated so long as such parties remain in full compliance. + +356 + +357 + + 9. You are not required to accept this License, since you have not + +358 + +signed it. However, nothing else grants you permission to modify or + +359 + +distribute the Library or its derivative works. These actions are + +360 + +prohibited by law if you do not accept this License. Therefore, by + +361 + +modifying or distributing the Library (or any work based on the + +362 + +Library), you indicate your acceptance of this License to do so, and + +363 + +all its terms and conditions for copying, distributing or modifying + +364 + +the Library or works based on it. + +365 + +366 + + 10. Each time you redistribute the Library (or any work based on the + +367 + +Library), the recipient automatically receives a license from the + +368 + +original licensor to copy, distribute, link with or modify the Library + +369 + +subject to these terms and conditions. You may not impose any further + +370 + +restrictions on the recipients' exercise of the rights granted herein. + +371 + +You are not responsible for enforcing compliance by third parties with + +372 + +this License. + +373 + + + +374 + + 11. If, as a consequence of a court judgment or allegation of patent + +375 + +infringement or for any other reason (not limited to patent issues), + +376 + +conditions are imposed on you (whether by court order, agreement or + +377 + +otherwise) that contradict the conditions of this License, they do not + +378 + +excuse you from the conditions of this License. If you cannot + +379 + +distribute so as to satisfy simultaneously your obligations under this + +380 + +License and any other pertinent obligations, then as a consequence you + +381 + +may not distribute the Library at all. For example, if a patent + +382 + +license would not permit royalty-free redistribution of the Library by + +383 + +all those who receive copies directly or indirectly through you, then + +384 + +the only way you could satisfy both it and this License would be to + +385 + +refrain entirely from distribution of the Library. + +386 + +387 + +If any portion of this section is held invalid or unenforceable under any + +388 + +particular circumstance, the balance of the section is intended to apply, + +389 + +and the section as a whole is intended to apply in other circumstances. + +390 + +391 + +It is not the purpose of this section to induce you to infringe any + +392 + +patents or other property right claims or to contest validity of any + +393 + +such claims; this section has the sole purpose of protecting the + +394 + +integrity of the free software distribution system which is + +395 + +implemented by public license practices. Many people have made + +396 + +generous contributions to the wide range of software distributed + +397 + +through that system in reliance on consistent application of that + +398 + +system; it is up to the author/donor to decide if he or she is willing + +399 + +to distribute software through any other system and a licensee cannot + +400 + +impose that choice. + +401 + +402 + +This section is intended to make thoroughly clear what is believed to + +403 + +be a consequence of the rest of this License. + +404 + +405 + + 12. If the distribution and/or use of the Library is restricted in + +406 + +certain countries either by patents or by copyrighted interfaces, the + +407 + +original copyright holder who places the Library under this License may add + +408 + +an explicit geographical distribution limitation excluding those countries, + +409 + +so that distribution is permitted only in or among countries not thus + +410 + +excluded. In such case, this License incorporates the limitation as if + +411 + +written in the body of this License. + +412 + +413 + + 13. The Free Software Foundation may publish revised and/or new + +414 + +versions of the Lesser General Public License from time to time. + +415 + +Such new versions will be similar in spirit to the present version, + +416 + +but may differ in detail to address new problems or concerns. + +417 + +418 + +Each version is given a distinguishing version number. If the Library + +419 + +specifies a version number of this License which applies to it and + +420 + +"any later version", you have the option of following the terms and + +421 + +conditions either of that version or of any later version published by + +422 + +the Free Software Foundation. If the Library does not specify a + +423 + +license version number, you may choose any version ever published by + +424 + +the Free Software Foundation. + +425 + + + +426 + + 14. If you wish to incorporate parts of the Library into other free + +427 + +programs whose distribution conditions are incompatible with these, + +428 + +write to the author to ask for permission. For software which is + +429 + +copyrighted by the Free Software Foundation, write to the Free + +430 + +Software Foundation; we sometimes make exceptions for this. Our + +431 + +decision will be guided by the two goals of preserving the free status + +432 + +of all derivatives of our free software and of promoting the sharing + +433 + +and reuse of software generally. + +434 + +435 + + NO WARRANTY + +436 + +437 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO + +438 + +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + +439 + +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + +440 + +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY + +441 + +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + +442 + +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + +443 + +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE + +444 + +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME + +445 + +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +******************************************************************* diff --git a/mysql-connector-java-5.1.40/README.txt b/mysql-connector-java-5.1.40/README.txt new file mode 100644 index 0000000..bacea06 --- /dev/null +++ b/mysql-connector-java-5.1.40/README.txt @@ -0,0 +1,2251 @@ +MySQL Connector/J 5.1.40 + +This is a release of MySQL Connector/J, Oracle's dual- +license JDBC Driver for MySQL. For the avoidance of +doubt, this particular copy of the software is released +under the version 2 of the GNU General Public License. +MySQL Connector/J is brought to you by Oracle. + +Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + +License information can be found in the COPYING file. + +MySQL FOSS License Exception +We want free and open source software applications under +certain licenses to be able to use the GPL-licensed MySQL +Connector/J (specified GPL-licensed MySQL client libraries) +despite the fact that not all such FOSS licenses are +compatible with version 2 of the GNU General Public License. +Therefore there are special exceptions to the terms and +conditions of the GPLv2 as applied to these client libraries, +which are identified and described in more detail in the +FOSS License Exception at + + +This software is OSI Certified Open Source Software. +OSI Certified is a certification mark of the Open Source Initiative. + +This distribution may include materials developed by third +parties. For license and attribution notices for these +materials, please refer to the documentation that accompanies +this distribution (see the "Licenses for Third-Party Components" +appendix) or view the online documentation at + +A copy of the license/notices is also reproduced below. + +GPLv2 Disclaimer +For the avoidance of doubt, except that if any license choice +other than GPL or LGPL is available it will apply instead, +Oracle elects to use only the General Public License version 2 +(GPLv2) at this time for any software where a choice of GPL +license versions is made available with the language indicating +that GPLv2 or any later version may be used, or where a choice +of which version of the GPL is applied is otherwise unspecified. + +CONTENTS + +* Documentation Location +* Third-Party Component Notices + +DOCUMENTATION LOCATION + +The documentation formerly contained in this file has moved +into the 'doc' directory, where it is available in HTML, PDF +and plaintext forms. + +You may also find the latest copy of the documentation on +the MySQL website at +http://dev.mysql.com/doc/connector-j/en + +*************************************************************** + +Third-Party Component Notices + +**************************************************************** + +%%The following software may be included in this product: +c3p0:JDBC DataSources/Resource Pools + +Use of any of this software is governed by the terms of the license below: + +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +******************************************************************* + +%%The following software may be included in this product: +jboss-common-jdbc-wrapper.jar + +Use of any of this software is governed by the terms of the license below: + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1.] + + Preamble + +14 + +15 + + The licenses for most software are designed to take away your + +16 + +freedom to share and change it. By contrast, the GNU General Public + +17 + +Licenses are intended to guarantee your freedom to share and change + +18 + +free software--to make sure the software is free for all its users. + +19 + +20 + + This license, the Lesser General Public License, applies to some + +21 + +specially designated software packages--typically libraries--of the + +22 + +Free Software Foundation and other authors who decide to use it. You + +23 + +can use it too, but we suggest you first think carefully about whether + +24 + +this license or the ordinary General Public License is the better + +25 + +strategy to use in any particular case, based on the explanations below. + +26 + +27 + + When we speak of free software, we are referring to freedom of use, + +28 + +not price. Our General Public Licenses are designed to make sure that + +29 + +you have the freedom to distribute copies of free software (and charge + +30 + +for this service if you wish); that you receive source code or can get + +31 + +it if you want it; that you can change the software and use pieces of + +32 + +it in new free programs; and that you are informed that you can do + +33 + +these things. + +34 + +35 + + To protect your rights, we need to make restrictions that forbid + +36 + +distributors to deny you these rights or to ask you to surrender these + +37 + +rights. These restrictions translate to certain responsibilities for + +38 + +you if you distribute copies of the library or if you modify it. + +39 + +40 + + For example, if you distribute copies of the library, whether gratis + +41 + +or for a fee, you must give the recipients all the rights that we gave + +42 + +you. You must make sure that they, too, receive or can get the source + +43 + +code. If you link other code with the library, you must provide + +44 + +complete object files to the recipients, so that they can relink them + +45 + +with the library after making changes to the library and recompiling + +46 + +it. And you must show them these terms so they know their rights. + +47 + +48 + + We protect your rights with a two-step method: (1) we copyright the + +49 + +library, and (2) we offer you this license, which gives you legal + +50 + +permission to copy, distribute and/or modify the library. + +51 + +52 + + To protect each distributor, we want to make it very clear that + +53 + +there is no warranty for the free library. Also, if the library is + +54 + +modified by someone else and passed on, the recipients should know + +55 + +that what they have is not the original version, so that the original + +56 + +author's reputation will not be affected by problems that might be + +57 + +introduced by others. + +58 + + + +59 + + Finally, software patents pose a constant threat to the existence of + +60 + +any free program. We wish to make sure that a company cannot + +61 + +effectively restrict the users of a free program by obtaining a + +62 + +restrictive license from a patent holder. Therefore, we insist that + +63 + +any patent license obtained for a version of the library must be + +64 + +consistent with the full freedom of use specified in this license. + +65 + +66 + + Most GNU software, including some libraries, is covered by the + +67 + +ordinary GNU General Public License. This license, the GNU Lesser + +68 + +General Public License, applies to certain designated libraries, and + +69 + +is quite different from the ordinary General Public License. We use + +70 + +this license for certain libraries in order to permit linking those + +71 + +libraries into non-free programs. + +72 + +73 + + When a program is linked with a library, whether statically or using + +74 + +a shared library, the combination of the two is legally speaking a + +75 + +combined work, a derivative of the original library. The ordinary + +76 + +General Public License therefore permits such linking only if the + +77 + +entire combination fits its criteria of freedom. The Lesser General + +78 + +Public License permits more lax criteria for linking other code with + +79 + +the library. + +80 + +81 + + We call this license the "Lesser" General Public License because it + +82 + +does Less to protect the user's freedom than the ordinary General + +83 + +Public License. It also provides other free software developers Less + +84 + +of an advantage over competing non-free programs. These disadvantages + +85 + +are the reason we use the ordinary General Public License for many + +86 + +libraries. However, the Lesser license provides advantages in certain + +87 + +special circumstances. + +88 + +89 + + For example, on rare occasions, there may be a special need to + +90 + +encourage the widest possible use of a certain library, so that it becomes + +91 + +a de-facto standard. To achieve this, non-free programs must be + +92 + +allowed to use the library. A more frequent case is that a free + +93 + +library does the same job as widely used non-free libraries. In this + +94 + +case, there is little to gain by limiting the free library to free + +95 + +software only, so we use the Lesser General Public License. + +96 + +97 + + In other cases, permission to use a particular library in non-free + +98 + +programs enables a greater number of people to use a large body of + +99 + +free software. For example, permission to use the GNU C Library in + +100 + +non-free programs enables many more people to use the whole GNU + +101 + +operating system, as well as its variant, the GNU/Linux operating + +102 + +system. + +103 + +104 + + Although the Lesser General Public License is Less protective of the + +105 + +users' freedom, it does ensure that the user of a program that is + +106 + +linked with the Library has the freedom and the wherewithal to run + +107 + +that program using a modified version of the Library. + +108 + +109 + + The precise terms and conditions for copying, distribution and + +110 + +modification follow. Pay close attention to the difference between a + +111 + +"work based on the library" and a "work that uses the library". The + +112 + +former contains code derived from the library, whereas the latter must + +113 + +be combined with the library in order to run. + +114 + + + +115 + + GNU LESSER GENERAL PUBLIC LICENSE + +116 + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +117 + +118 + + 0. This License Agreement applies to any software library or other + +119 + +program which contains a notice placed by the copyright holder or + +120 + +other authorized party saying it may be distributed under the terms of + +121 + +this Lesser General Public License (also called "this License"). + +122 + +Each licensee is addressed as "you". + +123 + +124 + + A "library" means a collection of software functions and/or data + +125 + +prepared so as to be conveniently linked with application programs + +126 + +(which use some of those functions and data) to form executables. + +127 + +128 + + The "Library", below, refers to any such software library or work + +129 + +which has been distributed under these terms. A "work based on the + +130 + +Library" means either the Library or any derivative work under + +131 + +copyright law: that is to say, a work containing the Library or a + +132 + +portion of it, either verbatim or with modifications and/or translated + +133 + +straightforwardly into another language. (Hereinafter, translation is + +134 + +included without limitation in the term "modification".) + +135 + +136 + + "Source code" for a work means the preferred form of the work for + +137 + +making modifications to it. For a library, complete source code means + +138 + +all the source code for all modules it contains, plus any associated + +139 + +interface definition files, plus the scripts used to control compilation + +140 + +and installation of the library. + +141 + +142 + + Activities other than copying, distribution and modification are not + +143 + +covered by this License; they are outside its scope. The act of + +144 + +running a program using the Library is not restricted, and output from + +145 + +such a program is covered only if its contents constitute a work based + +146 + +on the Library (independent of the use of the Library in a tool for + +147 + +writing it). Whether that is true depends on what the Library does + +148 + +and what the program that uses the Library does. + +149 + + + +150 + + 1. You may copy and distribute verbatim copies of the Library's + +151 + +complete source code as you receive it, in any medium, provided that + +152 + +you conspicuously and appropriately publish on each copy an + +153 + +appropriate copyright notice and disclaimer of warranty; keep intact + +154 + +all the notices that refer to this License and to the absence of any + +155 + +warranty; and distribute a copy of this License along with the + +156 + +Library. + +157 + +158 + + You may charge a fee for the physical act of transferring a copy, + +159 + +and you may at your option offer warranty protection in exchange for a + +160 + +fee. + +161 + + + +162 + + 2. You may modify your copy or copies of the Library or any portion + +163 + +of it, thus forming a work based on the Library, and copy and + +164 + +distribute such modifications or work under the terms of Section 1 + +165 + +above, provided that you also meet all of these conditions: + +166 + +167 + + a) The modified work must itself be a software library. + +168 + +169 + + b) You must cause the files modified to carry prominent notices + +170 + + stating that you changed the files and the date of any change. + +171 + +172 + + c) You must cause the whole of the work to be licensed at no + +173 + + charge to all third parties under the terms of this License. + +174 + +175 + + d) If a facility in the modified Library refers to a function or a + +176 + + table of data to be supplied by an application program that uses + +177 + + the facility, other than as an argument passed when the facility + +178 + + is invoked, then you must make a good faith effort to ensure that, + +179 + + in the event an application does not supply such function or + +180 + + table, the facility still operates, and performs whatever part of + +181 + + its purpose remains meaningful. + +182 + +183 + + (For example, a function in a library to compute square roots has + +184 + + a purpose that is entirely well-defined independent of the + +185 + + application. Therefore, Subsection 2d requires that any + +186 + + application-supplied function or table used by this function must + +187 + + be optional: if the application does not supply it, the square + +188 + + root function must still compute square roots.) + +189 + +190 + +These requirements apply to the modified work as a whole. If + +191 + +identifiable sections of that work are not derived from the Library, + +192 + +and can be reasonably considered independent and separate works in + +193 + +themselves, then this License, and its terms, do not apply to those + +194 + +sections when you distribute them as separate works. But when you + +195 + +distribute the same sections as part of a whole which is a work based + +196 + +on the Library, the distribution of the whole must be on the terms of + +197 + +this License, whose permissions for other licensees extend to the + +198 + +entire whole, and thus to each and every part regardless of who wrote + +199 + +it. + +200 + +201 + +Thus, it is not the intent of this section to claim rights or contest + +202 + +your rights to work written entirely by you; rather, the intent is to + +203 + +exercise the right to control the distribution of derivative or + +204 + +collective works based on the Library. + +205 + +206 + +In addition, mere aggregation of another work not based on the Library + +207 + +with the Library (or with a work based on the Library) on a volume of + +208 + +a storage or distribution medium does not bring the other work under + +209 + +the scope of this License. + +210 + +211 + + 3. You may opt to apply the terms of the ordinary GNU General Public + +212 + +License instead of this License to a given copy of the Library. To do + +213 + +this, you must alter all the notices that refer to this License, so + +214 + +that they refer to the ordinary GNU General Public License, version 2, + +215 + +instead of to this License. (If a newer version than version 2 of the + +216 + +ordinary GNU General Public License has appeared, then you can specify + +217 + +that version instead if you wish.) Do not make any other change in + +218 + +these notices. + +219 + + + +220 + + Once this change is made in a given copy, it is irreversible for + +221 + +that copy, so the ordinary GNU General Public License applies to all + +222 + +subsequent copies and derivative works made from that copy. + +223 + +224 + + This option is useful when you wish to copy part of the code of + +225 + +the Library into a program that is not a library. + +226 + +227 + + 4. You may copy and distribute the Library (or a portion or + +228 + +derivative of it, under Section 2) in object code or executable form + +229 + +under the terms of Sections 1 and 2 above provided that you accompany + +230 + +it with the complete corresponding machine-readable source code, which + +231 + +must be distributed under the terms of Sections 1 and 2 above on a + +232 + +medium customarily used for software interchange. + +233 + +234 + + If distribution of object code is made by offering access to copy + +235 + +from a designated place, then offering equivalent access to copy the + +236 + +source code from the same place satisfies the requirement to + +237 + +distribute the source code, even though third parties are not + +238 + +compelled to copy the source along with the object code. + +239 + +240 + + 5. A program that contains no derivative of any portion of the + +241 + +Library, but is designed to work with the Library by being compiled or + +242 + +linked with it, is called a "work that uses the Library". Such a + +243 + +work, in isolation, is not a derivative work of the Library, and + +244 + +therefore falls outside the scope of this License. + +245 + +246 + + However, linking a "work that uses the Library" with the Library + +247 + +creates an executable that is a derivative of the Library (because it + +248 + +contains portions of the Library), rather than a "work that uses the + +249 + +library". The executable is therefore covered by this License. + +250 + +Section 6 states terms for distribution of such executables. + +251 + +252 + + When a "work that uses the Library" uses material from a header file + +253 + +that is part of the Library, the object code for the work may be a + +254 + +derivative work of the Library even though the source code is not. + +255 + +Whether this is true is especially significant if the work can be + +256 + +linked without the Library, or if the work is itself a library. The + +257 + +threshold for this to be true is not precisely defined by law. + +258 + +259 + + If such an object file uses only numerical parameters, data + +260 + +structure layouts and accessors, and small macros and small inline + +261 + +functions (ten lines or less in length), then the use of the object + +262 + +file is unrestricted, regardless of whether it is legally a derivative + +263 + +work. (Executables containing this object code plus portions of the + +264 + +Library will still fall under Section 6.) + +265 + +266 + + Otherwise, if the work is a derivative of the Library, you may + +267 + +distribute the object code for the work under the terms of Section 6. + +268 + +Any executables containing that work also fall under Section 6, + +269 + +whether or not they are linked directly with the Library itself. + +270 + + + +271 + + 6. As an exception to the Sections above, you may also combine or + +272 + +link a "work that uses the Library" with the Library to produce a + +273 + +work containing portions of the Library, and distribute that work + +274 + +under terms of your choice, provided that the terms permit + +275 + +modification of the work for the customer's own use and reverse + +276 + +engineering for debugging such modifications. + +277 + +278 + + You must give prominent notice with each copy of the work that the + +279 + +Library is used in it and that the Library and its use are covered by + +280 + +this License. You must supply a copy of this License. If the work + +281 + +during execution displays copyright notices, you must include the + +282 + +copyright notice for the Library among them, as well as a reference + +283 + +directing the user to the copy of this License. Also, you must do one + +284 + +of these things: + +285 + +286 + + a) Accompany the work with the complete corresponding + +287 + + machine-readable source code for the Library including whatever + +288 + + changes were used in the work (which must be distributed under + +289 + + Sections 1 and 2 above); and, if the work is an executable linked + +290 + + with the Library, with the complete machine-readable "work that + +291 + + uses the Library", as object code and/or source code, so that the + +292 + + user can modify the Library and then relink to produce a modified + +293 + + executable containing the modified Library. (It is understood + +294 + + that the user who changes the contents of definitions files in the + +295 + + Library will not necessarily be able to recompile the application + +296 + + to use the modified definitions.) + +297 + +298 + + b) Use a suitable shared library mechanism for linking with the + +299 + + Library. A suitable mechanism is one that (1) uses at run time a + +300 + + copy of the library already present on the user's computer system, + +301 + + rather than copying library functions into the executable, and (2) + +302 + + will operate properly with a modified version of the library, if + +303 + + the user installs one, as long as the modified version is + +304 + + interface-compatible with the version that the work was made with. + +305 + +306 + + c) Accompany the work with a written offer, valid for at + +307 + + least three years, to give the same user the materials + +308 + + specified in Subsection 6a, above, for a charge no more + +309 + + than the cost of performing this distribution. + +310 + +311 + + d) If distribution of the work is made by offering access to copy + +312 + + from a designated place, offer equivalent access to copy the above + +313 + + specified materials from the same place. + +314 + +315 + + e) Verify that the user has already received a copy of these + +316 + + materials or that you have already sent this user a copy. + +317 + +318 + + For an executable, the required form of the "work that uses the + +319 + +Library" must include any data and utility programs needed for + +320 + +reproducing the executable from it. However, as a special exception, + +321 + +the materials to be distributed need not include anything that is + +322 + +normally distributed (in either source or binary form) with the major + +323 + +components (compiler, kernel, and so on) of the operating system on + +324 + +which the executable runs, unless that component itself accompanies + +325 + +the executable. + +326 + +327 + + It may happen that this requirement contradicts the license + +328 + +restrictions of other proprietary libraries that do not normally + +329 + +accompany the operating system. Such a contradiction means you cannot + +330 + +use both them and the Library together in an executable that you + +331 + +distribute. + +332 + + + +333 + + 7. You may place library facilities that are a work based on the + +334 + +Library side-by-side in a single library together with other library + +335 + +facilities not covered by this License, and distribute such a combined + +336 + +library, provided that the separate distribution of the work based on + +337 + +the Library and of the other library facilities is otherwise + +338 + +permitted, and provided that you do these two things: + +339 + +340 + + a) Accompany the combined library with a copy of the same work + +341 + + based on the Library, uncombined with any other library + +342 + + facilities. This must be distributed under the terms of the + +343 + + Sections above. + +344 + +345 + + b) Give prominent notice with the combined library of the fact + +346 + + that part of it is a work based on the Library, and explaining + +347 + + where to find the accompanying uncombined form of the same work. + +348 + +349 + + 8. You may not copy, modify, sublicense, link with, or distribute + +350 + +the Library except as expressly provided under this License. Any + +351 + +attempt otherwise to copy, modify, sublicense, link with, or + +352 + +distribute the Library is void, and will automatically terminate your + +353 + +rights under this License. However, parties who have received copies, + +354 + +or rights, from you under this License will not have their licenses + +355 + +terminated so long as such parties remain in full compliance. + +356 + +357 + + 9. You are not required to accept this License, since you have not + +358 + +signed it. However, nothing else grants you permission to modify or + +359 + +distribute the Library or its derivative works. These actions are + +360 + +prohibited by law if you do not accept this License. Therefore, by + +361 + +modifying or distributing the Library (or any work based on the + +362 + +Library), you indicate your acceptance of this License to do so, and + +363 + +all its terms and conditions for copying, distributing or modifying + +364 + +the Library or works based on it. + +365 + +366 + + 10. Each time you redistribute the Library (or any work based on the + +367 + +Library), the recipient automatically receives a license from the + +368 + +original licensor to copy, distribute, link with or modify the Library + +369 + +subject to these terms and conditions. You may not impose any further + +370 + +restrictions on the recipients' exercise of the rights granted herein. + +371 + +You are not responsible for enforcing compliance by third parties with + +372 + +this License. + +373 + + + +374 + + 11. If, as a consequence of a court judgment or allegation of patent + +375 + +infringement or for any other reason (not limited to patent issues), + +376 + +conditions are imposed on you (whether by court order, agreement or + +377 + +otherwise) that contradict the conditions of this License, they do not + +378 + +excuse you from the conditions of this License. If you cannot + +379 + +distribute so as to satisfy simultaneously your obligations under this + +380 + +License and any other pertinent obligations, then as a consequence you + +381 + +may not distribute the Library at all. For example, if a patent + +382 + +license would not permit royalty-free redistribution of the Library by + +383 + +all those who receive copies directly or indirectly through you, then + +384 + +the only way you could satisfy both it and this License would be to + +385 + +refrain entirely from distribution of the Library. + +386 + +387 + +If any portion of this section is held invalid or unenforceable under any + +388 + +particular circumstance, the balance of the section is intended to apply, + +389 + +and the section as a whole is intended to apply in other circumstances. + +390 + +391 + +It is not the purpose of this section to induce you to infringe any + +392 + +patents or other property right claims or to contest validity of any + +393 + +such claims; this section has the sole purpose of protecting the + +394 + +integrity of the free software distribution system which is + +395 + +implemented by public license practices. Many people have made + +396 + +generous contributions to the wide range of software distributed + +397 + +through that system in reliance on consistent application of that + +398 + +system; it is up to the author/donor to decide if he or she is willing + +399 + +to distribute software through any other system and a licensee cannot + +400 + +impose that choice. + +401 + +402 + +This section is intended to make thoroughly clear what is believed to + +403 + +be a consequence of the rest of this License. + +404 + +405 + + 12. If the distribution and/or use of the Library is restricted in + +406 + +certain countries either by patents or by copyrighted interfaces, the + +407 + +original copyright holder who places the Library under this License may add + +408 + +an explicit geographical distribution limitation excluding those countries, + +409 + +so that distribution is permitted only in or among countries not thus + +410 + +excluded. In such case, this License incorporates the limitation as if + +411 + +written in the body of this License. + +412 + +413 + + 13. The Free Software Foundation may publish revised and/or new + +414 + +versions of the Lesser General Public License from time to time. + +415 + +Such new versions will be similar in spirit to the present version, + +416 + +but may differ in detail to address new problems or concerns. + +417 + +418 + +Each version is given a distinguishing version number. If the Library + +419 + +specifies a version number of this License which applies to it and + +420 + +"any later version", you have the option of following the terms and + +421 + +conditions either of that version or of any later version published by + +422 + +the Free Software Foundation. If the Library does not specify a + +423 + +license version number, you may choose any version ever published by + +424 + +the Free Software Foundation. + +425 + + + +426 + + 14. If you wish to incorporate parts of the Library into other free + +427 + +programs whose distribution conditions are incompatible with these, + +428 + +write to the author to ask for permission. For software which is + +429 + +copyrighted by the Free Software Foundation, write to the Free + +430 + +Software Foundation; we sometimes make exceptions for this. Our + +431 + +decision will be guided by the two goals of preserving the free status + +432 + +of all derivatives of our free software and of promoting the sharing + +433 + +and reuse of software generally. + +434 + +435 + + NO WARRANTY + +436 + +437 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO + +438 + +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + +439 + +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + +440 + +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY + +441 + +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + +442 + +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + +443 + +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE + +444 + +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME + +445 + +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +******************************************************************* diff --git a/mysql-connector-java-5.1.40/build.xml b/mysql-connector-java-5.1.40/build.xml new file mode 100644 index 0000000..8ff3fa1 --- /dev/null +++ b/mysql-connector-java-5.1.40/build.xml @@ -0,0 +1,1763 @@ + + + + +Compiling Connector/J +===================== + +Connector/J 5.1 supports both JDBC 3 and JDBC 4+ with a single code base. This requires JDBC 3 classes to be compiled with Java 5 and JDBC 4+ to be compiled +with Java 8. +The variables 'com.mysql.jdbc.jdk5' and 'com.mysql.jdbc.jdk8' are used to find the respective compilers when building the driver. Side by side +with these, the variable 'com.mysql.jdbc.extra.libs' must point to the location of third-party libraries that we don't distribute and are required for +compiling. +Compiling all JDBC 4+ code with Java 8 alone produces some warnings since JDBC 4.0 and JDBC 4.1 classes don't require a compliler higher than Java 6. To get +more precise results and avoid warnings use the property 'com.mysql.jdbc.jre6.rtjar' to point to the Java 6 'rt.jar' library (usually in 'jre/lib/rt.jar'). +Further details can be found in http://dev.mysql.com/doc/connector-j/en/connector-j-installing-source.html. + +Targets: "dist", "full-dist", "package", "full-package", "full-package-no-sources", "compile", "install" + + +Testing Connector/J +=================== + +Connector/J 5.1 ships with an extensive test suite that can be run simply by providing a MySQL JDBC URL in the variable 'com.mysql.jdbc.testsuite.url' and by +calling the target "test". If nothing more is set, these tests run with the JVMs referred in the variables 'com.mysql.jdbc.jdk5' and 'com.mysql.jdbc.jdk8' for +JDBC 3 and JDBC 4+ related tests respectively. Alternatively, all tests can be run with a single JVM, provided that it is pointed out in the variable +'com.mysql.jdbc.testsuite.jvm'. +Running the full test suite with different JVMs and different MySQL servers at once is also possible. The variables 'com.mysql.jdbc.testsuite.jvm' and +'com.mysql.jdbc.testsuite.url' can be suffixed with an index ".1" to ".9" to indicate the multiple alternatives which results in the matrix of all possible +combinations. The target "test-multijvm" must be called in this case. +Running only one test set is possible by setting the variable 'test' with the class' fully qualified name. If also a comma separated list of test names is +provided in the variable 'methods', then only these will be executed. + +Targets: "test", "test-multijvm" + + +Compliance +========== + +This file ships with targets for these matters. Further details can be found in the respective targets. + + +Coverage and instrumentation +============================ + +This file ships with target "test-coverage" for collecting coverage results and "report-coverage" for creating the HTML coverage report. +The JCov library needed to run these targets can be found at http://hg.openjdk.java.net/code-tools/jcov, it's JARs should be placed +into ${com.mysql.jdbc.extra.libs}/jcov directory. +The "test-coverage" target instruments classes using JCov, runs tests collecting coverage info into result file set as +${com.mysql.jdbc.coverage.result.dir}/${com.mysql.jdbc.coverage.result.name}. +The "report-coverage" target first merges coverage result files which full paths are passed via 'com.mysql.jdbc.coverage.merge.files' +property into file which path is passed via 'com.mysql.jdbc.coverage.merge.result' property, then builds the HTML report from +'com.mysql.jdbc.coverage.merge.result' file into the directory passed via 'com.mysql.jdbc.coverage.report.dir' property. +If some properties are not passed to ANT script then default values are used: + com.mysql.jdbc.coverage.result.dir - "${buildDir}/coverage" + com.mysql.jdbc.coverage.result.name - "result.xml" + com.mysql.jdbc.coverage.merge.files - none, merge step is skipped + com.mysql.jdbc.coverage.merge.result - ${com.mysql.jdbc.coverage.result.dir}/result.xml + com.mysql.jdbc.coverage.report.dir - ${com.mysql.jdbc.coverage.result.dir}/report + +Targets: "test-coverage", "report-coverage" + + +MySQL Fabric support and testing +================================ + +Support for MySQL Fabric connections is seamlessly included in Connector/J, so when the driver is compiled it already includes all the required classes for it. +Testing MySQL Fabric support can be done by setting specific variables and running the respective targets. Further details can be found in the last targets in +this file. + + +Sample 'build.properties' that can be used to compile, build, test and test with multi JVMs +=========================================================================================== +~~~start cut here~~~ +# Basic settings for 'compile', 'test', 'test-multijvm' and targets that depend on these. + +# External libraries needed for both compiling and testing: +# - Further details in http://dev.mysql.com/doc/connector-j/en/connector-j-installing-source.html +# - Mandatory. +com.mysql.jdbc.extra.libs=<full_path_to_connector-j-jardeps> + +# JDKs needed for compiling: +# - Must point to JDK home directories. +# - Also used for testing if 'com.mysql.jdbc.testsuite.jvm' is not provided. +# - Mandatory. +com.mysql.jdbc.jdk5=<full_path_to_jdk1.5> +com.mysql.jdbc.jdk8=<full_path_to_jdk1.8> + +# Java 6 runtime libraries for compiling JDBC 4.0 and JDBC 4.1 classes: +# - Must point to the library 'jre/lib/rt.jar'. +# - Optional. If not provided, Java 8 runtime is used in its place. +com.mysql.jdbc.jre6.rtjar=<full_path_to_jre1.6/lib/rt.jar> + +# Single JVM/MySQL tests: +# - Must point to JDK or JRE home directories. +# - If not provided, JDBC 3 tests are run with 'com.mysql.jdbc.jdk5'. +# - If not provided or pointing to a Java5 JVM, JDBC 4+(<4.2) tests are run with 'com.mysql.jdbc.jdk8'. +# - If not provided or pointing to a JVM lower than 8, JDBC 4.2 tests are run with 'com.mysql.jdbc.jdk8'. +# - Optional. +com.mysql.jdbc.testsuite.jvm=<full_path_to_a_jdk_or_jre> +# - URL to the test database. +# - Any of the current MySQL versions. +# - Mandatory for 'test' target. +com.mysql.jdbc.testsuite.url=jdbc:mysql://<host>:<port>/<testDB>?user=<user>&password=<pwd> + +# Multiple JVM/MySQL tests: +# - Must point to JDK or JRE home directories. +# - If pointing to a Java5 JVM, JDBC 4+(<4.2) tests are run with 'com.mysql.jdbc.jdk8' instead. +# - If pointing to a JVM lower than 8, JDBC 4.2 tests are run with 'com.mysql.jdbc.jdk8'. +# - Mandatory (at least one, at most eight) for 'test-multijdk' target. +com.mysql.jdbc.testsuite.jvm.1=<full_path_to_a_jdk_or_jre> +com.mysql.jdbc.testsuite.jvm.2=<full_path_to_a_jdk_or_jre> +com.mysql.jdbc.testsuite.jvm.3=<full_path_to_a_jdk_or_jre> +# - URL to the test database(s). +# - Any of the current MySQL versions. +# - Mandatory (at least one, at most eight) for 'test-multijdk' target. +com.mysql.jdbc.testsuite.url.1=jdbc:mysql://<host1>:<port1>/<testDB1>?user=<user1>&password=<pwd1> +com.mysql.jdbc.testsuite.url.2=jdbc:mysql://<host2>:<port2>/<testDB2>?user=<user2>&password=<pwd2> + +# Cancels re-compiling between successive test executions. +# - Implicit in multiple JVM/MySQL in between iterations. +# - Comment variable if not needed. +# - Optional. +com.mysql.jdbc.noCleanBetweenCompiles=yes + +# Other targets may require specific settings. Check 'build.xml' for details. +~~~end cut here~~~ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Removing sources from '${toPackage}' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Compiling MySQL Connector/J JDBC 3 implementation with '${com.mysql.jdbc.jdk5}' to '${compiler.output}' + + + + + + + + + + + + + + + + + + + Compiling MySQL Connector/J JDBC 4+ implementation with '${com.mysql.jdbc.jdk8}' to '${compiler.output}' + + + + + + + + + + + + + + + + + + + + + + Compiling MySQL Connector/J testsuite with '${com.mysql.jdbc.jdk5}' to '${compiler.output}' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Compiling MySQL Connector/J-c3p0 integration with '${com.mysql.jdbc.jdk5}' to '${compiler.output}' + + + + + + + + + + + Compiling MySQL Connector/J-jboss integration with '${com.mysql.jdbc.jdk5}' to '${compiler.output}' + + + + + + + + + + + Compiling MySQL Connector/J-log4j integration with '${com.mysql.jdbc.jdk5}' to '${compiler.output}' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${test.message.jdbc3orcommon} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${test.message.jdbc4} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${test.message.jdbc42} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${test.message.dontfail} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running compliance test suite against '${com.mysql.jdbc.compliance.url.final}' with jvm '${com.mysql.jdbc3.testsuite.jvm.java}' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${test.message.dontfail} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mysql-connector-java-5.1.40/docs/README.txt b/mysql-connector-java-5.1.40/docs/README.txt new file mode 100644 index 0000000..3a200a5 --- /dev/null +++ b/mysql-connector-java-5.1.40/docs/README.txt @@ -0,0 +1,8066 @@ +MySQL Connector/J 5.1 Developer Guide + + Abstract + + This manual describes how to install, configure, and develop + database applications using MySQL Connector/J, the JDBC + driver for communicating with MySQL servers. + + For notes detailing the changes in each release of + Connector/J, see MySQL Connector/J Release Notes + (http://dev.mysql.com/doc/relnotes/connector-j/en/). + + For legal information, see the Legal Notices. + + For help with using MySQL, please visit either the MySQL + Forums (http://forums.mysql.com) or MySQL Mailing Lists + (http://lists.mysql.com), where you can discuss your issues + with other MySQL users. + + For additional documentation on MySQL products, including + translations of the documentation into other languages, and + downloadable versions in variety of formats, including HTML + and PDF formats, see the MySQL Documentation Library + (http://dev.mysql.com/doc). + + Licensing information. This product may include third-party + software, used under license. If you are using a Commercial + release of MySQL Connector/J 5.1, see this document + (http://downloads.mysql.com/docs/licenses/connector-j-5.1-com + -en.pdf) for licensing information, including licensing + information relating to third-party software that may be + included in this Commercial release. If you are using a + Community release of MySQL Connector/J 5.1, see this document + (http://downloads.mysql.com/docs/licenses/connector-j-5.1-gpl + -en.pdf) for licensing information, including licensing + information relating to third-party software that may be + included in this Community release. + + Document generated on: 2016-03-28 (revision: 47177) + ________________________________________________________ + +Preface and Legal Notices + + This manual describes how to install, configure, and develop + database applications using MySQL Connector/J, the JDBC + driver for communicating with MySQL servers. + +Legal Notices + + Copyright (c) 1998, 2016, Oracle and/or its affiliates. All + rights reserved. + + This software and related documentation are provided under a + license agreement containing restrictions on use and + disclosure and are protected by intellectual property laws. + Except as expressly permitted in your license agreement or + allowed by law, you may not use, copy, reproduce, translate, + broadcast, modify, license, transmit, distribute, exhibit, + perform, publish, or display any part, in any form, or by any + means. Reverse engineering, disassembly, or decompilation of + this software, unless required by law for interoperability, + is prohibited. + + The information contained herein is subject to change without + notice and is not warranted to be error-free. If you find any + errors, please report them to us in writing. + + If this is software or related documentation that is + delivered to the U.S. Government or anyone licensing it on + behalf of the U.S. Government, then the following notice is + applicable: + + U.S. GOVERNMENT END USERS: Oracle programs, including any + operating system, integrated software, any programs installed + on the hardware, and/or documentation, delivered to U.S. + Government end users are "commercial computer software" + pursuant to the applicable Federal Acquisition Regulation and + agency-specific supplemental regulations. As such, use, + duplication, disclosure, modification, and adaptation of the + programs, including any operating system, integrated + software, any programs installed on the hardware, and/or + documentation, shall be subject to license terms and license + restrictions applicable to the programs. No other rights are + granted to the U.S. Government. + + This software or hardware is developed for general use in a + variety of information management applications. It is not + developed or intended for use in any inherently dangerous + applications, including applications that may create a risk + of personal injury. If you use this software or hardware in + dangerous applications, then you shall be responsible to take + all appropriate fail-safe, backup, redundancy, and other + measures to ensure its safe use. Oracle Corporation and its + affiliates disclaim any liability for any damages caused by + use of this software or hardware in dangerous applications. + + Oracle and Java are registered trademarks of Oracle and/or + its affiliates. Other names may be trademarks of their + respective owners. + + Intel and Intel Xeon are trademarks or registered trademarks + of Intel Corporation. All SPARC trademarks are used under + license and are trademarks or registered trademarks of SPARC + International, Inc. AMD, Opteron, the AMD logo, and the AMD + Opteron logo are trademarks or registered trademarks of + Advanced Micro Devices. UNIX is a registered trademark of The + Open Group. + + This software or hardware and documentation may provide + access to or information about content, products, and + services from third parties. Oracle Corporation and its + affiliates are not responsible for and expressly disclaim all + warranties of any kind with respect to third-party content, + products, and services unless otherwise set forth in an + applicable agreement between you and Oracle. Oracle + Corporation and its affiliates will not be responsible for + any loss, costs, or damages incurred due to your access to or + use of third-party content, products, or services, except as + set forth in an applicable agreement between you and Oracle. + + Documentation Accessibility + + For information about Oracle's commitment to accessibility, + visit the Oracle Accessibility Program website at + http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc. + + Access to Oracle Support + + Oracle customers that have purchased support have access to + electronic support through My Oracle Support. For + information, visit + http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info or + visit http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs + if you are hearing impaired. + + This documentation is NOT distributed under a GPL license. + Use of this documentation is subject to the following terms: + + You may create a printed copy of this documentation solely + for your own personal use. Conversion to other formats is + allowed as long as the actual content is not altered or + edited in any way. You shall not publish or distribute this + documentation in any form or on any media, except if you + distribute the documentation in a manner similar to how + Oracle disseminates it (that is, electronically for download + on a Web site with the software) or on a CD-ROM or similar + medium, provided however that the documentation is + disseminated together with the software on the same medium. + Any other use, such as any dissemination of printed copies or + use of this documentation, in whole or in part, in another + publication, requires the prior written consent from an + authorized representative of Oracle. Oracle and/or its + affiliates reserve any and all rights to this documentation + not expressly granted above. + +Chapter 1 Overview of MySQL Connector/J + + MySQL provides connectivity for client applications developed + in the Java programming language with MySQL Connector/J, a + driver that implements the Java Database Connectivity (JDBC) + API + (http://www.oracle.com/technetwork/java/javase/jdbc/index.htm + l). + + MySQL Connector/J is a JDBC Type 4 driver. Different versions + are available that are compatible with the JDBC 3.0 and JDBC + 4.x specifications (see Chapter 2, "Connector/J Versions"). + The Type 4 designation means that the driver is a pure Java + implementation of the MySQL protocol and does not rely on the + MySQL client libraries. + + For large-scale programs that use common design patterns of + data access, consider using one of the popular persistence + frameworks such as Hibernate (http://www.hibernate.org/), + Spring's JDBC templates (http://www.springframework.org/) or + Ibatis SQL Maps (http://ibatis.apache.org/) to reduce the + amount of JDBC code for you to debug, tune, secure, and + maintain. + +Key Topics + + + * For help with connection strings, connection options, and + setting up your connection through JDBC, see Section 5.1, + "Driver/Datasource Class Names, URL Syntax and + Configuration Properties for Connector/J." + +Chapter 2 Connector/J Versions + + There are currently four versions of MySQL Connector/J + available: + + * Connector/J 5.1 is a Type 4 pure Java JDBC driver, which + conforms to the JDBC 3.0, 4.0, 4.1, and 4.2 + specifications. It provides compatibility with all the + functionality of MySQL, including 4.1, 5.0, 5.1, 5.5, + 5.6, and 5.7. Connector/J 5.1 provides ease of + development features, including auto-registration with + the Driver Manager, standardized validity checks, + categorized SQLExceptions, support for large update + counts, support for local and offset date-time variants + from the java.time package, support for JDBC-4.x XML + processing, support for per connection client + information, and support for the NCHAR + (http://dev.mysql.com/doc/refman/5.7/en/char.html), + NVARCHAR + (http://dev.mysql.com/doc/refman/5.7/en/char.html) and + NCLOB data types. This release also includes all bug + fixes up to and including Connector/J 5.0.6. + + * Connector/J 5.0 provides support for all the + functionality offered by Connector/J 3.1 and includes + distributed transaction (XA) support. + + * Connector/J 3.1 was designed for connectivity to MySQL + 4.1 and MySQL 5.0 servers and provides support for all + the functionality in MySQL 5.0 except distributed + transaction (XA) support. + + * Connector/J 3.0 provides core functionality and was + designed for connectivity to MySQL 3.x or MySQL 4.1 + servers, although it provides basic compatibility with + later versions of MySQL. Connector/J 3.0 does not support + server-side prepared statements, and does not support any + of the features in versions of MySQL later than 4.1. + + The following table summarizes the Connector/J versions + available, along with the details of JDBC driver type, what + version of the JDBC API it supports, what versions of MySQL + Server it works with, and whether it is currently supported + or not: + + Table 2.1 Summary of Connector/J Versions + Connector/J version Driver Type JDBC version MySQL Server + version Status + 5.1 4 3.0, 4.0, 4.1, 4.2 4.1, 5.0, 5.1, 5.5, 5.6, 5.7 + Recommended version + 5.0 4 3.0 4.1, 5.0 Released version + 3.1 4 3.0 4.1, 5.0 Obsolete + 3.0 4 3.0 3.x, 4.1 Obsolete + + The current recommended version for Connector/J is 5.1. This + guide covers all four connector versions, with specific notes + given where a setting applies to a specific option. + +2.1 Connector/J Release Notes and Change History + + For details of new features and bug fixes in each Connector/J + release, see the MySQL Connector/J Release Notes + (http://dev.mysql.com/doc/relnotes/connector-j/en/). + +2.2 Java Versions Supported + + The following table summarizes what version of JRE is + required to use Connector/J with Java applications, and what + version of JDK is required to build Connector/J source code: + + Table 2.2 Summary of Java Versions Required by Connector/J + Connector/J version JRE Supported JDK required (to build + source code) + 5.1 1.5.x, 1.6.x, 1.7.x, 1.8.x 1.6.x and 1.5.x + 5.0 1.3.x, 1.4.x, 1.5.x, 1.6.x 1.4.2, 1.5.x, 1.6.x + 3.1 1.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x 1.4.2, 1.5.x, 1.6.x + 3.0 1.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x 1.4.2, 1.5.x, 1.6.x + + If you are building Connector/J from source code using the + source distribution (see Section 3.4, "Installing from + Source"), you must use JDK 1.4.2 or newer to compile the + package for Connector/J 5.0 or earlier. For Connector/J 5.1, + you must have both JDK-1.6.x AND JDK-1.5.x installed to be + able to build the source code. + + JRE 1.7 support requires Connector/J 5.1.21 and higher. + + JRE 1.8 is required for Connector/J 5.1 to connect to MySQL + 5.6.27 and later and 5.7 with SSL/TLS when using some cipher + suites. + + Several JDBC 4.1 methods were implemented for the first time + in Connector/J 5.1.21. + + Because of the implementation of java.sql.Savepoint, + Connector/J 3.1.0 and newer will not run on a Java runtime + older than 1.4 unless the class verifier is turned off (by + setting the -Xverify:none option to the Java runtime). This + is because the class verifier will try to load the class + definition for java.sql.Savepoint even though it is not + accessed by the driver unless you actually use savepoint + functionality. + + Caching functionality provided by Connector/J 3.1.0 or newer + is also not available on JVMs older than 1.4.x, as it relies + on java.util.LinkedHashMap, which was first available in + JDK-1.4.0. + + MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x. + +Chapter 3 Connector/J Installation + + You can install the Connector/J package using either the + binary or source distribution. The binary distribution + provides the easiest method for installation; the source + distribution lets you customize your installation further. + With either solution, you manually add the Connector/J + location to your Java CLASSPATH. + + If you are upgrading from a previous version, read the + upgrade information in Section 3.3, "Upgrading from an Older + Version" before continuing. + + Connector/J is also available as part of the Maven project. + For more information and to download the Connector/J JAR + files, see the Maven repository + (http://search.maven.org/#search|ga|1|g%3A%22mysql%22%20AND%2 + 0a%3A%22mysql-connector-java%22). + +3.1 Installing Connector/J from a Binary Distribution + + For the easiest method of installation, use the binary + distribution of the Connector/J package. The binary + distribution is available either as a tar/gzip or zip file. + Extract it to a suitable location, then optionally make the + information about the package available by changing your + CLASSPATH (see Section 3.2, "Installing the Driver and + Configuring the CLASSPATH"). + + MySQL Connector/J is distributed as a .zip or .tar.gz archive + containing the sources, the class files, and the JAR archive + named mysql-connector-java-version-bin.jar. + + Starting with Connector/J 3.1.9, the .class files that + constitute the JAR files are only included as part of the + driver JAR file. + + Starting with Connector/J 3.1.8, the archive also includes a + debug build of the driver in a file named + mysql-connector-java-version-bin-g.jar. Do not use the debug + build of the driver unless instructed to do so when reporting + a problem or a bug, as it is not designed to be run in + production environments, and will have adverse performance + impact when used. The debug binary also depends on the + Aspect/J runtime library, which is located in the + src/lib/aspectjrt.jar file that comes with the Connector/J + distribution. + + Use the appropriate graphical or command-line utility to + extract the distribution (for example, WinZip for the .zip + archive, and tar for the .tar.gz archive). Because there are + potentially long file names in the distribution, we use the + GNU tar archive format. Use GNU tar (or an application that + understands the GNU tar archive format) to unpack the .tar.gz + variant of the distribution. + +3.2 Installing the Driver and Configuring the CLASSPATH + + Once you have extracted the distribution archive, you can + install the driver by placing + mysql-connector-java-version-bin.jar in your classpath, + either by adding the full path to it to your CLASSPATH + environment variable, or by directly specifying it with the + command line switch -cp when starting the JVM. + + To use the driver with the JDBC DriverManager, use + com.mysql.jdbc.Driver as the class that implements + java.sql.Driver. + + You can set the CLASSPATH environment variable under Unix, + Linux, or OS X either locally for a user within their + .profile, .login or other login file. You can also set it + globally by editing the global /etc/profile file. + + For example, add the Connector/J driver to your CLASSPATH + using one of the following forms, depending on your command + shell: +# Bourne-compatible shell (sh, ksh, bash, zsh): +shell> export CLASSPATH=/path/mysql-connector-java-ver-bin.jar:$CLASSP +ATH + +# C shell (csh, tcsh): +shell> setenv CLASSPATH /path/mysql-connector-java-ver-bin.jar:$CLASSP +ATH + + For Windows platforms, you set the environment variable + through the System Control Panel. + + To use MySQL Connector/J with an application server such as + GlassFish, Tomcat, or JBoss, read your vendor's documentation + for more information on how to configure third-party class + libraries, as most application servers ignore the CLASSPATH + environment variable. For configuration examples for some + J2EE application servers, see Chapter 7, "Connection Pooling + with Connector/J," Section 8.2, "Configuring Load Balancing + with Connector/J," and Section 8.4, "Advanced Load-balancing + and Failover Configuration." However, the authoritative + source for JDBC connection pool configuration information for + your particular application server is the documentation for + that application server. + + If you are developing servlets or JSPs, and your application + server is J2EE-compliant, you can put the driver's .jar file + in the WEB-INF/lib subdirectory of your webapp, as this is a + standard location for third party class libraries in J2EE web + applications. + + You can also use the MysqlDataSource or + MysqlConnectionPoolDataSource classes in the + com.mysql.jdbc.jdbc2.optional package, if your J2EE + application server supports or requires them. Starting with + Connector/J 5.0.0, the javax.sql.XADataSource interface is + implemented using the + com.mysql.jdbc.jdbc2.optional.MysqlXADataSource class, which + supports XA distributed transactions when used in combination + with MySQL server version 5.0 and later. + + The various MysqlDataSource classes support the following + parameters (through standard set mutators): + + * user + + * password + + * serverName (see the previous section about failover + hosts) + + * databaseName + + * port + +3.3 Upgrading from an Older Version + + This section has information for users who are upgrading from + one version of Connector/J to another, or to a new version of + the MySQL server that supports a more recent level of JDBC. A + newer version of Connector/J might include changes to support + new features, improve existing functionality, or comply with + new standards. + +3.3.1 Upgrading to MySQL Connector/J 5.1.x + + + * In Connector/J 5.0.x and earlier, the alias for a table + in a SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) + statement is returned when accessing the result set + metadata using ResultSetMetaData.getColumnName(). This + behavior however is not JDBC compliant, and in + Connector/J 5.1, this behavior has been changed so that + the original table name, rather than the alias, is + returned. + The JDBC-compliant behavior is designed to let API users + reconstruct the DML statement based on the metadata + within ResultSet and ResultSetMetaData. + You can get the alias for a column in a result set by + calling ResultSetMetaData.getColumnLabel(). To use the + old noncompliant behavior with + ResultSetMetaData.getColumnName(), use the + useOldAliasMetadataBehavior option and set the value to + true. + In Connector/J 5.0.x, the default value of + useOldAliasMetadataBehavior was true, but in Connector/J + 5.1 this was changed to a default value of false. + +3.3.2 JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or +Newer + + + * Using the UTF-8 Character Encoding - Prior to MySQL + server version 4.1, the UTF-8 character encoding was not + supported by the server, however the JDBC driver could + use it, allowing storage of multiple character sets in + latin1 tables on the server. + Starting with MySQL-4.1, this functionality is + deprecated. If you have applications that rely on this + functionality, and can not upgrade them to use the + official Unicode character support in MySQL server + version 4.1 or newer, add the following property to your + connection URL: + useOldUTF8Behavior=true + + * Server-side Prepared Statements - Connector/J 3.1 will + automatically detect and use server-side prepared + statements when they are available (MySQL server version + 4.1.0 and newer). If your application encounters issues + with server-side prepared statements, you can revert to + the older client-side emulated prepared statement code + that is still presently used for MySQL servers older than + 4.1.0 with the following connection property: + useServerPrepStmts=false + +3.3.3 Upgrading from MySQL Connector/J 3.0 to 3.1 + + Connector/J 3.1 is designed to be backward-compatible with + Connector/J 3.0 as much as possible. Major changes are + isolated to new functionality exposed in MySQL-4.1 and newer, + which includes Unicode character sets, server-side prepared + statements, SQLState codes returned in error messages by the + server and various performance enhancements that can be + enabled or disabled using configuration properties. + + * Unicode Character Sets: See the next section, as well as + Character Set Support + (http://dev.mysql.com/doc/refman/5.7/en/charset.html), + for information on this MySQL feature. If you have + something misconfigured, it will usually show up as an + error with a message similar to Illegal mix of + collations. + + * Server-side Prepared Statements: Connector/J 3.1 will + automatically detect and use server-side prepared + statements when they are available (MySQL server version + 4.1.0 and newer). + Starting with version 3.1.7, the driver scans SQL you are + preparing using all variants of + Connection.prepareStatement() to determine if it is a + supported type of statement to prepare on the server + side, and if it is not supported by the server, it + instead prepares it as a client-side emulated prepared + statement. You can disable this feature by passing + emulateUnsupportedPstmts=false in your JDBC URL. + If your application encounters issues with server-side + prepared statements, you can revert to the older + client-side emulated prepared statement code that is + still presently used for MySQL servers older than 4.1.0 + with the connection property useServerPrepStmts=false. + + * Datetimes with all-zero components (0000-00-00 ...): + These values cannot be represented reliably in Java. + Connector/J 3.0.x always converted them to NULL when + being read from a ResultSet. + Connector/J 3.1 throws an exception by default when these + values are encountered, as this is the most correct + behavior according to the JDBC and SQL standards. This + behavior can be modified using the zeroDateTimeBehavior + configuration property. The permissible values are: + + + exception (the default), which throws an + SQLException with an SQLState of S1009. + + + convertToNull, which returns NULL instead of the + date. + + + round, which rounds the date to the nearest closest + value which is 0001-01-01. + Starting with Connector/J 3.1.7, ResultSet.getString() + can be decoupled from this behavior using + noDatetimeStringSync=true (the default value is false) so + that you can retrieve the unaltered all-zero value as a + String. Note that this also precludes using any time zone + conversions, therefore the driver will not allow you to + enable noDatetimeStringSync and useTimezone at the same + time. + + * New SQLState Codes: Connector/J 3.1 uses SQL:1999 + SQLState codes returned by the MySQL server (if + supported), which are different from the legacy X/Open + state codes that Connector/J 3.0 uses. If connected to a + MySQL server older than MySQL-4.1.0 (the oldest version + to return SQLStates as part of the error code), the + driver will use a built-in mapping. You can revert to the + old mapping by using the configuration property + useSqlStateCodes=false. + + * ResultSet.getString(): Calling ResultSet.getString() on a + BLOB (http://dev.mysql.com/doc/refman/5.7/en/blob.html) + column will now return the address of the byte[] array + that represents it, instead of a String representation of + the BLOB + (http://dev.mysql.com/doc/refman/5.7/en/blob.html). BLOB + (http://dev.mysql.com/doc/refman/5.7/en/blob.html) values + have no character set, so they cannot be converted to + java.lang.Strings without data loss or corruption. + To store strings in MySQL with LOB behavior, use one of + the TEXT + (http://dev.mysql.com/doc/refman/5.7/en/blob.html) types, + which the driver will treat as a java.sql.Clob. + + * Debug builds: Starting with Connector/J 3.1.8 a debug + build of the driver in a file named + mysql-connector-java-version-bin-g.jar is shipped + alongside the normal binary jar file that is named + mysql-connector-java-version-bin.jar. + Starting with Connector/J 3.1.9, we do not ship the + .class files unbundled, they are only available in the + JAR archives that ship with the driver. + Do not use the debug build of the driver unless + instructed to do so when reporting a problem or bug, as + it is not designed to be run in production environments, + and will have adverse performance impact when used. The + debug binary also depends on the Aspect/J runtime + library, which is located in the src/lib/aspectjrt.jar + file that comes with the Connector/J distribution. + +3.4 Installing from Source + + Caution + + To just get MySQL Connector/J up and running on your system, + install Connector/J using a standard binary release + distribution. Instructions in this section is only for users + who, for various reasons, want to compile Connector/J from + source. + + The requirements and steps for installing from source + Connector/J 5.1.37 or later, 5.1.34 to 5.1.36, and 5.1.33 or + earlier are different; check the section below that is + relevant for the version you want. + + Installing Connector/J 5.1.37 or later from source. To + install MySQL Connector/J from its source tree on GitHub, you + need to have the following software on your system: + + * A Git client, to check out the sources from our GitHub + repository (available from http://git-scm.com/downloads). + + * Apache Ant version 1.8.2 or newer (available from + http://ant.apache.org/). + + * JDK 1.8.x AND JDK 1.5.x. + + * JRE 1.6.x (optional) + + * JUnit libraries (available from + https://github.com/junit-team/junit/wiki/Download-and-Ins + tall). + + * The required .jar files from the Hibernate ORM 4.1 or 4.2 + Final release bundle, which is available at + http://sourceforge.net/projects/hibernate/files/hibernate + 4/. + + To check out and compile MySQL Connector/J, follow these + steps: + + 1. Check out the code from the source code repository for + MySQL Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j; for the + latest release of the Connector/J 5.1 series, use the + following command: +shell> git clone https://github.com/mysql/mysql-connector-j.git + + To check out a release other than the latest one, use the + --branch option to specify the revision tag for it: +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connec +tor-j.git + + Under the current directory, the commands create a + mysql-connector-j subdirectory , which contains the code + you want. + + 2. Make sure that you have both JDK 1.8.x AND JDK 1.5.x + installed. You need both JDKs because besides supporting + JDBC from 4.0 to 4.2, Connector/J 5.1 also supports JDBC + 3.0, which is an older version and requires the older JDK + 1.5.x. + + 3. Consider also having JRE 1.6.x installed. This is + optional: if JRE 1.6.x is not available or not supplied + to Ant with the property com.mysql.jdbc.java6.rtjar, the + Java 8 bootstrap classes will be used. A warning will be + returned, saying that the bootstrap class path was not + set with the option to compile sources written for Java + 6. + + 4. Place the required junit.jar file in a separate + directory---for example, /home/username/ant-extralibs. + + 5. In the same directory for extra libraries described in + the last step, create a directory named hibernate4, and + put under it all the .jar files you can find under the + /lib/required/ folder in the Hibernate ORM 4 Final + release bundle. + + 6. Change your current working directory to the + mysql-connector-j directory created in step 1 above. + + 7. In the directory, create a file named build.properties to + indicate to Ant the locations of the root directories for + your JDK 1.8.x and JDK 1.5.x installations, the location + of the rt.jar of your JRE 1.6.x (optional), and the + location of the extra libraries. The file should contain + the following property settings, with the "path_to_*" + parts replaced by the appropriate filepaths: +com.mysql.jdbc.jdk8=path_to_jdk_1.8 +com.mysql.jdbc.jdk5=path_to_jdk_1.5 +com.mysql.jdbc.java6.rtjar=path_to_rt.jar_under_jre_1.6/rt.jar +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries + + Alternatively, you can set the values of those properties + through the Ant -D options. + + 8. Issue the following command to compile the driver and + create a .jar file for Connector/J: +shell> ant dist + + This creates a build directory in the current directory, + where all the build output goes. A directory is created + under the build directory, whose name includes the + version number of the release you are building. That + directory contains the sources, the compiled .class + files, and a .jar file for deployment. For more + information and other possible targets, including those + that create a fully packaged distribution, issue the + following command: +shell> ant -projecthelp + + + 9. Install the newly created .jar file for the JDBC driver + as you would install a binary .jar file you download from + MySQL by following the instructions given in Section 3.2, + "Installing the Driver and Configuring the CLASSPATH." + + Note that a package containing both the binary and source + code for Connector/J 5.1 can also be found at Connector/J 5.1 + Download + (http://dev.mysql.com/downloads/connector/j/5.1.html). + + Installing Connector/J 5.1.34 to 5.1.36 from source. To + install MySQL Connector/J 5.1.34 to 5.1.36 from the + Connector/J source tree on GitHub, make sure that you have + the following software on your system: + + * A Git client, to check out the sources from our GitHub + repository (available from http://git-scm.com/downloads). + + * Apache Ant version 1.8.2 or newer (available from + http://ant.apache.org/). + + * JDK 1.6.x AND JDK 1.5.x. + + * JUnit libraries (available from + https://github.com/junit-team/junit/wiki/Download-and-Ins + tall). + + * The required .jar files from the Hibernate ORM 4.1 or 4.2 + Final release bundle, which is available at + http://sourceforge.net/projects/hibernate/files/hibernate + 4/. + + To check out and compile MySQL Connector/J, follow these + steps: + + 1. Check out the code from the source code repository for + MySQL Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j, using the + --branch option to specify the revision tag for release + 5.1.xx: +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connec +tor-j.git + + Under the current directory, the commands create a + mysql-connector-j subdirectory , which contains the code + you want. + + 2. Make sure that you have both JDK 1.6.x AND JDK 1.5.x + installed. You need both JDKs because Connector/J 5.1 + supports both JDBC 3.0 (which has existed prior to JDK + 1.6.x) and JDBC 4.0. + + 3. Place the required junit.jar file in a separate + directory---for example, /home/username/ant-extralibs. + + 4. In the same directory for extra libraries described in + the last step, create a directory named hibernate4, and + put under it all the .jar files you can find under the + /lib/required/ folder in the Hibernate ORM 4 Final + release bundle. + + 5. Change your current working directory to the + mysql-connector-j directory created in step 1 above. + + 6. In the directory, create a file named build.properties to + indicate to Ant the locations of the root directories for + your JDK 1.5.x and JDK 1.6.x installations, as well as + the location of the extra libraries. The file should + contain the following property settings, with the + "path_to_*" parts replaced by the appropriate filepaths: +com.mysql.jdbc.jdk5=path_to_jdk_1.5 +com.mysql.jdbc.jdk6=path_to_jdk_1.6 +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries + + Alternatively, you can set the values of those properties + through the Ant -D options. + + 7. Issue the following command to compile the driver and + create a .jar file for Connector/J: +shell> ant dist + + This creates a build directory in the current directory, + where all the build output goes. A directory is created + under the build directory, whose name includes the + version number of the release you are building. That + directory contains the sources, the compiled .class + files, and a .jar file for deployment. For more + information and other possible targets, including those + that create a fully packaged distribution, issue the + following command: +shell> ant -projecthelp + + + 8. Install the newly created .jar file for the JDBC driver + as you would install a binary .jar file you download from + MySQL by following the instructions given in Section 3.2, + "Installing the Driver and Configuring the CLASSPATH." + + Installing Connector/J 5.1.33 or earlier from the source + tree. To install MySQL Connector/J 5.1.33 or earlier from + the Connector/J source tree on GitHub, make sure that you + have the following software on your system: + + * A Git client, to check out the source code from our + GitHub repository (available from + http://git-scm.com/downloads). + + * Apache Ant version 1.7 or newer (available from + http://ant.apache.org/). + + * JDK 1.6.x AND JDK 1.5.x. Refer to Section 2.2, "Java + Versions Supported" for the version of Java you need to + build or run any Connector/J release. + + * The Ant Contrib (version 1.03b is available from + http://sourceforge.net/projects/ant-contrib/files/ant-con + trib/1.0b3/) and JUnit (available from + https://github.com/junit-team/junit/wiki/Download-and-Ins + tall) libraries. + + + * The required .jar files from the Hibernate ORM 4.1 or 4.2 + Final release bundle, which is available at + http://sourceforge.net/projects/hibernate/files/hibernate + 4/. + + To check out and compile a specific branch of MySQL + Connector/J, follow these steps: + + 1. Check out the code from the source code repository for + MySQL Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j, using the + --branch option to specify the revision tag for release + 5.1.xx: +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connec +tor-j.git + + Under the current directory, the commands create a + mysql-connector-j subdirectory , which contains the code + you want. + + 2. To build Connector/J 5.1, make sure that you have both + JDK 1.6.x AND JDK 1.5.x installed. You need both JDKs + because Connector/J 5.1 supports both JDBC 3.0 (which has + existed prior to JDK 1.6.x) and JDBC 4.0. Set your + JAVA_HOME environment variable to the path to the JDK + 1.5.x installation. + + 3. Place the required ant-contrib.jar file (in exactly that + name, without the version number in it; rename the jar + file if needed) and junit.jar file in a separate + directory---for example, /home/username/ant-extralibs. + + 4. In the same directory for extra libraries described in + the last step, create a directory named hibernate4, and + put under it all the .jar files you can find under the + /lib/required/ folder in the Hibernate ORM 4 Final + release bundle. + + 5. Change your current working directory to the + mysql-connector-j directory created in step 1 above. + + 6. In the directory, create a file named build.properties to + indicate to Ant the locations of the Javac and rt.jar of + your JDK 1.6.x, as well as the location of the extra + libraries. The file should contain the following property + settings, with the "path_to_*" parts replaced by the + appropriate filepaths: +com.mysql.jdbc.java6.javac=path_to_javac_1.6/javac +com.mysql.jdbc.java6.rtjar=path_to_rt.jar_under_jdk_1.6/rt.jar +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries + + Alternatively, you can set the values of those properties + through the Ant -D options. + + 7. Issue the following command to compile the driver and + create a .jar file for Connector/J: +shell> ant dist + + This creates a build directory in the current directory, + where all the build output goes. A directory is created + under the build directory, whose name includes the + version number of the release you are building. That + directory contains the sources, the compiled .class + files, and a .jar file for deployment. For more + information and other possible targets, including those + that create a fully packaged distribution, issue the + following command: +shell> ant -projecthelp + + + 8. Install the newly created .jar file for the JDBC driver + as you would install a binary .jar file you download from + MySQL by following the instructions given in Section 3.2, + "Installing the Driver and Configuring the CLASSPATH." + +3.5 Testing Connector/J + + The Connector/J source code repository or packages that are + shipped with source code include an extensive test suite, + containing test cases that can be executed independently. The + test cases are divided into the following categories: + + * Functional or unit tests: Classes from the package + testsuite.simple. Include test code for the main features + of the Connector/J. + + * Performance tests: Classes from the package + testsuite.perf. Include test code to make measurements + for the performance of Connector/J. + + * Fabric tests: Classes from the package testsuite.fabric. + Includes the code to test Fabric-specific features. These + tests require the setting of some special properties that + are not documented here. Consult the code or the + Fabric-related targets in the bundled Ant build file, + build.xml. + + * Regression tests: Classes from the package + testsuite.regression. Includes code for testing bug and + regression fixes. + + The bundled Ant build file contains targets like test and + test-multijvm, which can facilitate the process of running + the Connector/J tests; see the target descriptions in the + build file for details. Besides the requirements for building + Connector/J from the source code described in Section 3.4, + "Installing from Source," a number of the tests also require + the File System Service Provider 1.2 for the Java Naming and + Directory Interface (JNDI), available at + http://www.oracle.com/technetwork/java/javasebusiness/downloa + ds/java-archive-downloads-java-plat-419418.html)---place the + jar files downloaded from there into the lib directory or in + the directory pointed to by the property + com.mysql.jdbc.extra.libs. + + To run the test using Ant, in addition to the properties + required for Section 3.4, "Installing from Source," you must + set the following properties in the build.properties file or + through the Ant -D options: + + * com.mysql.jdbc.testsuite.url: it specifies the JDBC URL + for connection to a MySQL test server; see Section 5.1, + "Driver/Datasource Class Names, URL Syntax and + Configuration Properties for Connector/J." + + * com.mysql.jdbc.testsuite.jvm: the JVM to be used for the + tests. If the property is set, the specified JVM will be + used for all test cases except if it points to a Java 5 + directory, in which case any test cases for JDBC 4.0 and + later are run with the JVM supplied with the property + com.mysql.jdbc.jdk8 (for 5.1.36 and earlier, supplied + with the property com.mysql.jdbc.jdk6). If the property + is not set, the JVM supplied with com.mysql.jdbc.jdk5 + will be used to run test cases for JDBC 3.0 and the one + supplied with com.mysql.jdbc.jdk8 (for 5.1.36 and + earlier, supplied with the property com.mysql.jdbc.jdk6) + will be used to run test cases for JDBC 4.0 and later. + + After setting these parameters, run the tests with Ant in the + following ways: + + * Building the test target with ant test runs all test + cases by default on a single server instance. If you want + to run a particular test case, put the test's fully + qualified class names in the test variable; for example: +shell > ant -Dtest=testsuite.simple.StringUtilsTest test + You can also run individual tests in a test case by + specifying the names of the corresponding methods in the + methods variable, separating multiple methods by commas; + for example: +shell > ant -Dtest=testsuite.simple.StringUtilsTest -Dmethods=testInde +xOfIgnoreCase,testGetBytes test + + + * Building the test-multijvm target with ant test-multijvm + runs all the test cases using multiple JVMs of different + versions on multiple server instances. For example, if + you want to run the tests using a Java 7 and a Java 8 JVM + on three server instances with different configurations, + you will need to use the following properties: +com.mysql.jdbc.testsuite.jvm.1=path_to_Java_7 +com.mysql.jdbc.testsuite.jvm.2=path_to_Java_8 +com.mysql.jdbc.testsuite.url.1=URL_to_1st_server +com.mysql.jdbc.testsuite.url.2=URL_to_2nd_server +com.mysql.jdbc.testsuite.url.3=URL_to_3rd_server + Unlike the target test, the target test-multijvm only + recognizes the properties com.mysql.jdbc.testsuite.jvm.N + and com.mysql.jdbc.testsuite.url.N, where N is a numeric + suffice; the same properties without the suffices are + ignored by test-multijvm. As with the target test, if any + of the com.mysql.jdbc.testsuite.jvm.N settings points to + Java 5, then Ant relies on the property + com.mysql.jdbc.jdk8 to run the tests specific to JDBC 4.0 + and later. + You can choose to run individual test cases or specific + tests by using the test or methods property, as explained + in the last bullet for the target test. Each test is run + once per possible combination of JVMs and server + instances (that is, 6 times in total for in this + example). + When a test for a certain JVM-server combination has + failed, test-multijvm does not throw an error, but moves + on to the next combination, until all tests for all + combinations are finished. + + While the test results are partially reported by the console, + complete reports in HTML and XML formats are provided: + + * For results of test: view the HTML report by opening + build/junit/unitregress/report/index.html. XML version of + the reports are located in the folder + build/junit/unitregress. + + * For results of test-multijvm: view the HTML report for + each JVM-server combination by opening + build/junit/MySQLN.server_version/operating_system_versio + n/jvm-version/unitregress/report/index.html. XML version + of the reports are located in the folder + build/junit/MySQLN.server_version/operating_system_versio + n/jvm-version/unitregress. + +Chapter 4 Connector/J Examples + + Examples of using Connector/J are located throughout this + document. This section provides a summary and links to these + examples. + + * Example 6.1, "Connector/J: Obtaining a connection from + the DriverManager" + + * Example 6.2, "Connector/J: Using java.sql.Statement to + execute a SELECT query" + + * Example 6.3, "Connector/J: Calling Stored Procedures" + + * Example 6.3, "Connector/J: Using + Connection.prepareCall()" + + * Example 6.3, "Connector/J: Registering output parameters" + + * Example 6.3, "Connector/J: Setting CallableStatement + input parameters" + + * Example 6.3, "Connector/J: Retrieving results and output + parameter values" + + * Example 6.4, "Connector/J: Retrieving AUTO_INCREMENT + column values using Statement.getGeneratedKeys()" + + * Example 6.4, "Connector/J: Retrieving AUTO_INCREMENT + column values using SELECT LAST_INSERT_ID()" + + * Example 6.4, "Connector/J: Retrieving AUTO_INCREMENT + column values in Updatable ResultSets" + + * Example 7, "Connector/J: Using a connection pool with a + J2EE application server" + + * Example 15, "Connector/J: Example of transaction with + retry logic" + +Chapter 5 Connector/J (JDBC) Reference + + This section of the manual contains reference material for + MySQL Connector/J. + +5.1 Driver/Datasource Class Names, URL Syntax and Configuration +Properties for Connector/J + + The name of the class that implements java.sql.Driver in + MySQL Connector/J is com.mysql.jdbc.Driver. The + org.gjt.mm.mysql.Driver class name is also usable for + backward compatibility with MM.MySQL, the predecessor of + Connector/J. Use this class name when registering the driver, + or when configuring a software to use MySQL Connector/J. + +JDBC URL Format + + The general format for a JDBC URL for connecting to a MySQL + server is as follows, with items in square brackets ([ ]) + being optional: +jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]] ?? +[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + + Here is a simple example for a connection URL: +jdbc:mysql://localhost:3306/sakila?profileSQL=true + + Supply multiple hosts for a server failover setup (see + Chapter 8, "Multi-Host Connections" for details): +# Connection URL for a server failover setup: +jdbc:mysql//primaryhost,secondaryhost1,secondaryhost2/test + + There are specialized URL schemes for configuring + Connector/J's multi-host functions like load balancing and + replication; here are some examples (see Chapter 8, + "Multi-Host Connections" for details): +# Connection URL for load balancing: +jdbc:mysql:loadbalance://localhost:3306,localhost:3310/sakila + +# Connection URL for server replication: +jdbc:mysql:replication://master,slave1,slave2,slave3/test + +Host and Port + + If no hosts are not specified, the host name defaults to + 127.0.0.1. If the port for a host is not specified, it + defaults to 3306, the default port number for MySQL servers. + +Initial Database for Connection + + If the database is not specified, the connection is made with + no default database. In this case, either call the + setCatalog() method on the Connection instance, or fully + specify table names using the database name (that is, SELECT + dbname.tablename.colname FROM dbname.tablename...) in your + SQL. Opening a connection without specifying the database to + use is generally only useful when building tools that work + with multiple databases, such as GUI database managers. + Note + + Always use the Connection.setCatalog() method to specify the + desired database in JDBC applications, rather than the USE + database statement. + +IPv6 Connections + + For IPv6 connections, use this alternative syntax to specify + hosts in the URL (the same syntax can also be used for IPv4 + connections): +jdbc:mysql://address=(key1=value)[(key2=value)]...[,address=(key3=valu +e)[(key4=value)]...]...[/[database]]?? +[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + + Supported keys include: + + * (protocol=tcp), or (protocol=pipe) for named pipes on + Windows. + + * (path=path_to_pipe) for named pipes. + + * (host=hostname) for TCP connections. + + * (port=port_number) for TCP connections. + + For example: +jdbc:mysql://address=(protocol=tcp)(host=localhost)(port=3306)/db + + Keys other than the four mentioned above are treated as + host-specific configuration properties, which allow per-host + overrides of any configuration property set for multi-host + connections (that is, when using failover, load balancing, or + replication). For example: +# IPv6 Connection URL for a server failover setup: +jdbc:mysql//address=(protocol=tcp)(host=primaryhost)(port=3306),?? +address=(protocol=tcp)(host=secondaryhost1)(port=3310)(user=test2)/tes +t + +# IPv6 Connection URL for load balancing: +jdbc:mysql:loadbalance://address=(protocol=tcp)(host=localhost)(port=3 +306)(user=test1),?? +address=(protocol=tcp)(host=localhost)(port=3310)(user=test2)/sakila + +# IPv6 Connection URL for server replication: +jdbc:mysql:replication://address=(protocol=tcp)(host=master)(port=3306 +)(user=test1),?? +address=(protocol=tcp)(host=slave1)(port=3310)(user=test2)/test + + Limit the overrides to user, password, network timeouts, and + statement and metadata cache sizes; the effects of other + per-host overrides are not defined. + + The ways to set the other configuration properties are the + same for IPv6 and IPv4 URLs; see Section 5.1, "." + +Setting Configuration Properties + + Configuration properties define how Connector/J will make a + connection to a MySQL server. Unless otherwise noted, + properties can be set for a DataSource object or for a + Connection object. + + Configuration properties can be set in one of the following + ways: + + * Using the set*() methods on MySQL implementations of + java.sql.DataSource (which is the preferred method when + using implementations of java.sql.DataSource): + + + com.mysql.jdbc.jdbc2.optional.MysqlDataSource + + + com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDat + aSource + + * As a key/value pair in the java.util.Properties instance + passed to DriverManager.getConnection() or + Driver.connect() + + * As a JDBC URL parameter in the URL given to + java.sql.DriverManager.getConnection(), + java.sql.Driver.connect() or the MySQL implementations of + the javax.sql.DataSource setURL() method. If you specify + a configuration property in the URL without providing a + value for it, nothing will be set; for example, adding + useServerPrepStmts alone to the URL does not make + Connector/J use server-side prepared statements; you need + to add useServerPrepStmts=true. + Note + If the mechanism you use to configure a JDBC URL is + XML-based, use the XML character literal & to + separate configuration parameters, as the ampersand is a + reserved character for XML. + + The properties are listed in the following tables. + + Connection/Authentication. + Properties and Descriptions + + user + + The user to connect as + + Since version: all versions + + password + + The password to use when connecting + + Since version: all versions + + socketFactory + + The name of the class that the driver should use for creating + socket connections to the server. This class must implement + the interface 'com.mysql.jdbc.SocketFactory' and have public + no-args constructor. + + Default: com.mysql.jdbc.StandardSocketFactory + + Since version: 3.0.3 + + connectTimeout + + Timeout for socket connect (in milliseconds), with 0 being no + timeout. Only works on JDK-1.4 or newer. Defaults to '0'. + + Default: 0 + + Since version: 3.0.1 + + socketTimeout + + Timeout on network socket operations (0, the default means no + timeout). + + Default: 0 + + Since version: 3.0.1 + + connectionLifecycleInterceptors + + A comma-delimited list of classes that implement + "com.mysql.jdbc.ConnectionLifecycleInterceptor" that should + notified of connection lifecycle events (creation, + destruction, commit, rollback, setCatalog and setAutoCommit) + and potentially alter the execution of these commands. + ConnectionLifecycleInterceptors are "stackable", more than + one interceptor may be specified via the configuration + property as a comma-delimited list, with the interceptors + executed in order from left to right. + + Since version: 5.1.4 + + useConfigs + + Load the comma-delimited list of configuration properties + before parsing the URL or applying user-specified properties. + These configurations are explained in the 'Configurations' of + the documentation. + + Since version: 3.1.5 + + authenticationPlugins + + Comma-delimited list of classes that implement + com.mysql.jdbc.AuthenticationPlugin and which will be used + for authentication unless disabled by + "disabledAuthenticationPlugins" property. + + Since version: 5.1.19 + + defaultAuthenticationPlugin + + Name of a class implementing + com.mysql.jdbc.AuthenticationPlugin which will be used as the + default authentication plugin (see below). It is an error to + use a class which is not listed in "authenticationPlugins" + nor it is one of the built-in plugins. It is an error to set + as default a plugin which was disabled with + "disabledAuthenticationPlugins" property. It is an error to + set this value to null or the empty string (i.e. there must + be at least a valid default authentication plugin specified + for the connection, meeting all constraints listed above). + + Default: + com.mysql.jdbc.authentication.MysqlNativePasswordPlugin + + Since version: 5.1.19 + + disabledAuthenticationPlugins + + Comma-delimited list of classes implementing + com.mysql.jdbc.AuthenticationPlugin or mechanisms, i.e. + "mysql_native_password". The authentication plugins or + mechanisms listed will not be used for authentication which + will fail if it requires one of them. It is an error to + disable the default authentication plugin (either the one + named by "defaultAuthenticationPlugin" property or the + hard-coded one if "defaultAuthenticationPlugin" property is + not set). + + Since version: 5.1.19 + + disconnectOnExpiredPasswords + + If "disconnectOnExpiredPasswords" is set to "false" and + password is expired then server enters "sandbox" mode and + sends ERR(08001, ER_MUST_CHANGE_PASSWORD) for all commands + that are not needed to set a new password until a new + password is set. + + Default: true + + Since version: 5.1.23 + + interactiveClient + + Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout + connections based on INTERACTIVE_TIMEOUT instead of + WAIT_TIMEOUT + + Default: false + + Since version: 3.1.0 + + localSocketAddress + + Hostname or IP address given to explicitly configure the + interface that the driver will bind the client side of the + TCP/IP connection to when connecting. + + Since version: 5.0.5 + + propertiesTransform + + An implementation of + com.mysql.jdbc.ConnectionPropertiesTransform that the driver + will use to modify URL properties passed to the driver before + attempting a connection + + Since version: 3.1.4 + + useCompression + + Use zlib compression when communicating with the server + (true/false)? Defaults to 'false'. + + Default: false + + Since version: 3.0.17 + + Networking. + Properties and Descriptions + + socksProxyHost + + Name or IP address of SOCKS host to connect through. + + Since version: 5.1.34 + + socksProxyPort + + Port of SOCKS server. + + Default: 1080 + + Since version: 5.1.34 + + maxAllowedPacket + + Maximum allowed packet size to send to server. If not set, + the value of system variable 'max_allowed_packet' will be + used to initialize this upon connecting. This value will not + take effect if set larger than the value of + 'max_allowed_packet'. Also, due to an internal dependency + with the property "blobSendChunkSize", this setting has a + minimum value of "8203" if "useServerPrepStmts" is set to + "true". + + Default: -1 + + Since version: 5.1.8 + + tcpKeepAlive + + If connecting using TCP/IP, should the driver set + SO_KEEPALIVE? + + Default: true + + Since version: 5.0.7 + + tcpNoDelay + + If connecting using TCP/IP, should the driver set + SO_TCP_NODELAY (disabling the Nagle Algorithm)? + + Default: true + + Since version: 5.0.7 + + tcpRcvBuf + + If connecting using TCP/IP, should the driver set SO_RCV_BUF + to the given value? The default value of '0', means use the + platform default value for this property) + + Default: 0 + + Since version: 5.0.7 + + tcpSndBuf + + If connecting using TCP/IP, should the driver set SO_SND_BUF + to the given value? The default value of '0', means use the + platform default value for this property) + + Default: 0 + + Since version: 5.0.7 + + tcpTrafficClass + + If connecting using TCP/IP, should the driver set traffic + class or type-of-service fields ?See the documentation for + java.net.Socket.setTrafficClass() for more information. + + Default: 0 + + Since version: 5.0.7 + + High Availability and Clustering. + Properties and Descriptions + + autoReconnect + + Should the driver try to re-establish stale and/or dead + connections? If enabled the driver will throw an exception + for a queries issued on a stale or dead connection, which + belong to the current transaction, but will attempt reconnect + before the next query issued on the connection in a new + transaction. The use of this feature is not recommended, + because it has side effects related to session state and data + consistency when applications don't handle SQLExceptions + properly, and is only designed to be used when you are unable + to configure your application to handle SQLExceptions + resulting from dead and stale connections properly. + Alternatively, as a last option, investigate setting the + MySQL server variable "wait_timeout" to a high value, rather + than the default of 8 hours. + + Default: false + + Since version: 1.1 + + autoReconnectForPools + + Use a reconnection strategy appropriate for connection pools + (defaults to 'false') + + Default: false + + Since version: 3.1.3 + + failOverReadOnly + + When failing over in autoReconnect mode, should the + connection be set to 'read-only'? + + Default: true + + Since version: 3.0.12 + + maxReconnects + + Maximum number of reconnects to attempt if autoReconnect is + true, default is '3'. + + Default: 3 + + Since version: 1.1 + + reconnectAtTxEnd + + If autoReconnect is set to true, should the driver attempt + reconnections at the end of every transaction? + + Default: false + + Since version: 3.0.10 + + retriesAllDown + + When using loadbalancing or failover, the number of times the + driver should cycle through available hosts, attempting to + connect. Between cycles, the driver will pause for 250ms if + no servers are available. + + Default: 120 + + Since version: 5.1.6 + + initialTimeout + + If autoReconnect is enabled, the initial time to wait between + re-connect attempts (in seconds, defaults to '2'). + + Default: 2 + + Since version: 1.1 + + roundRobinLoadBalance + + When autoReconnect is enabled, and failoverReadonly is false, + should we pick hosts to connect to on a round-robin basis? + + Default: false + + Since version: 3.1.2 + + queriesBeforeRetryMaster + + Number of queries to issue before falling back to the primary + host when failed over (when using multi-host failover). + Whichever condition is met first, 'queriesBeforeRetryMaster' + or 'secondsBeforeRetryMaster' will cause an attempt to be + made to reconnect to the primary host. Setting both + properties to 0 disables the automatic fall back to the + primary host at transaction boundaries. Defaults to 50. + + Default: 50 + + Since version: 3.0.2 + + secondsBeforeRetryMaster + + How long should the driver wait, when failed over, before + attempting to reconnect to the primary host? Whichever + condition is met first, 'queriesBeforeRetryMaster' or + 'secondsBeforeRetryMaster' will cause an attempt to be made + to reconnect to the master. Setting both properties to 0 + disables the automatic fall back to the primary host at + transaction boundaries. Time in seconds, defaults to 30 + + Default: 30 + + Since version: 3.0.2 + + allowMasterDownConnections + + By default, a replication-aware connection will fail to + connect when configured master hosts are all unavailable at + initial connection. Setting this property to 'true' allows to + establish the initial connection, by failing over to the + slave servers, in read-only state. It won't prevent + subsequent failures when switching back to the master hosts + i.e. by setting the replication connection to read/write + state. + + Default: false + + Since version: 5.1.27 + + allowSlaveDownConnections + + By default, a replication-aware connection will fail to + connect when configured slave hosts are all unavailable at + initial connection. Setting this property to 'true' allows to + establish the initial connection. It won't prevent failures + when switching to slaves i.e. by setting the replication + connection to read-only state. The property + 'readFromMasterWhenNoSlaves' should be used for this purpose. + + Default: false + + Since version: 5.1.38 + + readFromMasterWhenNoSlaves + + Replication-aware connections distribute load by using the + master hosts when in read/write state and by using the slave + hosts when in read-only state. If, when setting the + connection to read-only state, none of the slave hosts are + available, an SQLExeception is thrown back. Setting this + property to 'true' allows to fail over to the master hosts, + while setting the connection state to read-only, when no + slave hosts are available at switch instant. + + Default: false + + Since version: 5.1.38 + + replicationEnableJMX + + Enables JMX-based management of load-balanced connection + groups, including live addition/removal of hosts from + load-balancing pool. + + Default: false + + Since version: 5.1.27 + + selfDestructOnPingMaxOperations + + =If set to a non-zero value, the driver will report close the + connection and report failure when Connection.ping() or + Connection.isValid(int) is called if the connection's count + of commands sent to the server exceeds this value. + + Default: 0 + + Since version: 5.1.6 + + selfDestructOnPingSecondsLifetime + + If set to a non-zero value, the driver will report close the + connection and report failure when Connection.ping() or + Connection.isValid(int) is called if the connection's + lifetime exceeds this value. + + Default: 0 + + Since version: 5.1.6 + + resourceId + + A globally unique name that identifies the resource that this + datasource or connection is connected to, used for + XAResource.isSameRM() when the driver can't determine this + value based on hostnames used in the URL + + Since version: 5.0.1 + + Security. + Properties and Descriptions + + allowMultiQueries + + Allow the use of ';' to delimit multiple queries during one + statement (true/false), defaults to 'false', and does not + affect the addBatch() and executeBatch() methods, which + instead rely on rewriteBatchStatements. + + Default: false + + Since version: 3.1.1 + + useSSL + + Use SSL when communicating with the server (true/false), + default is 'true' when connecting to MySQL 5.5.45+, 5.6.26+ + or 5.7.6+, otherwise default is 'false' + + Default: false + + Since version: 3.0.2 + + requireSSL + + Require server support of SSL connection if useSSL=true? + (defaults to 'false'). + + Default: false + + Since version: 3.1.0 + + verifyServerCertificate + + If "useSSL" is set to "true", should the driver verify the + server's certificate? When using this feature, the keystore + parameters should be specified by the + "clientCertificateKeyStore*" properties, rather than system + properties. Default is 'false' when connecting to MySQL + 5.5.45+, 5.6.26+ or 5.7.6+ and "useSSL" was not explicitly + set to "true". Otherwise default is 'true' + + Default: true + + Since version: 5.1.6 + + clientCertificateKeyStoreUrl + + URL to the client certificate KeyStore (if not specified, use + defaults) + + Since version: 5.1.0 + + clientCertificateKeyStoreType + + KeyStore type for client certificates (NULL or empty means + use the default, which is "JKS". Standard keystore types + supported by the JVM are "JKS" and "PKCS12", your environment + may have more available depending on what security products + are installed and available to the JVM. + + Default: JKS + + Since version: 5.1.0 + + clientCertificateKeyStorePassword + + Password for the client certificates KeyStore + + Since version: 5.1.0 + + trustCertificateKeyStoreUrl + + URL to the trusted root certificate KeyStore (if not + specified, use defaults) + + Since version: 5.1.0 + + trustCertificateKeyStoreType + + KeyStore type for trusted root certificates (NULL or empty + means use the default, which is "JKS". Standard keystore + types supported by the JVM are "JKS" and "PKCS12", your + environment may have more available depending on what + security products are installed and available to the JVM. + + Default: JKS + + Since version: 5.1.0 + + trustCertificateKeyStorePassword + + Password for the trusted root certificates KeyStore + + Since version: 5.1.0 + + enabledSSLCipherSuites + + If "useSSL" is set to "true", overrides the cipher suites + enabled for use on the underlying SSL sockets. This may be + required when using external JSSE providers or to specify + cipher suites compatible with both MySQL server and used JVM. + + Since version: 5.1.35 + + allowLoadLocalInfile + + Should the driver allow use of 'LOAD DATA LOCAL INFILE...' + (defaults to 'true'). + + Default: true + + Since version: 3.0.3 + + allowUrlInLocalInfile + + Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' + statements? + + Default: false + + Since version: 3.1.4 + + allowPublicKeyRetrieval + + Allows special handshake roundtrip to get server RSA public + key directly from server. + + Default: false + + Since version: 5.1.31 + + paranoid + + Take measures to prevent exposure sensitive information in + error messages and clear data structures holding sensitive + data when possible? (defaults to 'false') + + Default: false + + Since version: 3.0.1 + + passwordCharacterEncoding + + What character encoding is used for passwords? Leaving this + set to the default value (null), uses the value set in + "characterEncoding" if there is one, otherwise uses UTF-8 as + default encoding. If the password contains non-ASCII + characters, the password encoding must match what server + encoding was set to when the password was created. For + passwords in other character encodings, the encoding will + have to be specified with this property (or with + "characterEncoding"), as it's not possible for the driver to + auto-detect this. + + Since version: 5.1.7 + + serverRSAPublicKeyFile + + File path to the server RSA public key file for + sha256_password authentication. If not specified, the public + key will be retrieved from the server. + + Since version: 5.1.31 + + Performance Extensions. + Properties and Descriptions + + callableStmtCacheSize + + If 'cacheCallableStmts' is enabled, how many callable + statements should be cached? + + Default: 100 + + Since version: 3.1.2 + + metadataCacheSize + + The number of queries to cache ResultSetMetadata for if + cacheResultSetMetaData is set to 'true' (default 50) + + Default: 50 + + Since version: 3.1.1 + + useLocalSessionState + + Should the driver refer to the internal values of autocommit + and transaction isolation that are set by + Connection.setAutoCommit() and + Connection.setTransactionIsolation() and transaction state as + maintained by the protocol, rather than querying the database + or blindly sending commands to the database for commit() or + rollback() method calls? + + Default: false + + Since version: 3.1.7 + + useLocalTransactionState + + Should the driver use the in-transaction state provided by + the MySQL protocol to determine if a commit() or rollback() + should actually be sent to the database? + + Default: false + + Since version: 5.1.7 + + prepStmtCacheSize + + If prepared statement caching is enabled, how many prepared + statements should be cached? + + Default: 25 + + Since version: 3.0.10 + + prepStmtCacheSqlLimit + + If prepared statement caching is enabled, what's the largest + SQL the driver will cache the parsing for? + + Default: 256 + + Since version: 3.0.10 + + parseInfoCacheFactory + + Name of a class implementing + com.mysql.jdbc.CacheAdapterFactory, which will be used to + create caches for the parsed representation of client-side + prepared statements. + + Default: com.mysql.jdbc.PerConnectionLRUFactory + + Since version: 5.1.1 + + serverConfigCacheFactory + + Name of a class implementing + com.mysql.jdbc.CacheAdapterFactory>, which will be used to create caches for MySQL + server configuration values + + Default: com.mysql.jdbc.PerVmServerConfigCacheFactory + + Since version: 5.1.1 + + alwaysSendSetIsolation + + Should the driver always communicate with the database when + Connection.setTransactionIsolation() is called? If set to + false, the driver will only communicate with the database + when the requested transaction isolation is different than + the whichever is newer, the last value that was set via + Connection.setTransactionIsolation(), or the value that was + read from the server when the connection was established. + Note that useLocalSessionState=true will force the same + behavior as alwaysSendSetIsolation=false, regardless of how + alwaysSendSetIsolation is set. + + Default: true + + Since version: 3.1.7 + + maintainTimeStats + + Should the driver maintain various internal timers to enable + idle time calculations as well as more verbose error messages + when the connection to the server fails? Setting this + property to false removes at least two calls to + System.getCurrentTimeMillis() per query. + + Default: true + + Since version: 3.1.9 + + useCursorFetch + + If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a + statement, should that statement use cursor-based fetching to + retrieve rows? + + Default: false + + Since version: 5.0.0 + + blobSendChunkSize + + Chunk size to use when sending BLOB/CLOBs via + ServerPreparedStatements. Note that this value cannot exceed + the value of "maxAllowedPacket" and, if that is the case, + then this value will be corrected automatically. + + Default: 1048576 + + Since version: 3.1.9 + + cacheCallableStmts + + Should the driver cache the parsing stage of + CallableStatements + + Default: false + + Since version: 3.1.2 + + cachePrepStmts + + Should the driver cache the parsing stage of + PreparedStatements of client-side prepared statements, the + "check" for suitability of server-side prepared and + server-side prepared statements themselves? + + Default: false + + Since version: 3.0.10 + + cacheResultSetMetadata + + Should the driver cache ResultSetMetaData for Statements and + PreparedStatements? (Req. JDK-1.4+, true/false, default + 'false') + + Default: false + + Since version: 3.1.1 + + cacheServerConfiguration + + Should the driver cache the results of 'SHOW VARIABLES' and + 'SHOW COLLATION' on a per-URL basis? + + Default: false + + Since version: 3.1.5 + + defaultFetchSize + + The driver will call setFetchSize(n) with this value on all + newly-created Statements + + Default: 0 + + Since version: 3.1.9 + + dontCheckOnDuplicateKeyUpdateInSQL + + Stops checking if every INSERT statement contains the "ON + DUPLICATE KEY UPDATE" clause. As a side effect, obtaining the + statement's generated keys information will return a list + where normally it wouldn't. Also be aware that, in this case, + the list of generated keys returned may not be accurate. The + effect of this property is canceled if set simultaneously + with 'rewriteBatchedStatements=true'. + + Default: false + + Since version: 5.1.32 + + dontTrackOpenResources + + The JDBC specification requires the driver to automatically + track and close resources, however if your application + doesn't do a good job of explicitly calling close() on + statements or result sets, this can cause memory leakage. + Setting this property to true relaxes this constraint, and + can be more memory efficient for some applications. Also the + automatic closing of the Statement and current ResultSet in + Statement.closeOnCompletion() and Statement.getMoreResults + ([Statement.CLOSE_CURRENT_RESULT | + Statement.CLOSE_ALL_RESULTS]), respectively, ceases to + happen. This property automatically sets + holdResultsOpenOverStatementClose=true. + + Default: false + + Since version: 3.1.7 + + dynamicCalendars + + Should the driver retrieve the default calendar when + required, or cache it per connection/session? + + Default: false + + Since version: 3.1.5 + + elideSetAutoCommits + + If using MySQL-4.1 or newer, should the driver only issue + 'set autocommit=n' queries when the server's state doesn't + match the requested state by + Connection.setAutoCommit(boolean)? + + Default: false + + Since version: 3.1.3 + + enableEscapeProcessing + + Sets the default escape processing behavior for Statement + objects. The method Statement.setEscapeProcessing() can be + used to specify the escape processing behavior for an + individual Statement object. Default escape processing + behavior in prepared statements must be defined with the + property 'processEscapeCodesForPrepStmts'. + + Default: true + + Since version: 5.1.37 + + enableQueryTimeouts + + When enabled, query timeouts set via + Statement.setQueryTimeout() use a shared java.util.Timer + instance for scheduling. Even if the timeout doesn't expire + before the query is processed, there will be memory used by + the TimerTask for the given timeout which won't be reclaimed + until the time the timeout would have expired if it hadn't + been cancelled by the driver. High-load environments might + want to consider disabling this functionality. + + Default: true + + Since version: 5.0.6 + + holdResultsOpenOverStatementClose + + Should the driver close result sets on Statement.close() as + required by the JDBC specification? + + Default: false + + Since version: 3.1.7 + + largeRowSizeThreshold + + What size result set row should the JDBC driver consider + "large", and thus use a more memory-efficient way of + representing the row internally? + + Default: 2048 + + Since version: 5.1.1 + + loadBalanceStrategy + + If using a load-balanced connection to connect to SQL nodes + in a MySQL Cluster/NDB configuration (by using the URL prefix + "jdbc:mysql:loadbalance://"), which load balancing algorithm + should the driver use: (1) "random" - the driver will pick a + random host for each request. This tends to work better than + round-robin, as the randomness will somewhat account for + spreading loads where requests vary in response time, while + round-robin can sometimes lead to overloaded nodes if there + are variations in response times across the workload. (2) + "bestResponseTime" - the driver will route the request to the + host that had the best response time for the previous + transaction. + + Default: random + + Since version: 5.0.6 + + locatorFetchBufferSize + + If 'emulateLocators' is configured to 'true', what size + buffer should be used when fetching BLOB data for + getBinaryInputStream? + + Default: 1048576 + + Since version: 3.2.1 + + readOnlyPropagatesToServer + + Should the driver issue appropriate statements to implicitly + set the transaction access mode on server side when + Connection.setReadOnly() is called? Setting this property to + 'true' enables InnoDB read-only potential optimizations but + also requires an extra roundtrip to set the right transaction + state. Even if this property is set to 'false', the driver + will do its best effort to prevent the execution of + database-state-changing queries. Requires minimum of MySQL + 5.6. + + Default: true + + Since version: 5.1.35 + + rewriteBatchedStatements + + Should the driver use multiqueries (irregardless of the + setting of "allowMultiQueries") as well as rewriting of + prepared statements for INSERT into multi-value inserts when + executeBatch() is called? Notice that this has the potential + for SQL injection if using plain java.sql.Statements and your + code doesn't sanitize input correctly. Notice that for + prepared statements, server-side prepared statements can not + currently take advantage of this rewrite option, and that if + you don't specify stream lengths when using + PreparedStatement.set*Stream(), the driver won't be able to + determine the optimum number of parameters per batch and you + might receive an error from the driver that the resultant + packet is too large. Statement.getGeneratedKeys() for these + rewritten statements only works when the entire batch + includes INSERT statements. Please be aware using + rewriteBatchedStatements=true with INSERT .. ON DUPLICATE KEY + UPDATE that for rewritten statement server returns only one + value as sum of all affected (or found) rows in batch and it + isn't possible to map it correctly to initial statements; in + this case driver returns 0 as a result of each batch + statement if total count was 0, and the + Statement.SUCCESS_NO_INFO as a result of each batch statement + if total count was > 0. + + Default: false + + Since version: 3.1.13 + + useDirectRowUnpack + + Use newer result set row unpacking code that skips a copy + from network buffers to a MySQL packet instance and instead + reads directly into the result set row data buffers. + + Default: true + + Since version: 5.1.1 + + useDynamicCharsetInfo + + Should the driver use a per-connection cache of character set + information queried from the server when necessary, or use a + built-in static mapping that is more efficient, but isn't + aware of custom character sets or character sets implemented + after the release of the JDBC driver? + + Default: true + + Since version: 5.0.6 + + useFastDateParsing + + Use internal String->Date/Time/Timestamp conversion routines + to avoid excessive object creation? This is part of the + legacy date-time code, thus the property has an effect only + when "useLegacyDatetimeCode=true." + + Default: true + + Since version: 5.0.5 + + useFastIntParsing + + Use internal String->Integer conversion routines to avoid + excessive object creation? + + Default: true + + Since version: 3.1.4 + + useJvmCharsetConverters + + Always use the character encoding routines built into the + JVM, rather than using lookup tables for single-byte + character sets? + + Default: false + + Since version: 5.0.1 + + useReadAheadInput + + Use newer, optimized non-blocking, buffered input stream when + reading from the server? + + Default: true + + Since version: 3.1.5 + + Debugging/Profiling. + Properties and Descriptions + + logger + + The name of a class that implements "com.mysql.jdbc.log.Log" + that will be used to log messages to. (default is + "com.mysql.jdbc.log.StandardLogger", which logs to STDERR) + + Default: com.mysql.jdbc.log.StandardLogger + + Since version: 3.1.1 + + gatherPerfMetrics + + Should the driver gather performance metrics, and report them + via the configured logger every 'reportMetricsIntervalMillis' + milliseconds? + + Default: false + + Since version: 3.1.2 + + profileSQL + + Trace queries and their execution/fetch times to the + configured logger (true/false) defaults to 'false' + + Default: false + + Since version: 3.1.0 + + profileSql + + Deprecated, use 'profileSQL' instead. Trace queries and their + execution/fetch times on STDERR (true/false) defaults to + 'false' + + Since version: 2.0.14 + + reportMetricsIntervalMillis + + If 'gatherPerfMetrics' is enabled, how often should they be + logged (in ms)? + + Default: 30000 + + Since version: 3.1.2 + + maxQuerySizeToLog + + Controls the maximum length/size of a query that will get + logged when profiling or tracing + + Default: 2048 + + Since version: 3.1.3 + + packetDebugBufferSize + + The maximum number of packets to retain when + 'enablePacketDebug' is true + + Default: 20 + + Since version: 3.1.3 + + slowQueryThresholdMillis + + If 'logSlowQueries' is enabled, how long should a query (in + ms) before it is logged as 'slow'? + + Default: 2000 + + Since version: 3.1.2 + + slowQueryThresholdNanos + + If 'useNanosForElapsedTime' is set to true, and this property + is set to a non-zero value, the driver will use this + threshold (in nanosecond units) to determine if a query was + slow. + + Default: 0 + + Since version: 5.0.7 + + useUsageAdvisor + + Should the driver issue 'usage' warnings advising proper and + efficient usage of JDBC and MySQL Connector/J to the log + (true/false, defaults to 'false')? + + Default: false + + Since version: 3.1.1 + + autoGenerateTestcaseScript + + Should the driver dump the SQL it is executing, including + server-side prepared statements to STDERR? + + Default: false + + Since version: 3.1.9 + + autoSlowLog + + Instead of using slowQueryThreshold* to determine if a query + is slow enough to be logged, maintain statistics that allow + the driver to determine queries that are outside the 99th + percentile? + + Default: true + + Since version: 5.1.4 + + clientInfoProvider + + The name of a class that implements the + com.mysql.jdbc.JDBC4ClientInfoProvider interface in order to + support JDBC-4.0's Connection.get/setClientInfo() methods + + Default: com.mysql.jdbc.JDBC4CommentClientInfoProvider + + Since version: 5.1.0 + + dumpMetadataOnColumnNotFound + + Should the driver dump the field-level metadata of a result + set into the exception message when ResultSet.findColumn() + fails? + + Default: false + + Since version: 3.1.13 + + dumpQueriesOnException + + Should the driver dump the contents of the query sent to the + server in the message for SQLExceptions? + + Default: false + + Since version: 3.1.3 + + enablePacketDebug + + When enabled, a ring-buffer of 'packetDebugBufferSize' + packets will be kept, and dumped when exceptions are thrown + in key areas in the driver's code + + Default: false + + Since version: 3.1.3 + + explainSlowQueries + + If 'logSlowQueries' is enabled, should the driver + automatically issue an 'EXPLAIN' on the server and send the + results to the configured log at a WARN level? + + Default: false + + Since version: 3.1.2 + + includeInnodbStatusInDeadlockExceptions + + Include the output of "SHOW ENGINE INNODB STATUS" in + exception messages when deadlock exceptions are detected? + + Default: false + + Since version: 5.0.7 + + includeThreadDumpInDeadlockExceptions + + Include a current Java thread dump in exception messages when + deadlock exceptions are detected? + + Default: false + + Since version: 5.1.15 + + includeThreadNamesAsStatementComment + + Include the name of the current thread as a comment visible + in "SHOW PROCESSLIST", or in Innodb deadlock dumps, useful in + correlation with + "includeInnodbStatusInDeadlockExceptions=true" and + "includeThreadDumpInDeadlockExceptions=true". + + Default: false + + Since version: 5.1.15 + + logSlowQueries + + Should queries that take longer than + 'slowQueryThresholdMillis' be logged? + + Default: false + + Since version: 3.1.2 + + logXaCommands + + Should the driver log XA commands sent by MysqlXaConnection + to the server, at the DEBUG level of logging? + + Default: false + + Since version: 5.0.5 + + profilerEventHandler + + Name of a class that implements the interface + com.mysql.jdbc.profiler.ProfilerEventHandler that will be + used to handle profiling/tracing events. + + Default: com.mysql.jdbc.profiler.LoggingProfilerEventHandler + + Since version: 5.1.6 + + resultSetSizeThreshold + + If the usage advisor is enabled, how many rows should a + result set contain before the driver warns that it is + suspiciously large? + + Default: 100 + + Since version: 5.0.5 + + traceProtocol + + Should trace-level network protocol be logged? + + Default: false + + Since version: 3.1.2 + + useNanosForElapsedTime + + For profiling/debugging functionality that measures elapsed + time, should the driver try to use nanoseconds resolution if + available (JDK >= 1.5)? + + Default: false + + Since version: 5.0.7 + + Miscellaneous. + Properties and Descriptions + + useUnicode + + Should the driver use Unicode character encodings when + handling strings? Should only be used when the driver can't + determine the character set mapping, or you are trying to + 'force' the driver to use a character set that MySQL either + doesn't natively support (such as UTF-8), true/false, + defaults to 'true' + + Default: true + + Since version: 1.1g + + characterEncoding + + If 'useUnicode' is set to true, what character encoding + should the driver use when dealing with strings? (defaults is + to 'autodetect') + + Since version: 1.1g + + characterSetResults + + Character set to tell the server to return results as. + + Since version: 3.0.13 + + connectionAttributes + + A comma-delimited list of user-defined key:value pairs (in + addition to standard MySQL-defined key:value pairs) to be + passed to MySQL Server for display as connection attributes + in the PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS table. + Example usage: connectionAttributes=key1:value1,key2:value2 + This functionality is available for use with MySQL Server + version 5.6 or later only. Earlier versions of MySQL Server + do not support connection attributes, causing this + configuration option to be ignored. Setting + connectionAttributes=none will cause connection attribute + processing to be bypassed, for situations where Connection + creation/initialization speed is critical. + + Since version: 5.1.25 + + connectionCollation + + If set, tells the server to use this collation via 'set + collation_connection' + + Since version: 3.0.13 + + useBlobToStoreUTF8OutsideBMP + + Tells the driver to treat [MEDIUM/LONG]BLOB columns as + [LONG]VARCHAR columns holding text encoded in UTF-8 that has + characters outside the BMP (4-byte encodings), which MySQL + server can't handle natively. + + Default: false + + Since version: 5.1.3 + + utf8OutsideBmpExcludedColumnNamePattern + + When "useBlobToStoreUTF8OutsideBMP" is set to "true", column + names matching the given regex will still be treated as BLOBs + unless they match the regex specified for + "utf8OutsideBmpIncludedColumnNamePattern". The regex must + follow the patterns used for the java.util.regex package. + + Since version: 5.1.3 + + utf8OutsideBmpIncludedColumnNamePattern + + Used to specify exclusion rules to + "utf8OutsideBmpExcludedColumnNamePattern". The regex must + follow the patterns used for the java.util.regex package. + + Since version: 5.1.3 + + loadBalanceEnableJMX + + Enables JMX-based management of load-balanced connection + groups, including live addition/removal of hosts from + load-balancing pool. + + Default: false + + Since version: 5.1.13 + + sessionVariables + + A comma-separated list of name/value pairs to be sent as SET + SESSION ... to the server when the driver connects. + + Since version: 3.1.8 + + useColumnNamesInFindColumn + + Prior to JDBC-4.0, the JDBC specification had a bug related + to what could be given as a "column name" to ResultSet + methods like findColumn(), or getters that took a String + property. JDBC-4.0 clarified "column name" to mean the label, + as given in an "AS" clause and returned by + ResultSetMetaData.getColumnLabel(), and if no AS clause, the + column name. Setting this property to "true" will give + behavior that is congruent to JDBC-3.0 and earlier versions + of the JDBC specification, but which because of the + specification bug could give unexpected results. This + property is preferred over "useOldAliasMetadataBehavior" + unless you need the specific behavior that it provides with + respect to ResultSetMetadata. + + Default: false + + Since version: 5.1.7 + + allowNanAndInf + + Should the driver allow NaN or +/- INF values in + PreparedStatement.setDouble()? + + Default: false + + Since version: 3.1.5 + + autoClosePStmtStreams + + Should the driver automatically call .close() on + streams/readers passed as arguments via set*() methods? + + Default: false + + Since version: 3.1.12 + + autoDeserialize + + Should the driver automatically detect and de-serialize + objects stored in BLOB fields? + + Default: false + + Since version: 3.1.5 + + blobsAreStrings + + Should the driver always treat BLOBs as Strings - + specifically to work around dubious metadata returned by the + server for GROUP BY clauses? + + Default: false + + Since version: 5.0.8 + + cacheDefaultTimezone + + Caches client's default time zone. This results in better + performance when dealing with time zone conversions in Date + and Time data types, however it won't be aware of time zone + changes if they happen at runtime. + + Default: true + + Since version: 5.1.35 + + capitalizeTypeNames + + Capitalize type names in DatabaseMetaData? (usually only + useful when using WebObjects, true/false, defaults to + 'false') + + Default: true + + Since version: 2.0.7 + + clobCharacterEncoding + + The character encoding to use for sending and retrieving + TEXT, MEDIUMTEXT and LONGTEXT values instead of the + configured connection characterEncoding + + Since version: 5.0.0 + + clobberStreamingResults + + This will cause a 'streaming' ResultSet to be automatically + closed, and any outstanding data still streaming from the + server to be discarded if another query is executed before + all the data has been read from the server. + + Default: false + + Since version: 3.0.9 + + compensateOnDuplicateKeyUpdateCounts + + Should the driver compensate for the update counts of "ON + DUPLICATE KEY" INSERT statements (2 = 1, 0 = 1) when using + prepared statements? + + Default: false + + Since version: 5.1.7 + + continueBatchOnError + + Should the driver continue processing batch commands if one + statement fails. The JDBC spec allows either way (defaults to + 'true'). + + Default: true + + Since version: 3.0.3 + + createDatabaseIfNotExist + + Creates the database given in the URL if it doesn't yet + exist. Assumes the configured user has permissions to create + databases. + + Default: false + + Since version: 3.1.9 + + detectCustomCollations + + Should the driver detect custom charsets/collations installed + on server (true/false, defaults to 'false'). If this option + set to 'true' driver gets actual charsets/collations from + server each time connection establishes. This could slow down + connection initialization significantly. + + Default: false + + Since version: 5.1.29 + + emptyStringsConvertToZero + + Should the driver allow conversions from empty string fields + to numeric values of '0'? + + Default: true + + Since version: 3.1.8 + + emulateLocators + + Should the driver emulate java.sql.Blobs with locators? With + this feature enabled, the driver will delay loading the + actual Blob data until the one of the retrieval methods + (getInputStream(), getBytes(), and so forth) on the blob data + stream has been accessed. For this to work, you must use a + column alias with the value of the column to the actual name + of the Blob. The feature also has the following restrictions: + The SELECT that created the result set must reference only + one table, the table must have a primary key; the SELECT must + alias the original blob column name, specified as a string, + to an alternate name; the SELECT must cover all columns that + make up the primary key. + + Default: false + + Since version: 3.1.0 + + emulateUnsupportedPstmts + + Should the driver detect prepared statements that are not + supported by the server, and replace them with client-side + emulated versions? + + Default: true + + Since version: 3.1.7 + + exceptionInterceptors + + Comma-delimited list of classes that implement + com.mysql.jdbc.ExceptionInterceptor. These classes will be + instantiated one per Connection instance, and all + SQLExceptions thrown by the driver will be allowed to be + intercepted by these interceptors, in a chained fashion, with + the first class listed as the head of the chain. + + Since version: 5.1.8 + + functionsNeverReturnBlobs + + Should the driver always treat data from functions returning + BLOBs as Strings - specifically to work around dubious + metadata returned by the server for GROUP BY clauses? + + Default: false + + Since version: 5.0.8 + + generateSimpleParameterMetadata + + Should the driver generate simplified parameter metadata for + PreparedStatements when no metadata is available either + because the server couldn't support preparing the statement, + or server-side prepared statements are disabled? + + Default: false + + Since version: 5.0.5 + + getProceduresReturnsFunctions + + Pre-JDBC4 DatabaseMetaData API has only the getProcedures() + and getProcedureColumns() methods, so they return metadata + info for both stored procedures and functions. JDBC4 was + extended with the getFunctions() and getFunctionColumns() + methods and the expected behaviours of previous methods are + not well defined. For JDBC4 and higher, default 'true' value + of the option means that calls of + DatabaseMetaData.getProcedures() and + DatabaseMetaData.getProcedureColumns() return metadata for + both procedures and functions as before, keeping backward + compatibility. Setting this property to 'false' decouples + Connector/J from its pre-JDBC4 behaviours for + DatabaseMetaData.getProcedures() and + DatabaseMetaData.getProcedureColumns(), forcing them to + return metadata for procedures only. + + Default: true + + Since version: 5.1.26 + + ignoreNonTxTables + + Ignore non-transactional table warning for rollback? + (defaults to 'false'). + + Default: false + + Since version: 3.0.9 + + jdbcCompliantTruncation + + Should the driver throw java.sql.DataTruncation exceptions + when data is truncated as is required by the JDBC + specification when connected to a server that supports + warnings (MySQL 4.1.0 and newer)? This property has no effect + if the server sql-mode includes STRICT_TRANS_TABLES. + + Default: true + + Since version: 3.1.2 + + loadBalanceAutoCommitStatementRegex + + When load-balancing is enabled for auto-commit statements + (via loadBalanceAutoCommitStatementThreshold), the statement + counter will only increment when the SQL matches the regular + expression. By default, every statement issued matches. + + Since version: 5.1.15 + + loadBalanceAutoCommitStatementThreshold + + When auto-commit is enabled, the number of statements which + should be executed before triggering load-balancing to + rebalance. Default value of 0 causes load-balanced + connections to only rebalance when exceptions are + encountered, or auto-commit is disabled and transactions are + explicitly committed or rolled back. + + Default: 0 + + Since version: 5.1.15 + + loadBalanceBlacklistTimeout + + Time in milliseconds between checks of servers which are + unavailable, by controlling how long a server lives in the + global blacklist. + + Default: 0 + + Since version: 5.1.0 + + loadBalanceConnectionGroup + + Logical group of load-balanced connections within a + classloader, used to manage different groups independently. + If not specified, live management of load-balanced + connections is disabled. + + Since version: 5.1.13 + + loadBalanceExceptionChecker + + Fully-qualified class name of custom exception checker. The + class must implement + com.mysql.jdbc.LoadBalanceExceptionChecker interface, and is + used to inspect SQLExceptions and determine whether they + should trigger fail-over to another host in a load-balanced + deployment. + + Default: com.mysql.jdbc.StandardLoadBalanceExceptionChecker + + Since version: 5.1.13 + + loadBalancePingTimeout + + Time in milliseconds to wait for ping response from each of + load-balanced physical connections when using load-balanced + Connection. + + Default: 0 + + Since version: 5.1.13 + + loadBalanceSQLExceptionSubclassFailover + + Comma-delimited list of classes/interfaces used by default + load-balanced exception checker to determine whether a given + SQLException should trigger failover. The comparison is done + using Class.isInstance(SQLException) using the thrown + SQLException. + + Since version: 5.1.13 + + loadBalanceSQLStateFailover + + Comma-delimited list of SQLState codes used by default + load-balanced exception checker to determine whether a given + SQLException should trigger failover. The SQLState of a given + SQLException is evaluated to determine whether it begins with + any value in the comma-delimited list. + + Since version: 5.1.13 + + loadBalanceValidateConnectionOnSwapServer + + Should the load-balanced Connection explicitly check whether + the connection is live when swapping to a new physical + connection at commit/rollback? + + Default: false + + Since version: 5.1.13 + + maxRows + + The maximum number of rows to return (0, the default means + return all rows). + + Default: -1 + + Since version: all versions + + netTimeoutForStreamingResults + + What value should the driver automatically set the server + setting 'net_write_timeout' to when the streaming result sets + feature is in use? (value has unit of seconds, the value '0' + means the driver will not try and adjust this value) + + Default: 600 + + Since version: 5.1.0 + + noAccessToProcedureBodies + + When determining procedure parameter types for + CallableStatements, and the connected user can't access + procedure bodies through "SHOW CREATE PROCEDURE" or select on + mysql.proc should the driver instead create basic metadata + (all parameters reported as IN VARCHARs, but allowing + registerOutParameter() to be called on them anyway) instead + of throwing an exception? + + Default: false + + Since version: 5.0.3 + + noDatetimeStringSync + + Don't ensure that + ResultSet.getDatetimeType().toString().equals(ResultSet.getSt + ring()) + + Default: false + + Since version: 3.1.7 + + noTimezoneConversionForDateType + + Don't convert DATE values using the server time zone if + 'useTimezone'='true' or 'useLegacyDatetimeCode'='false' + + Default: true + + Since version: 5.1.35 + + noTimezoneConversionForTimeType + + Don't convert TIME values using the server time zone if + 'useTimezone'='true' + + Default: false + + Since version: 5.0.0 + + nullCatalogMeansCurrent + + When DatabaseMetadataMethods ask for a 'catalog' parameter, + does the value null mean use the current catalog? (this is + not JDBC-compliant, but follows legacy behavior from earlier + versions of the driver) + + Default: true + + Since version: 3.1.8 + + nullNamePatternMatchesAll + + Should DatabaseMetaData methods that accept *pattern + parameters treat null the same as '%' (this is not + JDBC-compliant, however older versions of the driver accepted + this departure from the specification) + + Default: true + + Since version: 3.1.8 + + overrideSupportsIntegrityEnhancementFacility + + Should the driver return "true" for + DatabaseMetaData.supportsIntegrityEnhancementFacility() even + if the database doesn't support it to workaround applications + that require this method to return "true" to signal support + of foreign keys, even though the SQL specification states + that this facility contains much more than just foreign key + support (one such application being OpenOffice)? + + Default: false + + Since version: 3.1.12 + + padCharsWithSpace + + If a result set column has the CHAR type and the value does + not fill the amount of characters specified in the DDL for + the column, should the driver pad the remaining characters + with space (for ANSI compliance)? + + Default: false + + Since version: 5.0.6 + + pedantic + + Follow the JDBC spec to the letter. + + Default: false + + Since version: 3.0.0 + + pinGlobalTxToPhysicalConnection + + When using XAConnections, should the driver ensure that + operations on a given XID are always routed to the same + physical connection? This allows the XAConnection to support + "XA START ... JOIN" after "XA END" has been called + + Default: false + + Since version: 5.0.1 + + populateInsertRowWithDefaultValues + + When using ResultSets that are CONCUR_UPDATABLE, should the + driver pre-populate the "insert" row with default values from + the DDL for the table used in the query so those values are + immediately available for ResultSet accessors? This + functionality requires a call to the database for metadata + each time a result set of this type is created. If disabled + (the default), the default values will be populated by the an + internal call to refreshRow() which pulls back default values + and/or values changed by triggers. + + Default: false + + Since version: 5.0.5 + + processEscapeCodesForPrepStmts + + Should the driver process escape codes in queries that are + prepared? Default escape processing behavior in non-prepared + statements must be defined with the property + 'enableEscapeProcessing'. + + Default: true + + Since version: 3.1.12 + + queryTimeoutKillsConnection + + If the timeout given in Statement.setQueryTimeout() expires, + should the driver forcibly abort the Connection instead of + attempting to abort the query? + + Default: false + + Since version: 5.1.9 + + relaxAutoCommit + + If the version of MySQL the driver connects to does not + support transactions, still allow calls to commit(), + rollback() and setAutoCommit() (true/false, defaults to + 'false')? + + Default: false + + Since version: 2.0.13 + + retainStatementAfterResultSetClose + + Should the driver retain the Statement reference in a + ResultSet after ResultSet.close() has been called. This is + not JDBC-compliant after JDBC-4.0. + + Default: false + + Since version: 3.1.11 + + rollbackOnPooledClose + + Should the driver issue a rollback() when the logical + connection in a pool is closed? + + Default: true + + Since version: 3.0.15 + + runningCTS13 + + Enables workarounds for bugs in Sun's JDBC compliance + testsuite version 1.3 + + Default: false + + Since version: 3.1.7 + + sendFractionalSeconds + + Send fractional part from TIMESTAMP seconds. If set to false, + the nanoseconds value of TIMESTAMP values will be truncated + before sending any data to the server. This option applies + only to prepared statements, callable statements or updatable + result sets. + + Default: true + + Since version: 5.1.37 + + serverTimezone + + Override detection/mapping of time zone. Used when time zone + from server doesn't map to Java time zone + + Since version: 3.0.2 + + statementInterceptors + + A comma-delimited list of classes that implement + "com.mysql.jdbc.StatementInterceptor" that should be placed + "in between" query execution to influence the results. + StatementInterceptors are "chainable", the results returned + by the "current" interceptor will be passed on to the next in + in the chain, from left-to-right order, as specified in this + property. + + Since version: 5.1.1 + + strictFloatingPoint + + Used only in older versions of compliance test + + Default: false + + Since version: 3.0.0 + + strictUpdates + + Should the driver do strict checking (all primary keys + selected) of updatable result sets (true, false, defaults to + 'true')? + + Default: true + + Since version: 3.0.4 + + tinyInt1isBit + + Should the driver treat the datatype TINYINT(1) as the BIT + type (because the server silently converts BIT -> TINYINT(1) + when creating tables)? + + Default: true + + Since version: 3.0.16 + + transformedBitIsBoolean + + If the driver converts TINYINT(1) to a different type, should + it use BOOLEAN instead of BIT for future compatibility with + MySQL-5.0, as MySQL-5.0 has a BIT type? + + Default: false + + Since version: 3.1.9 + + treatUtilDateAsTimestamp + + Should the driver treat java.util.Date as a TIMESTAMP for the + purposes of PreparedStatement.setObject()? + + Default: true + + Since version: 5.0.5 + + ultraDevHack + + Create PreparedStatements for prepareCall() when required, + because UltraDev is broken and issues a prepareCall() for + _all_ statements? (true/false, defaults to 'false') + + Default: false + + Since version: 2.0.3 + + useAffectedRows + + Don't set the CLIENT_FOUND_ROWS flag when connecting to the + server (not JDBC-compliant, will break most applications that + rely on "found" rows vs. "affected rows" for DML statements), + but does cause "correct" update counts from "INSERT ... ON + DUPLICATE KEY UPDATE" statements to be returned by the + server. + + Default: false + + Since version: 5.1.7 + + useGmtMillisForDatetimes + + Convert between session time zone and GMT before creating + Date and Timestamp instances (value of 'false' leads to + legacy behavior, 'true' leads to more JDBC-compliant + behavior)? This is part of the legacy date-time code, thus + the property has an effect only when + "useLegacyDatetimeCode=true." + + Default: false + + Since version: 3.1.12 + + useHostsInPrivileges + + Add '@hostname' to users in + DatabaseMetaData.getColumn/TablePrivileges() (true/false), + defaults to 'true'. + + Default: true + + Since version: 3.0.2 + + useInformationSchema + + When connected to MySQL-5.0.7 or newer, should the driver use + the INFORMATION_SCHEMA to derive information used by + DatabaseMetaData? + + Default: false + + Since version: 5.0.0 + + useJDBCCompliantTimezoneShift + + Should the driver use JDBC-compliant rules when converting + TIME/TIMESTAMP/DATETIME values' time zone information for + those JDBC arguments which take a java.util.Calendar + argument? This is part of the legacy date-time code, thus the + property has an effect only when + "useLegacyDatetimeCode=true." + + Default: false + + Since version: 5.0.0 + + useLegacyDatetimeCode + + Use code for DATE/TIME/DATETIME/TIMESTAMP handling in result + sets and statements that consistently handles time zone + conversions from client to server and back again, or use the + legacy code for these datatypes that has been in the driver + for backwards-compatibility? Setting this property to 'false' + voids the effects of "useTimezone," + "useJDBCCompliantTimezoneShift," "useGmtMillisForDatetimes," + and "useFastDateParsing." + + Default: true + + Since version: 5.1.6 + + useOldAliasMetadataBehavior + + Should the driver use the legacy behavior for "AS" clauses on + columns and tables, and only return aliases (if any) for + ResultSetMetaData.getColumnName() or + ResultSetMetaData.getTableName() rather than the original + column/table name? In 5.0.x, the default value was true. + + Default: false + + Since version: 5.0.4 + + useOldUTF8Behavior + + Use the UTF-8 behavior the driver did when communicating with + 4.0 and older servers + + Default: false + + Since version: 3.1.6 + + useOnlyServerErrorMessages + + Don't prepend 'standard' SQLState error messages to error + messages returned by the server. + + Default: true + + Since version: 3.0.15 + + useSSPSCompatibleTimezoneShift + + If migrating from an environment that was using server-side + prepared statements, and the configuration property + "useJDBCCompliantTimeZoneShift" set to "true", use compatible + behavior when not using server-side prepared statements when + sending TIMESTAMP values to the MySQL server. + + Default: false + + Since version: 5.0.5 + + useServerPrepStmts + + Use server-side prepared statements if the server supports + them? + + Default: false + + Since version: 3.1.0 + + useSqlStateCodes + + Use SQL Standard state codes instead of 'legacy' X/Open/SQL + state codes (true/false), default is 'true' + + Default: true + + Since version: 3.1.3 + + useStreamLengthsInPrepStmts + + Honor stream length parameter in + PreparedStatement/ResultSet.setXXXStream() method calls + (true/false, defaults to 'true')? + + Default: true + + Since version: 3.0.2 + + useTimezone + + Convert time/date types between client and server time zones + (true/false, defaults to 'false')? This is part of the legacy + date-time code, thus the property has an effect only when + "useLegacyDatetimeCode=true." + + Default: false + + Since version: 3.0.2 + + useUnbufferedInput + + Don't use BufferedInputStream for reading data from the + server + + Default: true + + Since version: 3.0.11 + + yearIsDateType + + Should the JDBC driver treat the MySQL type "YEAR" as a + java.sql.Date, or as a SHORT? + + Default: true + + Since version: 3.1.9 + + zeroDateTimeBehavior + + What should happen when the driver encounters DATETIME values + that are composed entirely of zeros (used by MySQL to + represent invalid dates)? Valid values are "exception", + "round" and "convertToNull". + + Default: exception + + Since version: 3.1.4 + + Connector/J also supports access to MySQL using named pipes + on Windows platforms with the NamedPipeSocketFactory as a + plugin-socket factory. If you do not use a namedPipePath + property, the default of '\\.\pipe\MySQL' is used. If you use + the NamedPipeSocketFactory, the host name and port number + values in the JDBC URL are ignored. To enable this feature, + set the socketFactory property: +socketFactory=com.mysql.jdbc.NamedPipeSocketFactory + + Named pipes only work when connecting to a MySQL server on + the same physical machine where the JDBC driver is running. + In simple performance tests, named pipe access is between + 30%-50% faster than the standard TCP/IP access. However, this + varies per system, and named pipes are slower than TCP/IP in + many Windows configurations. + + To create your own socket factories, follow the example code + in com.mysql.jdbc.NamedPipeSocketFactory, or + com.mysql.jdbc.StandardSocketFactory. + +5.1.1 Properties Files for the useConfigs Option + + The useConfigs connection option is convenient shorthand for + specifying combinations of options for particular scenarios. + The argument values you can use with this option correspond + to the names of .properties files within the Connector/J + mysql-connector-java-version-bin.jar JAR file. For example, + the Connector/J 5.1.9 driver includes the following + configuration properties files: +$ unzip mysql-connector-java-5.1.19-bin.jar '*/configs/*' +Archive: mysql-connector-java-5.1.19-bin.jar + creating: com/mysql/jdbc/configs/ + inflating: com/mysql/jdbc/configs/3-0-Compat.properties + inflating: com/mysql/jdbc/configs/5-0-Compat.properties + inflating: com/mysql/jdbc/configs/clusterBase.properties + inflating: com/mysql/jdbc/configs/coldFusion.properties + inflating: com/mysql/jdbc/configs/fullDebug.properties + inflating: com/mysql/jdbc/configs/maxPerformance.properties + inflating: com/mysql/jdbc/configs/solarisMaxPerformance.properties + + To specify one of these combinations of options, specify + useConfigs=3-0-Compat, useConfigs=maxPerformance, and so on. + The following sections show the options that are part of each + useConfigs setting. For the details of why each one is + included, see the comments in the .properties files. + +3-0-Compat + +emptyStringsConvertToZero=true +jdbcCompliantTruncation=false +noDatetimeStringSync=true +nullCatalogMeansCurrent=true +nullNamePatternMatchesAll=true +transformedBitIsBoolean=false +dontTrackOpenResources=true +zeroDateTimeBehavior=convertToNull +useServerPrepStmts=false +autoClosePStmtStreams=true +processEscapeCodesForPrepStmts=false +useFastDateParsing=false +populateInsertRowWithDefaultValues=false +useDirectRowUnpack=false + +5-0-Compat + +useDirectRowUnpack=false + +clusterBase + +autoReconnect=true +failOverReadOnly=false +roundRobinLoadBalance=true + +coldFusion + +useDynamicCharsetInfo=false +alwaysSendSetIsolation=false +useLocalSessionState=true +autoReconnect=true + +fullDebug + +profileSQL=true +gatherPerfMetrics=true +useUsageAdvisor=true +logSlowQueries=true +explainSlowQueries=true + +maxPerformance + +cachePrepStmts=true +cacheCallableStmts=true +cacheServerConfiguration=true +useLocalSessionState=true +elideSetAutoCommits=true +alwaysSendSetIsolation=false +enableQueryTimeouts=false + +solarisMaxPerformance + +useUnbufferedInput=false +useReadAheadInput=false +maintainTimeStats=false + +5.2 JDBC API Implementation Notes + + MySQL Connector/J, as a rigorous implementation of the JDBC + API + (http://www.oracle.com/technetwork/java/javase/jdbc/index.htm + l), passes all of the tests in the publicly available version + of Oracle's JDBC compliance test suite. The JDBC + specification is flexible on how certain functionality should + be implemented. This section gives details on an + interface-by-interface level about implementation decisions + that might affect how you code applications with MySQL + Connector/J. + + * BLOB + Starting with Connector/J version 3.1.0, you can emulate + BLOBs with locators by adding the property + emulateLocators=true to your JDBC URL. Using this method, + the driver will delay loading the actual BLOB data until + you retrieve the other data and then use retrieval + methods (getInputStream(), getBytes(), and so forth) on + the BLOB data stream. + You must use a column alias with the value of the column + to the actual name of the BLOB, for example: +SELECT id, 'data' as blob_data from blobtable + + You must also follow these rules: + + + The SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) + must reference only one table. The table must have a + primary key + (http://dev.mysql.com/doc/refman/5.7/en/glossary.htm + l#glos_primary_key). + + + The SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) + must alias the original BLOB column name, specified + as a string, to an alternate name. + + + The SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) + must cover all columns that make up the primary key. + The BLOB implementation does not allow in-place + modification (they are copies, as reported by the + DatabaseMetaData.locatorsUpdateCopies() method). Because + of this, use the corresponding + PreparedStatement.setBlob() or ResultSet.updateBlob() (in + the case of updatable result sets) methods to save + changes back to the database. + + * CallableStatement + Starting with Connector/J 3.1.1, stored procedures are + supported when connecting to MySQL version 5.0 or newer + using the CallableStatement interface. Currently, the + getParameterMetaData() method of CallableStatement is not + supported. + + * CLOB + The CLOB implementation does not allow in-place + modification (they are copies, as reported by the + DatabaseMetaData.locatorsUpdateCopies() method). Because + of this, use the PreparedStatement.setClob() method to + save changes back to the database. The JDBC API does not + have a ResultSet.updateClob() method. + + * Connection + Unlike the pre-Connector/J JDBC driver (MM.MySQL), the + isClosed() method does not ping the server to determine + if it is available. In accordance with the JDBC + specification, it only returns true if closed() has been + called on the connection. If you need to determine if the + connection is still valid, issue a simple query, such as + SELECT 1. The driver will throw an exception if the + connection is no longer valid. + + * DatabaseMetaData + Foreign key + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glo + s_foreign_key) information + (getImportedKeys()/getExportedKeys() and + getCrossReference()) is only available from InnoDB + (http://dev.mysql.com/doc/refman/5.7/en/innodb-storage-en + gine.html) tables. The driver uses SHOW CREATE TABLE + (http://dev.mysql.com/doc/refman/5.7/en/show-create-table + .html) to retrieve this information, so if any other + storage engines add support for foreign keys, the driver + would transparently support them as well. + + * PreparedStatement + PreparedStatements are implemented by the driver, as + MySQL does not have a prepared statement feature. Because + of this, the driver does not implement + getParameterMetaData() or getMetaData() as it would + require the driver to have a complete SQL parser in the + client. + Starting with version 3.1.0 MySQL Connector/J, + server-side prepared statements and binary-encoded result + sets are used when the server supports them. + Take care when using a server-side prepared statement + with large parameters that are set using + setBinaryStream(), setAsciiStream(), setUnicodeStream(), + setBlob(), or setClob(). To re-execute the statement with + any large parameter changed to a nonlarge parameter, call + clearParameters() and set all parameters again. The + reason for this is as follows: + + + During both server-side prepared statements and + client-side emulation, large data is exchanged only + when PreparedStatement.execute() is called. + + + Once that has been done, the stream used to read the + data on the client side is closed (as per the JDBC + spec), and cannot be read from again. + + + If a parameter changes from large to nonlarge, the + driver must reset the server-side state of the + prepared statement to allow the parameter that is + being changed to take the place of the prior large + value. This removes all of the large data that has + already been sent to the server, thus requiring the + data to be re-sent, using the setBinaryStream(), + setAsciiStream(), setUnicodeStream(), setBlob() or + setClob() method. + Consequently, to change the type of a parameter to a + nonlarge one, you must call clearParameters() and set all + parameters of the prepared statement again before it can + be re-executed. + + * ResultSet + By default, ResultSets are completely retrieved and + stored in memory. In most cases this is the most + efficient way to operate and, due to the design of the + MySQL network protocol, is easier to implement. If you + are working with ResultSets that have a large number of + rows or large values and cannot allocate heap space in + your JVM for the memory required, you can tell the driver + to stream the results back one row at a time. + To enable this functionality, create a Statement instance + in the following manner: +stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY); +stmt.setFetchSize(Integer.MIN_VALUE); + + The combination of a forward-only, read-only result set, + with a fetch size of Integer.MIN_VALUE serves as a signal + to the driver to stream result sets row-by-row. After + this, any result sets created with the statement will be + retrieved row-by-row. + There are some caveats with this approach. You must read + all of the rows in the result set (or close it) before + you can issue any other queries on the connection, or an + exception will be thrown. + The earliest the locks these statements hold can be + released (whether they be MyISAM table-level locks or + row-level locks in some other storage engine such as + InnoDB) is when the statement completes. + If the statement is within scope of a transaction, then + locks are released when the transaction completes (which + implies that the statement needs to complete first). As + with most other databases, statements are not complete + until all the results pending on the statement are read + or the active result set for the statement is closed. + Therefore, if using streaming results, process them as + quickly as possible if you want to maintain concurrent + access to the tables referenced by the statement + producing the result set. + + * ResultSetMetaData + The isAutoIncrement() method only works when using MySQL + servers 4.0 and newer. + + * Statement + When using versions of the JDBC driver earlier than + 3.2.1, and connected to server versions earlier than + 5.0.3, the setFetchSize() method has no effect, other + than to toggle result set streaming as described above. + Connector/J 5.0.0 and later include support for both + Statement.cancel() and Statement.setQueryTimeout(). Both + require MySQL 5.0.0 or newer server, and require a + separate connection to issue the KILL QUERY + (http://dev.mysql.com/doc/refman/5.7/en/kill.html) + statement. In the case of setQueryTimeout(), the + implementation creates an additional thread to handle the + timeout functionality. + Note + Failures to cancel the statement for setQueryTimeout() + may manifest themselves as RuntimeException rather than + failing silently, as there is currently no way to unblock + the thread that is executing the query being cancelled + due to timeout expiration and have it throw the exception + instead. + Note + The MySQL statement KILL QUERY + (http://dev.mysql.com/doc/refman/5.7/en/kill.html) (which + is what the driver uses to implement Statement.cancel()) + is non-deterministic; thus, avoid the use of + Statement.cancel() if possible. If no query is in + process, the next query issued will be killed by the + server. This race condition is guarded against as of + Connector/J 5.1.18. + MySQL does not support SQL cursors, and the JDBC driver + doesn't emulate them, so setCursorName() has no effect. + Connector/J 5.1.3 and later include two additional + methods: + + + setLocalInfileInputStream() sets an InputStream + instance that will be used to send data to the MySQL + server for a LOAD DATA LOCAL INFILE + (http://dev.mysql.com/doc/refman/5.7/en/load-data.ht + ml) statement rather than a FileInputStream or + URLInputStream that represents the path given as an + argument to the statement. + This stream will be read to completion upon + execution of a LOAD DATA LOCAL INFILE + (http://dev.mysql.com/doc/refman/5.7/en/load-data.ht + ml) statement, and will automatically be closed by + the driver, so it needs to be reset before each call + to execute*() that would cause the MySQL server to + request data to fulfill the request for LOAD DATA + LOCAL INFILE + (http://dev.mysql.com/doc/refman/5.7/en/load-data.ht + ml). + If this value is set to NULL, the driver will revert + to using a FileInputStream or URLInputStream as + required. + + + getLocalInfileInputStream() returns the InputStream + instance that will be used to send data in response + to a LOAD DATA LOCAL INFILE + (http://dev.mysql.com/doc/refman/5.7/en/load-data.ht + ml) statement. + This method returns NULL if no such stream has been + set using setLocalInfileInputStream(). + +5.3 Java, JDBC and MySQL Types + + MySQL Connector/J is flexible in the way it handles + conversions between MySQL data types and Java data types. + + In general, any MySQL data type can be converted to a + java.lang.String, and any numeric type can be converted to + any of the Java numeric types, although round-off, overflow, + or loss of precision may occur. + Note + + All TEXT types return Types.LONGVARCHAR with different + getPrecision() values (65535, 255, 16777215, and 2147483647 + respectively) with getColumnType() returning -1. This + behavior is intentional even though TINYTEXT does not fall, + regarding to its size, within the LONGVARCHAR category. This + is to avoid different handling inside the same base type. And + getColumnType() returns -1 because the internal server + handling is of type TEXT, which is similar to BLOB. + + Also note that getColumnTypeName() will return VARCHAR even + though getColumnType() returns Types.LONGVARCHAR, because + VARCHAR is the designated column database-specific name for + this type. + + Starting with Connector/J 3.1.0, the JDBC driver issues + warnings or throws DataTruncation exceptions as is required + by the JDBC specification unless the connection was + configured not to do so by using the property + jdbcCompliantTruncation and setting it to false. + + The conversions that are always guaranteed to work are listed + in the following table. The first column lists one or more + MySQL data types, and the second column lists one or more + Java types to which the MySQL types can be converted. + + Table 5.1 Connection Properties - Miscellaneous + These MySQL Data Types Can always be converted to these Java + types + CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String, + java.io.InputStream, java.io.Reader, java.sql.Blob, + java.sql.Clob + FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, + SMALLINT, MEDIUMINT, INTEGER, BIGINT java.lang.String, + java.lang.Short, java.lang.Integer, java.lang.Long, + java.lang.Double, java.math.BigDecimal + DATE, TIME, DATETIME, TIMESTAMP java.lang.String, + java.sql.Date, java.sql.Timestamp + Note + + Round-off, overflow or loss of precision may occur if you + choose a Java numeric data type that has less precision or + capacity than the MySQL data type you are converting to/from. + + The ResultSet.getObject() method uses the type conversions + between MySQL and Java types, following the JDBC + specification where appropriate. The value returned by + ResultSetMetaData.GetColumnClassName() is also shown below. + For more information on the JDBC types, see the reference on + the java.sql.Types + (http://docs.oracle.com/javase/8/docs/api/java/sql/Types.html + ) class. + + Table 5.2 MySQL Types to Java Types for ResultSet.getObject() + MySQL Type Name Return value of GetColumnClassName Returned + as Java Class + BIT(1) (new in MySQL-5.0) BIT java.lang.Boolean + BIT( > 1) (new in MySQL-5.0) BIT byte[] + TINYINT TINYINT java.lang.Boolean if the configuration + property tinyInt1isBit is set to true (the default) and the + storage size is 1, or java.lang.Integer if not. + BOOL, BOOLEAN TINYINT See TINYINT, above as these are aliases + for TINYINT(1), currently. + SMALLINT[(M)] [UNSIGNED] SMALLINT [UNSIGNED] + java.lang.Integer (regardless if UNSIGNED or not) + MEDIUMINT[(M)] [UNSIGNED] MEDIUMINT [UNSIGNED] + java.lang.Integer, if UNSIGNED java.lang.Long (C/J 3.1 and + earlier), or java.lang.Integer for C/J 5.0 and later + INT,INTEGER[(M)] [UNSIGNED] INTEGER [UNSIGNED] + java.lang.Integer, if UNSIGNED java.lang.Long + BIGINT[(M)] [UNSIGNED] BIGINT [UNSIGNED] java.lang.Long, if + UNSIGNED java.math.BigInteger + FLOAT[(M,D)] FLOAT java.lang.Float + DOUBLE[(M,B)] DOUBLE java.lang.Double + DECIMAL[(M[,D])] DECIMAL java.math.BigDecimal + DATE DATE java.sql.Date + DATETIME DATETIME java.sql.Timestamp + TIMESTAMP[(M)] TIMESTAMP java.sql.Timestamp + TIME TIME java.sql.Time + YEAR[(2|4)] YEAR If yearIsDateType configuration property is + set to false, then the returned object type is + java.sql.Short. If set to true (the default), then the + returned object is of type java.sql.Date with the date set to + January 1st, at midnight. + CHAR(M) CHAR java.lang.String (unless the character set for + the column is BINARY, then byte[] is returned. + VARCHAR(M) [BINARY] VARCHAR java.lang.String (unless the + character set for the column is BINARY, then byte[] is + returned. + BINARY(M) BINARY byte[] + VARBINARY(M) VARBINARY byte[] + TINYBLOB TINYBLOB byte[] + TINYTEXT VARCHAR java.lang.String + BLOB BLOB byte[] + TEXT VARCHAR java.lang.String + MEDIUMBLOB MEDIUMBLOB byte[] + MEDIUMTEXT VARCHAR java.lang.String + LONGBLOB LONGBLOB byte[] + LONGTEXT VARCHAR java.lang.String + ENUM('value1','value2',...) CHAR java.lang.String + SET('value1','value2',...) CHAR java.lang.String + +5.4 Using Character Sets and Unicode + + All strings sent from the JDBC driver to the server are + converted automatically from native Java Unicode form to the + client character encoding, including all queries sent using + Statement.execute(), Statement.executeUpdate(), + Statement.executeQuery() as well as all PreparedStatement and + CallableStatement parameters with the exclusion of parameters + set using setBytes(), setBinaryStream(), setAsciiStream(), + setUnicodeStream() and setBlob(). + +Number of Encodings Per Connection + + In MySQL Server 4.1 and higher, Connector/J supports a single + character encoding between client and server, and any number + of character encodings for data returned by the server to the + client in ResultSets. + + Prior to MySQL Server 4.1, Connector/J supported a single + character encoding per connection, which could either be + automatically detected from the server configuration, or + could be configured by the user through the useUnicode and + characterEncoding properties. + +Setting the Character Encoding + + The character encoding between client and server is + automatically detected upon connection. You specify the + encoding on the server using the character_set_server + (http://dev.mysql.com/doc/refman/5.7/en/server-system-variabl + es.html#sysvar_character_set_server) for server versions + 4.1.0 and newer, and character_set system variable for server + versions older than 4.1.0. The driver automatically uses the + encoding specified by the server. For more information, see + Server Character Set and Collation + (http://dev.mysql.com/doc/refman/5.7/en/charset-server.html). + + For example, to use 4-byte UTF-8 character sets with + Connector/J, configure the MySQL server with + character_set_server=utf8mb4 + (http://dev.mysql.com/doc/refman/5.7/en/server-system-variabl + es.html#sysvar_character_set_server), and leave + characterEncoding out of the Connector/J connection string. + Connector/J will then autodetect the UTF-8 setting. + + To override the automatically detected encoding on the client + side, use the characterEncoding property in the URL used to + connect to the server. + + To allow multiple character sets to be sent from the client, + use the UTF-8 encoding, either by configuring utf8 as the + default server character set, or by configuring the JDBC + driver to use UTF-8 through the characterEncoding property. + + When specifying character encodings on the client side, use + Java-style names. The following table lists MySQL character + set names and the corresponding Java-style names: + + Table 5.3 MySQL to Java Encoding Name Translations + MySQL Character Set Name Java-Style Character Encoding Name + ascii US-ASCII + big5 Big5 + gbk GBK + sjis SJIS (or Cp932 or MS932 for MySQL Server < 4.1.11) + cp932 Cp932 or MS932 (MySQL Server > 4.1.11) + gb2312 EUC_CN + ujis EUC_JP + euckr EUC_KR + latin1 Cp1252 + latin2 ISO8859_2 + greek ISO8859_7 + hebrew ISO8859_8 + cp866 Cp866 + tis620 TIS620 + cp1250 Cp1250 + cp1251 Cp1251 + cp1257 Cp1257 + macroman MacRoman + macce MacCentralEurope + utf8 UTF-8 + ucs2 UnicodeBig + Warning + + Do not issue the query set names with Connector/J, as the + driver will not detect that the character set has changed, + and will continue to use the character set detected during + the initial connection setup. + +5.5 Connecting Securely Using SSL + + SSL in MySQL Connector/J encrypts all data (other than the + initial handshake) between the JDBC driver and the server. + There is a performance penalty for enabling SSL, the severity + of which depends on multiple factors including (but not + limited to) the size of the query, the amount of data + returned, the server hardware, the SSL library used, the + network bandwidth, and so on. + + For SSL support to work, you must have the following: + + * A JDK that includes JSSE (Java Secure Sockets Extension), + like JDK-1.4.1 or newer. SSL does not currently work with + a JDK that you can add JSSE to, like JDK-1.2.x or + JDK-1.3.x due to the following JSSE bug: + http://bugs.java.com/bugdatabase/view_bug.do?bug_id=42735 + 44 + + * A MySQL server that supports SSL and has been compiled + and configured to do so, which is MySQL 4.0.4 or later. + For more information, see Building MySQL with Support for + Secure Connections + (http://dev.mysql.com/doc/refman/5.7/en/building-with-sec + ure-connection-support.html). + + * A client certificate (covered later in this section) + + The system works through two Java truststore files, one file + contains the certificate information for the server + (truststore in the examples below). The other file contains + the certificate for the client (keystore in the examples + below). All Java truststore files are password protected by + supplying a suitable password to the keytool when you create + the files. You need the file names and associated passwords + to create an SSL connection. + + You will first need to import the MySQL server CA Certificate + into a Java truststore. A sample MySQL server CA Certificate + is located in the SSL subdirectory of the MySQL source + distribution. This is what SSL will use to determine if you + are communicating with a secure MySQL server. Alternatively, + use the CA Certificate that you have generated or been + provided with by your SSL provider. + + To use Java's keytool to create a truststore in the current + directory , and import the server's CA certificate + (cacert.pem), you can do the following (assuming that keytool + is in your path. The keytool is typically located in the bin + subdirectory of your JDK or JRE): +shell> keytool -import -alias mysqlServerCACert \ + -file cacert.pem -keystore truststore + + Enter the password when prompted for the keystore file. + Interaction with keytool looks like this: +Enter keystore password: ********* +Owner: EMAILADDRESS=walrus@example.com, CN=Walrus, + O=My Company, L=Orenburg, ST=Some-State, C=RU +Issuer: EMAILADDRESS=walrus@example.com, CN=Walrus, + O=My Company, L=Orenburg, ST=Some-State, C=RU +Serial number: 0 +Valid from: + Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003 +Certificate fingerprints: + MD5: 61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB + SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C +Trust this certificate? [no]: yes +Certificate was added to keystore + + You then have two options: either import the client + certificate that matches the CA certificate you just + imported, or create a new client certificate. + + Importing an existing certificate requires the certificate to + be in DER format. You can use openssl to convert an existing + certificate into the new format. For example: +shell> openssl x509 -outform DER -in client-cert.pem -out client.cert + + Now import the converted certificate into your keystore using + keytool: +shell> keytool -import -file client.cert -keystore keystore -alias mys +qlClientCertificate + + To generate your own client certificate, use keytool to + create a suitable certificate and add it to the keystore + file: +shell> keytool -genkey -keyalg rsa \ + -alias mysqlClientCertificate -keystore keystore + + Keytool will prompt you for the following information, and + create a keystore named keystore in the current directory. + + Respond with information that is appropriate for your + situation: +Enter keystore password: ********* +What is your first and last name? + [Unknown]: Matthews +What is the name of your organizational unit? + [Unknown]: Software Development +What is the name of your organization? + [Unknown]: My Company +What is the name of your City or Locality? + [Unknown]: Flossmoor +What is the name of your State or Province? + [Unknown]: IL +What is the two-letter country code for this unit? + [Unknown]: US +Is correct? + [no]: y + +Enter key password for + (RETURN if same as keystore password): + + Finally, to get JSSE to use the keystore and truststore that + you have generated, you need to set the following system + properties when you start your JVM, replacing + path_to_keystore_file with the full path to the keystore file + you created, path_to_truststore_file with the path to the + truststore file you created, and using the appropriate + password values for each property. You can do this either on + the command line: +-Djavax.net.ssl.keyStore=path_to_keystore_file +-Djavax.net.ssl.keyStorePassword=password +-Djavax.net.ssl.trustStore=path_to_truststore_file +-Djavax.net.ssl.trustStorePassword=password + + Or you can set the values directly within the application: +System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file"); +System.setProperty("javax.net.ssl.keyStorePassword","password"); +System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file +"); +System.setProperty("javax.net.ssl.trustStorePassword","password"); + + You will also need to set useSSL to true in your connection + parameters for MySQL Connector/J, either by adding + useSSL=true to your URL, or by setting the property useSSL to + true in the java.util.Properties instance you pass to + DriverManager.getConnection(). + + You can test that SSL is working by turning on JSSE debugging + (as detailed below), and look for the following key events: +... +*** ClientHello, v3.1 +RandomCookie: GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, +?? + 54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, +?? + 217, 219, 239, 202, 19, 121, 78 } +Session ID: {} +Cipher Suites: { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 } +Compression Methods: { 0 } +*** +[write] MD5 and SHA1 hashes: len = 59 +0000: 01 00 00 37 03 01 3D B6 90 FA C7 94 B4 D7 4A 0C ...7..=.......J +. +0010: 36 F4 00 A8 37 67 D7 40 10 8A E1 BE 84 99 02 D9 6...7g.@....... +. +0020: DB EF CA 13 79 4E 00 00 10 00 05 00 04 00 09 00 ....yN......... +. +0030: 0A 00 12 00 13 00 03 00 11 01 00 ........... +main, WRITE: SSL v3.1 Handshake, length = 59 +main, READ: SSL v3.1 Handshake, length = 74 +*** ServerHello, v3.1 +RandomCookie: GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58, + ?? + 202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3, + ?? + 132, 110, 82, 148, 160, 92 } +Session ID: {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, +?? + 182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, +?? + 219, 158, 177, 187, 143} +Cipher Suite: { 0, 5 } +Compression Method: 0 +*** +%% Created: [Session-1, SSL_RSA_WITH_RC4_128_SHA] +** SSL_RSA_WITH_RC4_128_SHA +[read] MD5 and SHA1 hashes: len = 74 +0000: 02 00 00 46 03 01 3D B6 43 98 74 32 04 67 19 64 ...F..=.C.t2.g. +d +0010: 3A CA 4F B9 B2 64 D7 42 FE 15 53 BB BE 2A AA 03 :.O..d.B..S..*. +. +0020: 84 6E 52 94 A0 5C 20 A3 E3 54 35 51 7F FC FE B2 .nR..\ ..T5Q... +. +0030: B3 44 3F B6 9E 1E 0B 96 4F AA 4C FF 5C 0F E2 18 .D?.....O.L.\.. +. +0040: 11 B1 DB 9E B1 BB 8F 00 05 00 .......... +main, READ: SSL v3.1 Handshake, length = 1712 +... + + JSSE provides debugging (to stdout) when you set the + following system property: -Djavax.net.debug=all This will + tell you what keystores and truststores are being used, as + well as what is going on during the SSL handshake and + certificate exchange. It will be helpful when trying to + determine what is not working when trying to get an SSL + connection to happen. + +5.6 Connecting Using PAM Authentication + + Java applications using Connector/J 5.1.21 and higher can + connect to MySQL servers that use the pluggable + authentication module (PAM) authentication scheme. + + For PAM authentication to work, you must have the following: + + * A MySQL server that supports PAM authentication: a + commercial distribution of MySQL 5.5.16 or higher. See + The PAM Authentication Plugin + (http://dev.mysql.com/doc/refman/5.7/en/pam-authenticatio + n-plugin.html) for more information. Connector/J + implements the same cleartext authentication method as in + The Cleartext Client-Side Authentication Plugin + (http://dev.mysql.com/doc/refman/5.7/en/cleartext-authent + ication-plugin.html). + + * SSL capability, as explained in Section 5.5, "Connecting + Securely Using SSL." Because the PAM authentication + scheme sends the original password to the server, the + connection to the server must be encrypted. + + PAM authentication support is enabled by default in + Connector/J 5.1.21 and up, so no extra configuration is + needed. + + To disable the PAM authentication feature, specify + mysql_clear_password (the method) or + com.mysql.jdbc.authentication.MysqlClearPasswordPlugin (the + class name) in the comma-separated list of arguments for the + disabledAuthenticationPlugins connection option. See Section + 5.1, "Driver/Datasource Class Names, URL Syntax and + Configuration Properties for Connector/J" for details about + that connection option. + +5.7 Using Master/Slave Replication with ReplicationConnection + + See Section 8.3, "Configuring Master/Slave Replication with + Connector/J" for details on the topic. + +5.8 Mapping MySQL Error Numbers to JDBC SQLState Codes + + The table below provides a mapping of the MySQL error numbers + to JDBC SQLState values. + + Table 5.4 Mapping of MySQL Error Numbers to SQLStates + MySQL Error Number MySQL Error Name Legacy (X/Open) SQLState + SQL Standard SQLState + 1022 ER_DUP_KEY 23000 23000 + 1037 ER_OUTOFMEMORY S1001 HY001 + 1038 ER_OUT_OF_SORTMEMORY S1001 HY001 + 1040 ER_CON_COUNT_ERROR 08004 08004 + 1042 ER_BAD_HOST_ERROR 08004 08S01 + 1043 ER_HANDSHAKE_ERROR 08004 08S01 + 1044 ER_DBACCESS_DENIED_ERROR 42000 42000 + 1045 ER_ACCESS_DENIED_ERROR 28000 28000 + 1046 ER_NO_DB_ERROR 3D000 3D000 + 1047 ER_UNKNOWN_COM_ERROR 08S01 08S01 + 1048 ER_BAD_NULL_ERROR 23000 23000 + 1049 ER_BAD_DB_ERROR 42000 42000 + 1050 ER_TABLE_EXISTS_ERROR 42S01 42S01 + 1051 ER_BAD_TABLE_ERROR 42S02 42S02 + 1052 ER_NON_UNIQ_ERROR 23000 23000 + 1053 ER_SERVER_SHUTDOWN 08S01 08S01 + 1054 ER_BAD_FIELD_ERROR S0022 42S22 + 1055 ER_WRONG_FIELD_WITH_GROUP S1009 42000 + 1056 ER_WRONG_GROUP_FIELD S1009 42000 + 1057 ER_WRONG_SUM_SELECT S1009 42000 + 1058 ER_WRONG_VALUE_COUNT 21S01 21S01 + 1059 ER_TOO_LONG_IDENT S1009 42000 + 1060 ER_DUP_FIELDNAME S1009 42S21 + 1061 ER_DUP_KEYNAME S1009 42000 + 1062 ER_DUP_ENTRY S1009 23000 + 1063 ER_WRONG_FIELD_SPEC S1009 42000 + 1064 ER_PARSE_ERROR 42000 42000 + 1065 ER_EMPTY_QUERY 42000 42000 + 1066 ER_NONUNIQ_TABLE S1009 42000 + 1067 ER_INVALID_DEFAULT S1009 42000 + 1068 ER_MULTIPLE_PRI_KEY S1009 42000 + 1069 ER_TOO_MANY_KEYS S1009 42000 + 1070 ER_TOO_MANY_KEY_PARTS S1009 42000 + 1071 ER_TOO_LONG_KEY S1009 42000 + 1072 ER_KEY_COLUMN_DOES_NOT_EXITS S1009 42000 + 1073 ER_BLOB_USED_AS_KEY S1009 42000 + 1074 ER_TOO_BIG_FIELDLENGTH S1009 42000 + 1075 ER_WRONG_AUTO_KEY S1009 42000 + 1080 ER_FORCING_CLOSE 08S01 08S01 + 1081 ER_IPSOCK_ERROR 08S01 08S01 + 1082 ER_NO_SUCH_INDEX S1009 42S12 + 1083 ER_WRONG_FIELD_TERMINATORS S1009 42000 + 1084 ER_BLOBS_AND_NO_TERMINATED S1009 42000 + 1090 ER_CANT_REMOVE_ALL_FIELDS 42000 42000 + 1091 ER_CANT_DROP_FIELD_OR_KEY 42000 42000 + 1101 ER_BLOB_CANT_HAVE_DEFAULT 42000 42000 + 1102 ER_WRONG_DB_NAME 42000 42000 + 1103 ER_WRONG_TABLE_NAME 42000 42000 + 1104 ER_TOO_BIG_SELECT 42000 42000 + 1106 ER_UNKNOWN_PROCEDURE 42000 42000 + 1107 ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000 42000 + 1109 ER_UNKNOWN_TABLE 42S02 42S02 + 1110 ER_FIELD_SPECIFIED_TWICE 42000 42000 + 1112 ER_UNSUPPORTED_EXTENSION 42000 42000 + 1113 ER_TABLE_MUST_HAVE_COLUMNS 42000 42000 + 1115 ER_UNKNOWN_CHARACTER_SET 42000 42000 + 1118 ER_TOO_BIG_ROWSIZE 42000 42000 + 1120 ER_WRONG_OUTER_JOIN 42000 42000 + 1121 ER_NULL_COLUMN_IN_INDEX 42000 42000 + 1129 ER_HOST_IS_BLOCKED 08004 HY000 + 1130 ER_HOST_NOT_PRIVILEGED 08004 HY000 + 1131 ER_PASSWORD_ANONYMOUS_USER 42000 42000 + 1132 ER_PASSWORD_NOT_ALLOWED 42000 42000 + 1133 ER_PASSWORD_NO_MATCH 42000 42000 + 1136 ER_WRONG_VALUE_COUNT_ON_ROW 21S01 21S01 + 1138 ER_INVALID_USE_OF_NULL S1000 42000 + 1139 ER_REGEXP_ERROR 42000 42000 + 1140 ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000 42000 + 1141 ER_NONEXISTING_GRANT 42000 42000 + 1142 ER_TABLEACCESS_DENIED_ERROR 42000 42000 + 1143 ER_COLUMNACCESS_DENIED_ERROR 42000 42000 + 1144 ER_ILLEGAL_GRANT_FOR_TABLE 42000 42000 + 1145 ER_GRANT_WRONG_HOST_OR_USER 42000 42000 + 1146 ER_NO_SUCH_TABLE 42S02 42S02 + 1147 ER_NONEXISTING_TABLE_GRANT 42000 42000 + 1148 ER_NOT_ALLOWED_COMMAND 42000 42000 + 1149 ER_SYNTAX_ERROR 42000 42000 + 1152 ER_ABORTING_CONNECTION 08S01 08S01 + 1153 ER_NET_PACKET_TOO_LARGE 08S01 08S01 + 1154 ER_NET_READ_ERROR_FROM_PIPE 08S01 08S01 + 1155 ER_NET_FCNTL_ERROR 08S01 08S01 + 1156 ER_NET_PACKETS_OUT_OF_ORDER 08S01 08S01 + 1157 ER_NET_UNCOMPRESS_ERROR 08S01 08S01 + 1158 ER_NET_READ_ERROR 08S01 08S01 + 1159 ER_NET_READ_INTERRUPTED 08S01 08S01 + 1160 ER_NET_ERROR_ON_WRITE 08S01 08S01 + 1161 ER_NET_WRITE_INTERRUPTED 08S01 08S01 + 1162 ER_TOO_LONG_STRING 42000 42000 + 1163 ER_TABLE_CANT_HANDLE_BLOB 42000 42000 + 1164 ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000 42000 + 1166 ER_WRONG_COLUMN_NAME 42000 42000 + 1167 ER_WRONG_KEY_COLUMN 42000 42000 + 1169 ER_DUP_UNIQUE 23000 23000 + 1170 ER_BLOB_KEY_WITHOUT_LENGTH 42000 42000 + 1171 ER_PRIMARY_CANT_HAVE_NULL 42000 42000 + 1172 ER_TOO_MANY_ROWS 42000 42000 + 1173 ER_REQUIRES_PRIMARY_KEY 42000 42000 + 1176 ER_KEY_DOES_NOT_EXITS 42000 42000 + 1177 ER_CHECK_NO_SUCH_TABLE 42000 42000 + 1178 ER_CHECK_NOT_IMPLEMENTED 42000 42000 + 1179 ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000 25000 + 1184 ER_NEW_ABORTING_CONNECTION 08S01 08S01 + 1189 ER_MASTER_NET_READ 08S01 08S01 + 1190 ER_MASTER_NET_WRITE 08S01 08S01 + 1203 ER_TOO_MANY_USER_CONNECTIONS 42000 42000 + 1205 ER_LOCK_WAIT_TIMEOUT 40001 40001 + 1207 ER_READ_ONLY_TRANSACTION 25000 25000 + 1211 ER_NO_PERMISSION_TO_CREATE_USER 42000 42000 + 1213 ER_LOCK_DEADLOCK 40001 40001 + 1216 ER_NO_REFERENCED_ROW 23000 23000 + 1217 ER_ROW_IS_REFERENCED 23000 23000 + 1218 ER_CONNECT_TO_MASTER 08S01 08S01 + 1222 ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 21000 21000 + 1226 ER_USER_LIMIT_REACHED 42000 42000 + 1227 ER_SPECIFIC_ACCESS_DENIED_ERROR 42000 42000 + 1230 ER_NO_DEFAULT 42000 42000 + 1231 ER_WRONG_VALUE_FOR_VAR 42000 42000 + 1232 ER_WRONG_TYPE_FOR_VAR 42000 42000 + 1234 ER_CANT_USE_OPTION_HERE 42000 42000 + 1235 ER_NOT_SUPPORTED_YET 42000 42000 + 1239 ER_WRONG_FK_DEF 42000 42000 + 1241 ER_OPERAND_COLUMNS 21000 21000 + 1242 ER_SUBQUERY_NO_1_ROW 21000 21000 + 1247 ER_ILLEGAL_REFERENCE 42S22 42S22 + 1248 ER_DERIVED_MUST_HAVE_ALIAS 42000 42000 + 1249 ER_SELECT_REDUCED 01000 01000 + 1250 ER_TABLENAME_NOT_ALLOWED_HERE 42000 42000 + 1251 ER_NOT_SUPPORTED_AUTH_MODE 08004 08004 + 1252 ER_SPATIAL_CANT_HAVE_NULL 42000 42000 + 1253 ER_COLLATION_CHARSET_MISMATCH 42000 42000 + 1261 ER_WARN_TOO_FEW_RECORDS 01000 01000 + 1262 ER_WARN_TOO_MANY_RECORDS 01000 01000 + 1263 ER_WARN_NULL_TO_NOTNULL S1000 01000 + 1264 ER_WARN_DATA_OUT_OF_RANGE 01000 01000 + 1265 ER_WARN_DATA_TRUNCATED 01000 01000 + 1280 ER_WRONG_NAME_FOR_INDEX 42000 42000 + 1281 ER_WRONG_NAME_FOR_CATALOG 42000 42000 + 1286 ER_UNKNOWN_STORAGE_ENGINE 42000 42000 + 1292 ER_TRUNCATED_WRONG_VALUE 22007 22007 + 1303 ER_SP_NO_RECURSIVE_CREATE S1000 2F003 + 1304 ER_SP_ALREADY_EXISTS 42000 42000 + 1305 ER_SP_DOES_NOT_EXIST 42000 42000 + 1308 ER_SP_LILABEL_MISMATCH 42000 42000 + 1309 ER_SP_LABEL_REDEFINE 42000 42000 + 1310 ER_SP_LABEL_MISMATCH 42000 42000 + 1311 ER_SP_UNINIT_VAR 01000 01000 + 1312 ER_SP_BADSELECT 0A000 0A000 + 1313 ER_SP_BADRETURN 42000 42000 + 1314 ER_SP_BADSTATEMENT 0A000 0A000 + 1315 ER_UPDATE_LOG_DEPRECATED_IGNORED 42000 42000 + 1316 ER_UPDATE_LOG_DEPRECATED_TRANSLATED 42000 42000 + 1317 ER_QUERY_INTERRUPTED S1000 70100 + 1318 ER_SP_WRONG_NO_OF_ARGS 42000 42000 + 1319 ER_SP_COND_MISMATCH 42000 42000 + 1320 ER_SP_NORETURN 42000 42000 + 1321 ER_SP_NORETURNEND S1000 2F005 + 1322 ER_SP_BAD_CURSOR_QUERY 42000 42000 + 1323 ER_SP_BAD_CURSOR_SELECT 42000 42000 + 1324 ER_SP_CURSOR_MISMATCH 42000 42000 + 1325 ER_SP_CURSOR_ALREADY_OPEN 24000 24000 + 1326 ER_SP_CURSOR_NOT_OPEN 24000 24000 + 1327 ER_SP_UNDECLARED_VAR 42000 42000 + 1329 ER_SP_FETCH_NO_DATA S1000 02000 + 1330 ER_SP_DUP_PARAM 42000 42000 + 1331 ER_SP_DUP_VAR 42000 42000 + 1332 ER_SP_DUP_COND 42000 42000 + 1333 ER_SP_DUP_CURS 42000 42000 + 1335 ER_SP_SUBSELECT_NYI 0A000 0A000 + 1336 ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 0A000 0A000 + 1337 ER_SP_VARCOND_AFTER_CURSHNDLR 42000 42000 + 1338 ER_SP_CURSOR_AFTER_HANDLER 42000 42000 + 1339 ER_SP_CASE_NOT_FOUND S1000 20000 + 1365 ER_DIVISION_BY_ZERO 22012 22012 + 1367 ER_ILLEGAL_VALUE_FOR_TYPE 22007 22007 + 1370 ER_PROCACCESS_DENIED_ERROR 42000 42000 + 1397 ER_XAER_NOTA S1000 XAE04 + 1398 ER_XAER_INVAL S1000 XAE05 + 1399 ER_XAER_RMFAIL S1000 XAE07 + 1400 ER_XAER_OUTSIDE S1000 XAE09 + 1401 ER_XA_RMERR S1000 XAE03 + 1402 ER_XA_RBROLLBACK S1000 XA100 + 1403 ER_NONEXISTING_PROC_GRANT 42000 42000 + 1406 ER_DATA_TOO_LONG 22001 22001 + 1407 ER_SP_BAD_SQLSTATE 42000 42000 + 1410 ER_CANT_CREATE_USER_WITH_GRANT 42000 42000 + 1413 ER_SP_DUP_HANDLER 42000 42000 + 1414 ER_SP_NOT_VAR_ARG 42000 42000 + 1415 ER_SP_NO_RETSET 0A000 0A000 + 1416 ER_CANT_CREATE_GEOMETRY_OBJECT 22003 22003 + 1425 ER_TOO_BIG_SCALE 42000 42000 + 1426 ER_TOO_BIG_PRECISION 42000 42000 + 1427 ER_M_BIGGER_THAN_D 42000 42000 + 1437 ER_TOO_LONG_BODY 42000 42000 + 1439 ER_TOO_BIG_DISPLAYWIDTH 42000 42000 + 1440 ER_XAER_DUPID S1000 XAE08 + 1441 ER_DATETIME_FUNCTION_OVERFLOW 22008 22008 + 1451 ER_ROW_IS_REFERENCED_2 23000 23000 + 1452 ER_NO_REFERENCED_ROW_2 23000 23000 + 1453 ER_SP_BAD_VAR_SHADOW 42000 42000 + 1458 ER_SP_WRONG_NAME 42000 42000 + 1460 ER_SP_NO_AGGREGATE 42000 42000 + 1461 ER_MAX_PREPARED_STMT_COUNT_REACHED 42000 42000 + 1463 ER_NON_GROUPING_FIELD_USED 42000 42000 + 1557 ER_FOREIGN_DUPLICATE_KEY 23000 23000 + 1568 ER_CANT_CHANGE_TX_ISOLATION S1000 25001 + 1582 ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT 42000 42000 + 1583 ER_WRONG_PARAMETERS_TO_NATIVE_FCT 42000 42000 + 1584 ER_WRONG_PARAMETERS_TO_STORED_FCT 42000 42000 + 1586 ER_DUP_ENTRY_WITH_KEY_NAME 23000 23000 + 1613 ER_XA_RBTIMEOUT S1000 XA106 + 1614 ER_XA_RBDEADLOCK S1000 XA102 + 1630 ER_FUNC_INEXISTENT_NAME_COLLISION 42000 42000 + 1641 ER_DUP_SIGNAL_SET 42000 42000 + 1642 ER_SIGNAL_WARN 01000 01000 + 1643 ER_SIGNAL_NOT_FOUND S1000 02000 + 1645 ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER S1000 0K000 + 1687 ER_SPATIAL_MUST_HAVE_GEOM_COL 42000 42000 + 1690 ER_DATA_OUT_OF_RANGE 22003 22003 + 1698 ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000 28000 + 1701 ER_TRUNCATE_ILLEGAL_FK 42000 42000 + 1758 ER_DA_INVALID_CONDITION_NUMBER 35000 35000 + 1761 ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO 23000 23000 + 1762 ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO 23000 23000 + 1792 ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION S1000 25006 + 1845 ER_ALTER_OPERATION_NOT_SUPPORTED 0A000 0A000 + 1846 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON 0A000 0A000 + 1859 ER_DUP_UNKNOWN_IN_INDEX 23000 23000 + 1873 ER_ACCESS_DENIED_CHANGE_USER_ERROR 28000 28000 + 1887 ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER S1000 0Z002 + 1903 ER_INVALID_ARGUMENT_FOR_LOGARITHM S1000 2201E + +Chapter 6 JDBC Concepts + + This section provides some general JDBC background. + +6.1 Connecting to MySQL Using the JDBC DriverManager Interface + + When you are using JDBC outside of an application server, the + DriverManager class manages the establishment of connections. + + Specify to the DriverManager which JDBC drivers to try to + make Connections with. The easiest way to do this is to use + Class.forName() on the class that implements the + java.sql.Driver interface. With MySQL Connector/J, the name + of this class is com.mysql.jdbc.Driver. With this method, you + could use an external configuration file to supply the driver + class name and driver parameters to use when connecting to a + database. + + The following section of Java code shows how you might + register MySQL Connector/J from the main() method of your + application. If testing this code, first read the + installation section at Chapter 3, "Connector/J + Installation," to make sure you have connector installed + correctly and the CLASSPATH set up. Also, ensure that MySQL + is configured to accept external TCP/IP connections. +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +// Notice, do not import com.mysql.jdbc.* +// or you will have problems! + +public class LoadDriver { + public static void main(String[] args) { + try { + // The newInstance() call is a work around for some + // broken Java implementations + + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } catch (Exception ex) { + // handle the error + } + } +} + + After the driver has been registered with the DriverManager, + you can obtain a Connection instance that is connected to a + particular database by calling DriverManager.getConnection(): + + Example 6.1 Connector/J: Obtaining a connection from the + DriverManager + + If you have not already done so, please review the portion of + Section 6.1, "Connecting to MySQL Using the JDBC + DriverManager Interface" above before working with the + example below. + + This example shows how you can obtain a Connection instance + from the DriverManager. There are a few different signatures + for the getConnection() method. Consult the API documentation + that comes with your JDK for more specific information on how + to use them. +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +Connection conn = null; +... +try { + conn = + DriverManager.getConnection("jdbc:mysql://localhost/test?" + + "user=minty&password=greatsqldb"); + + // Do something with the Connection + + ... +} catch (SQLException ex) { + // handle any errors + System.out.println("SQLException: " + ex.getMessage()); + System.out.println("SQLState: " + ex.getSQLState()); + System.out.println("VendorError: " + ex.getErrorCode()); +} + + Once a Connection is established, it can be used to create + Statement and PreparedStatement objects, as well as retrieve + metadata about the database. This is explained in the + following sections. + +6.2 Using JDBC Statement Objects to Execute SQL + + Statement objects allow you to execute basic SQL queries and + retrieve the results through the ResultSet class, which is + described later. + + To create a Statement instance, you call the + createStatement() method on the Connection object you have + retrieved using one of the DriverManager.getConnection() or + DataSource.getConnection() methods described earlier. + + Once you have a Statement instance, you can execute a SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) query by + calling the executeQuery(String) method with the SQL you want + to use. + + To update data in the database, use the executeUpdate(String + SQL) method. This method returns the number of rows matched + by the update statement, not the number of rows that were + modified. + + If you do not know ahead of time whether the SQL statement + will be a SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) or an + UPDATE + (http://dev.mysql.com/doc/refman/5.7/en/update.html)/INSERT + (http://dev.mysql.com/doc/refman/5.7/en/insert.html), then + you can use the execute(String SQL) method. This method will + return true if the SQL query was a SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html), or + false if it was an UPDATE + (http://dev.mysql.com/doc/refman/5.7/en/update.html), INSERT + (http://dev.mysql.com/doc/refman/5.7/en/insert.html), or + DELETE (http://dev.mysql.com/doc/refman/5.7/en/delete.html) + statement. If the statement was a SELECT + (http://dev.mysql.com/doc/refman/5.7/en/select.html) query, + you can retrieve the results by calling the getResultSet() + method. If the statement was an UPDATE + (http://dev.mysql.com/doc/refman/5.7/en/update.html), INSERT + (http://dev.mysql.com/doc/refman/5.7/en/insert.html), or + DELETE (http://dev.mysql.com/doc/refman/5.7/en/delete.html) + statement, you can retrieve the affected rows count by + calling getUpdateCount() on the Statement instance. + + Example 6.2 Connector/J: Using java.sql.Statement to execute + a SELECT query +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.ResultSet; + +// assume that conn is an already created JDBC connection (see previou +s examples) + +Statement stmt = null; +ResultSet rs = null; + +try { + stmt = conn.createStatement(); + rs = stmt.executeQuery("SELECT foo FROM bar"); + + // or alternatively, if you don't know ahead of time that + // the query will be a SELECT... + + if (stmt.execute("SELECT foo FROM bar")) { + rs = stmt.getResultSet(); + } + + // Now do something with the ResultSet .... +} +catch (SQLException ex){ + // handle any errors + System.out.println("SQLException: " + ex.getMessage()); + System.out.println("SQLState: " + ex.getSQLState()); + System.out.println("VendorError: " + ex.getErrorCode()); +} +finally { + // it is a good idea to release + // resources in a finally{} block + // in reverse-order of their creation + // if they are no-longer needed + + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { } // ignore + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { } // ignore + + stmt = null; + } +} + +6.3 Using JDBC CallableStatements to Execute Stored Procedures + + Starting with MySQL server version 5.0 when used with + Connector/J 3.1.1 or newer, the java.sql.CallableStatement + interface is fully implemented with the exception of the + getParameterMetaData() method. + + For more information on MySQL stored procedures, please refer + to Using Stored Routines (Procedures and Functions) + (http://dev.mysql.com/doc/refman/5.7/en/stored-routines.html) + . + + Connector/J exposes stored procedure functionality through + JDBC's CallableStatement interface. + Note + + Current versions of MySQL server do not return enough + information for the JDBC driver to provide result set + metadata for callable statements. This means that when using + CallableStatement, ResultSetMetaData may return NULL. + + The following example shows a stored procedure that returns + the value of inOutParam incremented by 1, and the string + passed in using inputParam as a ResultSet: + + Example 6.3 Connector/J: Calling Stored Procedures +CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \ + INOUT inOutParam INT) +BEGIN + DECLARE z INT; + SET z = inOutParam + 1; + SET inOutParam = z; + + SELECT inputParam; + + SELECT CONCAT('zyxw', inputParam); +END + + To use the demoSp procedure with Connector/J, follow these + steps: + + 1. Prepare the callable statement by using + Connection.prepareCall(). + Notice that you have to use JDBC escape syntax, and that + the parentheses surrounding the parameter placeholders + are not optional: + Example 6.4 Connector/J: Using Connection.prepareCall() +import java.sql.CallableStatement; + +... + + // + // Prepare a call to the stored procedure 'demoSp' + // with two parameters + // + // Notice the use of JDBC-escape syntax ({call ...}) + // + + CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}"); + + + + cStmt.setString(1, "abcdefg"); + + Note + Connection.prepareCall() is an expensive method, due to + the metadata retrieval that the driver performs to + support output parameters. For performance reasons, + minimize unnecessary calls to Connection.prepareCall() by + reusing CallableStatement instances in your code. + + 2. Register the output parameters (if any exist) + To retrieve the values of output parameters (parameters + specified as OUT or INOUT when you created the stored + procedure), JDBC requires that they be specified before + statement execution using the various + registerOutputParameter() methods in the + CallableStatement interface: + Example 6.5 Connector/J: Registering output parameters +import java.sql.Types; +... +// +// Connector/J supports both named and indexed +// output parameters. You can register output +// parameters using either method, as well +// as retrieve output parameters using either +// method, regardless of what method was +// used to register them. +// +// The following examples show how to use +// the various methods of registering +// output parameters (you should of course +// use only one registration per parameter). +// + +// +// Registers the second parameter as output, and +// uses the type 'INTEGER' for values returned from +// getObject() +// + +cStmt.registerOutParameter(2, Types.INTEGER); + +// +// Registers the named parameter 'inOutParam', and +// uses the type 'INTEGER' for values returned from +// getObject() +// + +cStmt.registerOutParameter("inOutParam", Types.INTEGER); +... + + + 3. Set the input parameters (if any exist) + Input and in/out parameters are set as for + PreparedStatement objects. However, CallableStatement + also supports setting parameters by name: + Example 6.6 Connector/J: Setting CallableStatement input + parameters +... + + // + // Set a parameter by index + // + + cStmt.setString(1, "abcdefg"); + + // + // Alternatively, set a parameter using + // the parameter name + // + + cStmt.setString("inputParameter", "abcdefg"); + + // + // Set the 'in/out' parameter using an index + // + + cStmt.setInt(2, 1); + + // + // Alternatively, set the 'in/out' parameter + // by name + // + + cStmt.setInt("inOutParam", 1); + +... + + + 4. Execute the CallableStatement, and retrieve any result + sets or output parameters. + Although CallableStatement supports calling any of the + Statement execute methods (executeUpdate(), + executeQuery() or execute()), the most flexible method to + call is execute(), as you do not need to know ahead of + time if the stored procedure returns result sets: + Example 6.7 Connector/J: Retrieving results and output + parameter values +... + + boolean hadResults = cStmt.execute(); + + // + // Process all returned result sets + // + + while (hadResults) { + ResultSet rs = cStmt.getResultSet(); + + // process result set + ... + + hadResults = cStmt.getMoreResults(); + } + + // + // Retrieve output parameters + // + // Connector/J supports both index-based and + // name-based retrieval + // + + int outputValue = cStmt.getInt(2); // index-based + + outputValue = cStmt.getInt("inOutParam"); // name-based + +... + +6.4 Retrieving AUTO_INCREMENT Column Values through JDBC + + Before version 3.0 of the JDBC API, there was no standard way + of retrieving key values from databases that supported auto + increment or identity columns. With older JDBC drivers for + MySQL, you could always use a MySQL-specific method on the + Statement interface, or issue the query SELECT + LAST_INSERT_ID() after issuing an INSERT + (http://dev.mysql.com/doc/refman/5.7/en/insert.html) to a + table that had an AUTO_INCREMENT key. Using the + MySQL-specific method call isn't portable, and issuing a + SELECT (http://dev.mysql.com/doc/refman/5.7/en/select.html) + to get the AUTO_INCREMENT key's value requires another + round-trip to the database, which isn't as efficient as + possible. The following code snippets demonstrate the three + different ways to retrieve AUTO_INCREMENT values. First, we + demonstrate the use of the new JDBC 3.0 method + getGeneratedKeys() which is now the preferred method to use + if you need to retrieve AUTO_INCREMENT keys and have access + to JDBC 3.0. The second example shows how you can retrieve + the same value using a standard SELECT LAST_INSERT_ID() + query. The final example shows how updatable result sets can + retrieve the AUTO_INCREMENT value when using the insertRow() + method. + + Example 6.8 Connector/J: Retrieving AUTO_INCREMENT column + values using Statement.getGeneratedKeys() +Statement stmt = null; +ResultSet rs = null; + +try { + + // + // Create a Statement instance that we can use for + // 'normal' result sets assuming you have a + // Connection 'conn' to a MySQL database already + // available + + stmt = conn.createStatement(); + + // + // Issue the DDL queries for the table for this example + // + + stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); + stmt.executeUpdate( + "CREATE TABLE autoIncTutorial (" + + + "priKey INT NOT NULL AUTO_INCREMENT, " + + + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); + + // + // Insert one row that will generate an AUTO INCREMENT + // key in the 'priKey' field + // + + stmt.executeUpdate( + "INSERT INTO autoIncTutorial (dataField) " + + + "values ('Can I Get the Auto Increment Field?')", + Statement.RETURN_GENERATED_KEYS); + + // + // Example of using Statement.getGeneratedKeys() + // to retrieve the value of an auto-increment + // value + // + + int autoIncKeyFromApi = -1; + + rs = stmt.getGeneratedKeys(); + + if (rs.next()) { + autoIncKeyFromApi = rs.getInt(1); + } else { + + // throw an exception from here + } + + System.out.println("Key returned from getGeneratedKeys():" + + + autoIncKeyFromApi); +} finally { + + if (rs != null) { + try { + rs.close(); + } catch (SQLException ex) { + // ignore + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException ex) { + // ignore + } + } +} + + Example 6.9 Connector/J: Retrieving AUTO_INCREMENT column + values using SELECT LAST_INSERT_ID() +Statement stmt = null; +ResultSet rs = null; + +try { + + // + // Create a Statement instance that we can use for + // 'normal' result sets. + + stmt = conn.createStatement(); + + // + // Issue the DDL queries for the table for this example + // + + stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); + stmt.executeUpdate( + "CREATE TABLE autoIncTutorial (" + + + "priKey INT NOT NULL AUTO_INCREMENT, " + + + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); + + // + // Insert one row that will generate an AUTO INCREMENT + // key in the 'priKey' field + // + + stmt.executeUpdate( + "INSERT INTO autoIncTutorial (dataField) " + + + "values ('Can I Get the Auto Increment Field?')"); + + // + // Use the MySQL LAST_INSERT_ID() + // function to do the same thing as getGeneratedKeys() + // + + int autoIncKeyFromFunc = -1; + rs = stmt.executeQuery("SELECT LAST_INSERT_ID()"); + + if (rs.next()) { + autoIncKeyFromFunc = rs.getInt(1); + } else { + // throw an exception from here + } + + System.out.println("Key returned from " + + "'SELECT LAST_INSERT_ID()': " + + autoIncKeyFromFunc); + +} finally { + + if (rs != null) { + try { + rs.close(); + } catch (SQLException ex) { + // ignore + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException ex) { + // ignore + } + } +} + + Example 6.10 Connector/J: Retrieving AUTO_INCREMENT column + values in Updatable ResultSets +Statement stmt = null; +ResultSet rs = null; + +try { + + // + // Create a Statement instance that we can use for + // 'normal' result sets as well as an 'updatable' + // one, assuming you have a Connection 'conn' to + // a MySQL database already available + // + + stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_UPDATABLE); + + // + // Issue the DDL queries for the table for this example + // + + stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); + stmt.executeUpdate( + "CREATE TABLE autoIncTutorial (" + + + "priKey INT NOT NULL AUTO_INCREMENT, " + + + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); + + // + // Example of retrieving an AUTO INCREMENT key + // from an updatable result set + // + + rs = stmt.executeQuery("SELECT priKey, dataField " + + + "FROM autoIncTutorial"); + + rs.moveToInsertRow(); + + rs.updateString("dataField", "AUTO INCREMENT here?"); + rs.insertRow(); + + // + // the driver adds rows at the end + // + + rs.last(); + + // + // We should now be on the row we just inserted + // + + int autoIncKeyFromRS = rs.getInt("priKey"); + + System.out.println("Key returned for inserted row: " + + + autoIncKeyFromRS); + +} finally { + + if (rs != null) { + try { + rs.close(); + } catch (SQLException ex) { + // ignore + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException ex) { + // ignore + } + } +} + + Running the preceding example code should produce the + following output: +Key returned from getGeneratedKeys(): 1 +Key returned from SELECT LAST_INSERT_ID(): 1 +Key returned for inserted row: 1 + + At times, it can be tricky to use the SELECT LAST_INSERT_ID() + query, as that function's value is scoped to a connection. + So, if some other query happens on the same connection, the + value is overwritten. On the other hand, the + getGeneratedKeys() method is scoped by the Statement + instance, so it can be used even if other queries happen on + the same connection, but not on the same Statement instance. + +Chapter 7 Connection Pooling with Connector/J + + Connection pooling is a technique of creating and managing a + pool of connections that are ready for use by any thread + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_th + read) that needs them. Connection pooling can greatly + increase the performance of your Java application, while + reducing overall resource usage. + +How Connection Pooling Works + + Most applications only need a thread to have access to a JDBC + connection when they are actively processing a transaction + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_tr + ansaction), which often takes only milliseconds to complete. + When not processing a transaction, the connection sits idle. + Connection pooling enables the idle connection to be used by + some other thread to do useful work. + + In practice, when a thread needs to do work against a MySQL + or other database with JDBC, it requests a connection from + the pool. When the thread is finished using the connection, + it returns it to the pool, so that it can be used by any + other threads. + + When the connection is loaned out from the pool, it is used + exclusively by the thread that requested it. From a + programming point of view, it is the same as if your thread + called DriverManager.getConnection() every time it needed a + JDBC connection. With connection pooling, your thread may end + up using either a new connection or an already-existing + connection. + +Benefits of Connection Pooling + + The main benefits to connection pooling are: + + * Reduced connection creation time. + Although this is not usually an issue with the quick + connection setup that MySQL offers compared to other + databases, creating new JDBC connections still incurs + networking and JDBC driver overhead that will be avoided + if connections are recycled. + + * Simplified programming model. + When using connection pooling, each individual thread can + act as though it has created its own JDBC connection, + allowing you to use straightforward JDBC programming + techniques. + + * Controlled resource usage. + If you create a new connection every time a thread needs + one rather than using connection pooling, your + application's resource usage can be wasteful, and it + could lead to unpredictable behaviors for your + application when it is under a heavy load. + +Using Connection Pooling with Connector/J + + The concept of connection pooling in JDBC has been + standardized through the JDBC 2.0 Optional interfaces, and + all major application servers have implementations of these + APIs that work with MySQL Connector/J. + + Generally, you configure a connection pool in your + application server configuration files, and access it through + the Java Naming and Directory Interface (JNDI). The following + code shows how you might use a connection pool from an + application deployed in a J2EE application server: + + Example 7.1 Connector/J: Using a connection pool with a J2EE + application server +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.naming.InitialContext; +import javax.sql.DataSource; + + +public class MyServletJspOrEjb { + + public void doSomething() throws Exception { + /* + + * Create a JNDI Initial context to be able to + + * lookup the DataSource + * + + * In production-level code, this should be cached as + + * an instance or static variable, as it can + + * be quite expensive to create a JNDI context. + * + + * Note: This code only works when you are using servlets + + * or EJBs in a J2EE application server. If you are + + * using connection pooling in standalone Java code, you + + * will have to create/configure datasources using whatever + + * mechanisms your particular connection pooling library + + * provides. + */ + + InitialContext ctx = new InitialContext(); + + /* + + * Lookup the DataSource, which will be backed by a pool + + * that the application server provides. DataSource instances + + * are also a good candidate for caching as an instance + + * variable, as JNDI lookups can be expensive as well. + */ + + DataSource ds = + (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB"); + + /* + + * The following code is what would actually be in your + + * Servlet, JSP or EJB 'service' method...where you need + + * to work with a JDBC connection. + */ + + Connection conn = null; + Statement stmt = null; + + try { + conn = ds.getConnection(); + + /* + + * Now, use normal JDBC programming to work with + + * MySQL, making sure to close each resource when you're + + * finished with it, which permits the connection pool + + * resources to be recovered as quickly as possible + */ + + stmt = conn.createStatement(); + stmt.execute("SOME SQL QUERY"); + + stmt.close(); + stmt = null; + + conn.close(); + conn = null; + } finally { + /* + + * close any jdbc instances here that weren't + + * explicitly closed during normal code path, so + + * that we don't 'leak' resources... + */ + + if (stmt != null) { + try { + stmt.close(); + } catch (sqlexception sqlex) { + // ignore, as we can't do anything about it here + } + + stmt = null; + } + + if (conn != null) { + try { + conn.close(); + } catch (sqlexception sqlex) { + // ignore, as we can't do anything about it here + } + + conn = null; + } + } + } +} + + As shown in the example above, after obtaining the JNDI + InitialContext, and looking up the DataSource, the rest of + the code follows familiar JDBC conventions. + + When using connection pooling, always make sure that + connections, and anything created by them (such as statements + or result sets) are closed. This rule applies no matter what + happens in your code (exceptions, flow-of-control, and so + forth). When these objects are closed, they can be re-used; + otherwise, they will be stranded, which means that the MySQL + server resources they represent (such as buffers, locks, or + sockets) are tied up for some time, or in the worst case can + be tied up forever. + +Sizing the Connection Pool + + Each connection to MySQL has overhead (memory, CPU, context + switches, and so forth) on both the client and server side. + Every connection limits how many resources there are + available to your application as well as the MySQL server. + Many of these resources will be used whether or not the + connection is actually doing any useful work! Connection + pools can be tuned to maximize performance, while keeping + resource utilization below the point where your application + will start to fail rather than just run slower. + + The optimal size for the connection pool depends on + anticipated load and average database transaction time. In + practice, the optimal connection pool size can be smaller + than you might expect. If you take Oracle's Java Petstore + blueprint application for example, a connection pool of 15-20 + connections can serve a relatively moderate load (600 + concurrent users) using MySQL and Tomcat with acceptable + response times. + + To correctly size a connection pool for your application, + create load test scripts with tools such as Apache JMeter or + The Grinder, and load test your application. + + An easy way to determine a starting point is to configure + your connection pool's maximum number of connections to be + unbounded, run a load test, and measure the largest amount of + concurrently used connections. You can then work backward + from there to determine what values of minimum and maximum + pooled connections give the best performance for your + particular application. + +Validating Connections + + MySQL Connector/J can validate the connection by executing a + lightweight ping against a server. In the case of + load-balanced connections, this is performed against all + active pooled internal connections that are retained. This is + beneficial to Java applications using connection pools, as + the pool can use this feature to validate connections. + Depending on your connection pool and configuration, this + validation can be carried out at different times: + + 1. Before the pool returns a connection to the application. + + 2. When the application returns a connection to the pool. + + 3. During periodic checks of idle connections. + + To use this feature, specify a validation query in your + connection pool that starts with /* ping */. Note that the + syntax must be exactly as specified. This will cause the + driver send a ping to the server and return a dummy + lightweight result set. When using a ReplicationConnection or + LoadBalancedConnection, the ping will be sent across all + active connections. + + It is critical that the syntax be specified correctly. The + syntax needs to be exact for reasons of efficiency, as this + test is done for every statement that is executed: + +protected static final String PING_MARKER = "/* ping */"; +... +if (sql.charAt(0) == '/') { +if (sql.startsWith(PING_MARKER)) { +doPingInstead(); +... + + + None of the following snippets will work, because the ping + syntax is sensitive to whitespace, capitalization, and + placement: +sql = "/* PING */ SELECT 1"; +sql = "SELECT 1 /* ping*/"; +sql = "/*ping*/ SELECT 1"; +sql = " /* ping */ SELECT 1"; +sql = "/*to ping or not to ping*/ SELECT 1"; + + All of the previous statements will issue a normal SELECT + statement and will not be transformed into the lightweight + ping. Further, for load-balanced connections, the statement + will be executed against one connection in the internal pool, + rather than validating each underlying physical connection. + This results in the non-active physical connections assuming + a stale state, and they may die. If Connector/J then + re-balances, it might select a dead connection, resulting in + an exception being passed to the application. To help prevent + this, you can use loadBalanceValidateConnectionOnSwapServer + to validate the connection before use. + + If your Connector/J deployment uses a connection pool that + allows you to specify a validation query, take advantage of + it, but ensure that the query starts exactly with /* ping */. + This is particularly important if you are using the + load-balancing or replication-aware features of Connector/J, + as it will help keep alive connections which otherwise will + go stale and die, causing problems later. + +Chapter 8 Multi-Host Connections + + The following sections discuss a number of topics that + involve multi-host connections, namely, server + load-balancing, failover, and replication. + + Developers should know the following things about multi-host + connections that are managed through Connector/J: + + * Each multi-host connection is a wrapper of the underlying + physical connections. + + * Each of the underlying physical connections has its own + session. Sessions cannot be tracked, shared, or copied, + given the MySQL architecture. + + * Every switch between physical connections means a switch + between sessions. + + * Within a transaction boundary, there are no switches + between physical connections. Beyond a transaction + boundary, there is no guarantee that a switch does not + occur. + Note + If an application reuses session-scope data (for example, + variables, SSPs) beyond a transaction boundary, failures + are possible, as a switch between the physical + connections (which is also a switch between sessions) + might occur. Therefore, the application should re-prepare + the session data and also restart the last transaction in + case of an exception, or it should re-prepare session + data for each new transaction if it does not want to deal + with exception handling. + +8.1 Configuring Server Failover + + MySQL Connector/J supports server failover. A failover + happens when connection-related errors occur for an + underlying, active connection. The connection errors are, by + default, propagated to the client, which has to handle them + by, for example, recreating the working objects (Statement, + ResultSet, etc.) and restarting the processes. Sometimes, the + driver might eventually fall back to the original host + automatically before the client application continues to run, + in which case the host switch is transparent and the client + application will not even notice it. + + A connection using failover support works just like a + standard connection: the client does not experience any + disruptions in the failover process. This means the client + can rely on the same connection instance even if two + successive statements might be executed on two different + physical hosts. However, this does not mean the client does + not have to deal with the exception that triggered the server + switch. + + The failover is configured at the initial setup stage of the + server connection by the connection URL (see explanations for + its format here): +jdbc:mysql://[primary host][:port],[secondary host 1][:port][,[seconda +ry host 2][:port]]...[/[database]]?? +[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + + The host list in the connection URL comprises of two types of + hosts, the primary and the secondary. When starting a new + connection, the driver always tries to connect to the primary + host first and, if required, fails over to the secondary + hosts on the list sequentially when communication problems + are experienced. Even if the initial connection to the + primary host fails and the driver gets connected to a + secondary host, the primary host never loses its special + status: for example, it can be configured with an access mode + distinct from those of the secondary hosts, and it can be put + on a higher priority when a host is to be picked during a + failover process. + + The failover support is configured by the following + connection properties (their functions are explained in the + paragraphs below): + + * failOverReadOnly + + * secondsBeforeRetryMaster + + * queriesBeforeRetryMaster + + * retriesAllDown + + * autoReconnect + + * autoReconnectForPools + +Configuring Connection Access Mode + + As with any standard connection, the initial connection to + the primary host is in read/write mode. However, if the + driver fails to establish the initial connection to the + primary host and it automatically switches to the next host + on the list, the access mode now depends on the value of the + property failOverReadOnly, which is "true" by default. The + same happens if the driver is initially connected to the + primary host and, because of some connection failure, it + fails over to a secondary host. Every time the connection + falls back to the primary host, its access mode will be + read/write, irrespective of whether or not the primary host + has been connected to before. The connection access mode can + be changed any time at runtime by calling the method + Connection.setReadOnly(boolean), which partially overrides + the property failOverReadOnly. When failOverReadOnly=false + and the access mode is explicitly set to either true or + false, it becomes the mode for every connection after a host + switch, no matter what host type are we connected to; but, if + failOverReadOnly=true, changing the access mode to read/write + is only possible if the driver is connecting to the primary + host; however, even if the access mode cannot be changed for + the current connection, the driver remembers the client's + last intention and, when falling back to the primary host, + that is the mode that will be used. For an illustration, see + the following successions of events with a two-host + connection. + + * Sequence A, with failOverReadOnly=true: + + 1. Connects to primary host in read/write mode + + 2. Sets Connection.setReadOnly(true); primary host now + in read-only mode + + 3. Failover event; connects to secondary host in + read-only mode + + 4. Sets Connection.setReadOnly(false); secondary host + remains in read-only mode + + 5. Falls back to primary host; connection now in + read/write mode + + * Sequence B, with failOverReadOnly=false + + 1. Connects to primary host in read/write mode + + 2. Sets Connection.setReadOnly(true); primary host now + in read-only mode + + 3. Failover event; connects to secondary host in + read-only mode + + 4. Set Connection.setReadOnly(false); connection to + secondary host switches to read/write mode + + 5. Falls back to primary host; connection now in + read/write mode + + The difference between the two scenarios is in step 4: the + access mode for the secondary host in sequence A does not + change at that step, but the driver remembers and uses the + set mode when falling back to the primary host, which would + be read-only otherwise; but in sequence B, the access mode + for the secondary host changes immediately. + +Configuring Fallback to Primary Host + + As already mentioned, the primary host is special in the + failover arrangement when it comes to the host's access mode. + Additionally, the driver tries to fall back to the primary + host as soon as possible by default, even if no communication + exception occurs. Two properties, secondsBeforeRetryMaster + and queriesBeforeRetryMaster, determine when the driver is + ready to retry a reconnection to the primary host (the Master + in the property names stands for the primary host of our + connection URL, which is not necessarily a master host in a + replication setup; the naming was maintained for back + compatibility with Connector/J versions prior to 5.1.35): + + * secondsBeforeRetryMaster determines how much time the + driver waits before trying to fall back to the primary + host + + * queriesBeforeRetryMaster determines the number of queries + that are executed before the driver tries to fall back to + the primary host. Note that for the driver, each call to + a Statement.execute*() method increments the query + execution counter; therefore, when calls are made to + Statement.executeBatch() or if allowMultiQueries or + rewriteBatchStatements are enabled, the driver may not + have an accurate count of the actual number of queries + executed on the server. Also, the driver calls the + Statement.execute*() methods internally in several + occasions. All these mean you can only use + queriesBeforeRetryMaster only as a coarse specification + for when to fall back to the primary host. + + In general, an attempt to fallback to the primary host is + made when at least one of the conditions specified by the two + properties is met, and the attempt always takes place at + transaction boundaries. However, if auto-commit is turned + off, the check happens only when the method + Connection.commit() or Connection.rollback() is called. The + automatic fallback to the primary host can be turned off by + setting simultaneously secondsBeforeRetryMaster and + queriesBeforeRetryMaster to "0". Setting only one of the + properties to "0" only disables one part of the check. + +Configuring Reconnection Attempts + + When establishing a new connection or when a failover event + occurs, the driver tries to connect successively to the next + candidate on the host list. When the end of the list has been + reached, it restarts all over again from the beginning of the + list; however, the primary host is skipped over, if (a) NOT + all the secondary hosts have already been tested at least + once, AND (b) the fallback conditions defined by + secondsBeforeRetryMaster and queriesBeforeRetryMaster are not + yet fulfilled. Each run-through of the whole host list, + (which is not necessarily completed at the end of the host + list) counts as a single connection attempt. The driver tries + as many connection attempts as specified by the value of the + property retriesAllDown. + +Seamless Reconnection + + Although not recommended, you can make the driver perform + failovers without invalidating the active Statement or + ResultSet instances by setting either the parameter + autoReconnect or autoReconnectForPools to true. This allows + the client to continue using the same object instances after + a failover event, without taking any exceptional measures. + This, however, may lead to unexpected results: for example, + if the driver is connected to the primary host with + read/write access mode and it fails-over to a secondary host + in real-only mode, further attempts to issue data-changing + queries will result in errors, and the client will not be + aware of that. This limitation is particularly relevant when + using data streaming: after the failover, the ResultSet looks + to be alright, but the underlying connection may have changed + already, and no backing cursor is available anymore. + +8.2 Configuring Load Balancing with Connector/J + + Connector/J has long provided an effective means to + distribute read/write load across multiple MySQL server + instances for Cluster or master-master replication + deployments. Starting with Connector/J 5.1.3, you can now + dynamically configure load-balanced connections, with no + service outage. In-process transactions are not lost, and no + application exceptions are generated if any application is + trying to use that particular server instance. + + The load balancing is configured at the initial setup stage + of the server connection by the following connection URL, + which has a similar format as the general URL for MySQL + connection, but a specialized scheme: +jdbc:mysql:loadbalance://[host1][:port],[host2][:port][,[host3][:port] +]...[/[database]] ?? +[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + + There are two configuration properties associated with this + functionality: + + * loadBalanceConnectionGroup - This provides the ability to + group connections from different sources. This allows you + to manage these JDBC sources within a single class loader + in any combination you choose. If they use the same + configuration, and you want to manage them as a logical + single group, give them the same name. This is the key + property for management: if you do not define a name + (string) for loadBalanceConnectionGroup, you cannot + manage the connections. All load-balanced connections + sharing the same loadBalanceConnectionGroup value, + regardless of how the application creates them, will be + managed together. + + * loadBalanceEnableJMX - The ability to manage the + connections is exposed when you define a + loadBalanceConnectionGroup; but if you want to manage + this externally, enable JMX by setting this property to + true. This enables a JMX implementation, which exposes + the management and monitoring operations of a connection + group. Further, start your application with the + -Dcom.sun.management.jmxremote JVM flag. You can then + perform connect and perform operations using a JMX client + such as jconsole. + + Once a connection has been made using the correct connection + properties, a number of monitoring properties are available: + + * Current active host count. + + * Current active physical connection count. + + * Current active logical connection count. + + * Total logical connections created. + + * Total transaction count. + + The following management operations can also be performed: + + * Add host. + + * Remove host. + + The JMX interface, + com.mysql.jdbc.jmx.LoadBalanceConnectionGroupManagerMBean, + has the following methods: + + * int getActiveHostCount(String group); + + * int getTotalHostCount(String group); + + * long getTotalLogicalConnectionCount(String group); + + * long getActiveLogicalConnectionCount(String group); + + * long getActivePhysicalConnectionCount(String group); + + * long getTotalPhysicalConnectionCount(String group); + + * long getTotalTransactionCount(String group); + + * void removeHost(String group, String host) throws + SQLException; + + * void stopNewConnectionsToHost(String group, String host) + throws SQLException; + + * void addHost(String group, String host, boolean + forExisting); + + * String getActiveHostsList(String group); + + * String getRegisteredConnectionGroups(); + + The getRegisteredConnectionGroups() method returns the names + of all connection groups defined in that class loader. + + You can test this setup with the following code: + +public class Test { + + private static String URL = "jdbc:mysql:loadbalance://" + + "localhost:3306,localhost:3310/test?" + + "loadBalanceConnectionGroup=first&loadBalanceEnableJMX=true"; + + public static void main(String[] args) throws Exception { + new Thread(new Repeater()).start(); + new Thread(new Repeater()).start(); + new Thread(new Repeater()).start(); + } + + static Connection getNewConnection() throws SQLException, ClassNot +FoundException { + Class.forName("com.mysql.jdbc.Driver"); + return DriverManager.getConnection(URL, "root", ""); + } + + static void executeSimpleTransaction(Connection c, int conn, int t +rans){ + try { + c.setAutoCommit(false); + Statement s = c.createStatement(); + s.executeQuery("SELECT SLEEP(1) /* Connection: " + conn + +", transaction: " + trans + " */"); + c.commit(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static class Repeater implements Runnable { + public void run() { + for(int i=0; i < 100; i++){ + try { + Connection c = getNewConnection(); + for(int j=0; j < 10; j++){ + executeSimpleTransaction(c, i, j); + Thread.sleep(Math.round(100 * Math.random())); + } + c.close(); + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} + + + After compiling, the application can be started with the + -Dcom.sun.management.jmxremote flag, to enable remote + management. jconsole can then be started. The Test main class + will be listed by jconsole. Select this and click Connect. + You can then navigate to the + com.mysql.jdbc.jmx.LoadBalanceConnectionGroupManager bean. At + this point, you can click on various operations and examine + the returned result. + + If you now had an additional instance of MySQL running on + port 3309, you could ensure that Connector/J starts using it + by using the addHost(), which is exposed in jconsole. Note + that these operations can be performed dynamically without + having to stop the application running. + + For further information on the combination of load balancing + and failover, see Section 8.4, "Advanced Load-balancing and + Failover Configuration." + +8.3 Configuring Master/Slave Replication with Connector/J + + This section describe a number of features of Connector/J's + support for replication-aware deployments. + + The replication is configured at the initial setup stage of + the server connection by the connection URL, which has a + similar format as the general URL for MySQL connection, but a + specialized scheme: +jdbc:mysql:replication://[master host][:port],[slave host 1][:port][,[ +slave host 2][:port]]...[/[database]] ?? +[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] + + Users may specify the property + allowMasterDownConnections=true to allow Connection objects + to be created even though no master hosts are reachable. Such + Connection objects report they are read-only, and + isMasterConnection() returns false for them. The Connection + tests for available master hosts when + Connection.setReadOnly(false) is called, throwing an + SQLException if it cannot establish a connection to a master, + or switching to a master connection if the host is available. + + For Connector/J 5.1.38 and later, users may specify the + property allowSlavesDownConnections=true to allow Connection + objects to be created even though no slave hosts are + reachable. A Connection then, at runtime, tests for available + slave hosts when Connection.setReadOnly(true) is called (see + explanation for the method below), throwing an SQLException + if it cannot establish a connection to a slave, unless the + property readFromMasterWhenNoSlaves is set to be "true" (see + below for a description of the property). + +Scaling out Read Load by Distributing Read Traffic to Slaves + + Connector/J 3.1.7 and higher includes a variant of the driver + that will automatically send queries to a read/write master, + or a failover or round-robin loadbalanced set of slaves based + on the state of Connection.getReadOnly(). + + An application signals that it wants a transaction to be + read-only by calling Connection.setReadOnly(true). The + replication-aware connection will use one of the slave + connections, which are load-balanced per slave host using a + round-robin scheme. A given connection is sticky to a slave + until a transaction boundary command (a commit or rollback) + is issued, or until the slave is removed from service. For + Connector/J 5.1.38 and later, after calling + Connection.setReadOnly(true), if you want to allow connection + to a master when no slaves are available, set the property + readFromMasterWhenNoSlaves to "true." Notice that the master + host will be used in read-only state in those cases, as if it + is a slave host. Also notice that setting + readFromMasterWhenNoSlaves=true might result in an extra load + for the master host in a transparent manner. + + If you have a write transaction, or if you have a read that + is time-sensitive (remember, replication in MySQL is + asynchronous), set the connection to be not read-only, by + calling Connection.setReadOnly(false) and the driver will + ensure that further calls are sent to the master MySQL + server. The driver takes care of propagating the current + state of autocommit, isolation level, and catalog between all + of the connections that it uses to accomplish this load + balancing functionality. + + To enable this functionality, use the + com.mysql.jdbc.ReplicationDriver class when configuring your + application server's connection pool or when creating an + instance of a JDBC driver for your standalone application. + Because it accepts the same URL format as the standard MySQL + JDBC driver, ReplicationDriver does not currently work with + java.sql.DriverManager-based connection creation unless it is + the only MySQL JDBC driver registered with the DriverManager + . + + Here is a short example of how ReplicationDriver might be + used in a standalone application: +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.Properties; + +import com.mysql.jdbc.ReplicationDriver; + +public class ReplicationDriverDemo { + + public static void main(String[] args) throws Exception { + ReplicationDriver driver = new ReplicationDriver(); + + Properties props = new Properties(); + + // We want this for failover on the slaves + props.put("autoReconnect", "true"); + + // We want to load balance between the slaves + props.put("roundRobinLoadBalance", "true"); + + props.put("user", "foo"); + props.put("password", "bar"); + + // + // Looks like a normal MySQL JDBC url, with a + // comma-separated list of hosts, the first + // being the 'master', the rest being any number + // of slaves that the driver will load balance against + // + + Connection conn = + driver.connect("jdbc:mysql:replication://master,slave1,slave2, +slave3/test", + props); + + // + // Perform read/write work on the master + // by setting the read-only flag to "false" + // + + conn.setReadOnly(false); + conn.setAutoCommit(false); + conn.createStatement().executeUpdate("UPDATE some_table ...."); + conn.commit(); + + // + // Now, do a query from a slave, the driver automatically picks on +e + // from the list + // + + conn.setReadOnly(true); + + ResultSet rs = + conn.createStatement().executeQuery("SELECT a,b FROM alt_table") +; + + ....... + } +} + + Consider using the Load Balancing JDBC Pool (lbpool) tool, + which provides a wrapper around the standard JDBC driver and + enables you to use DB connection pools that includes checks + for system failures and uneven load distribution. For more + information, see Load Balancing JDBC Driver for MySQL + (mysql-lbpool) (http://code.google.com/p/mysql-lbpool/). + +Support for Multiple-Master Replication Topographies + + Since Connector/J 5.1.27, multi-master replication + topographies are supported. + + The connection URL for replication discussed earlier (i.e., + in the format of + jdbc:mysql:replication://master,slave1,slave2,slave3/test) + assumes that the first (and only the first) host is the + master. Supporting deployments with an arbitrary number of + masters and slaves requires a different URL syntax for + specifying the hosts and the properties for specific hosts, + which is just an expansion of the URL syntax discussed in + Section 5.1, "" with the property type=[master|slave]; for + example: +jdbc:mysql://address=(type=master)(host=master1host),address=(type=mas +ter)(host=master2host),address=(type=slave)(host=slave1host)/database + + Connector/J uses a load-balanced connection internally for + management of the master connections, which means that + ReplicationConnection, when configured to use multiple + masters, exposes the same options to balance load across + master hosts as described in Section 8.2, "Configuring Load + Balancing with Connector/J." + +Live Reconfiguration of Replication Topography + + Since Connector/J 5.1.28, live management of replication host + (single or multi-master) topographies is also supported. This + enables users to promote slaves for Java applications without + requiring an application restart. + + The replication hosts are most effectively managed in the + context of a replication connection group. A + ReplicationConnectionGroup class represents a logical + grouping of connections which can be managed together. There + may be one or more such replication connection groups in a + given Java class loader (there can be an application with two + different JDBC resources needing to be managed + independently). This key class exposes host management + methods for replication connections, and + ReplicationConnection objects register themselves with the + appropriate ReplicationConnectionGroup if a value for the new + replicationConnectionGroup property is specified. The + ReplicationConnectionGroup object tracks these connections + until they are closed, and it is used to manipulate the hosts + associated with these connections. + + Some important methods related to host management include: + + * getMasterHosts(): Returns a collection of strings + representing the hosts configured as masters + + * getSlaveHosts(): Returns a collection of strings + representing the hosts configured as slaves + + * addSlaveHost(String host): Adds new host to pool of + possible slave hosts for selection at start of new + read-only workload + + * promoteSlaveToMaster(String host): Removes the host from + the pool of potential slaves for future read-only + processes (existing read-only process is allowed to + continue to completion) and adds the host to the pool of + potential master hosts + + * removeSlaveHost(String host, boolean closeGently): + Removes the host (host name match must be exact) from the + list of configured slaves; if closeGently is false, + existing connections which have this host as currently + active will be closed hardly (application should expect + exceptions) + + * removeMasterHost(String host, boolean closeGently): Same + as removeSlaveHost(), but removes the host from the list + of configured masters + + Some useful management metrics include: + + * getConnectionCountWithHostAsSlave(String host): Returns + the number of ReplicationConnection objects that have the + given host configured as a possible slave + + * getConnectionCountWithHostAsMaster(String host): Returns + the number of ReplicationConnection objects that have the + given host configured as a possible master + + * getNumberOfSlavesAdded(): Returns the number of times a + slave host has been dynamically added to the group pool + + * getNumberOfSlavesRemoved(): Returns the number of times a + slave host has been dynamically removed from the group + pool + + * getNumberOfSlavePromotions(): Returns the number of times + a slave host has been promoted to a master + + * getTotalConnectionCount(): Returns the number of + ReplicationConnection objects which have been registered + with this group + + * getActiveConnectionCount(): Returns the number of + ReplicationConnection objects currently being managed by + this group + +ReplicationConnectionGroupManager + + com.mysql.jdbc.ReplicationConnectionGroupManager provides + access to the replication connection groups, together with + some utility methods. + + * getConnectionGroup(String groupName): Returns the + ReplicationConnectionGroup object matching the groupName + provided + + The other methods in ReplicationConnectionGroupManager mirror + those of ReplicationConnectionGroup, except that the first + argument is a String group name. These methods will operate + on all matching ReplicationConnectionGroups, which are + helpful for removing a server from service and have it + decommissioned across all possible + ReplicationConnectionGroups. + + These methods might be useful for in-JVM management of + replication hosts if an application triggers topography + changes. For managing host configurations from outside the + JVM, JMX can be used. + +Using JMX for Managing Replication Hosts + + When Connector/J is started with replicationEnableJMX=true + and a value set for the property replicationConnectionGroup, + a JMX MBean will be registered, allowing manipulation of + replication hosts by a JMX client. The MBean interface is + defined in com.mysql.jdbc.jmx.ReplicationGroupManagerMBean, + and leverages the ReplicationConnectionGroupManager static + methods: + public abstract void addSlaveHost(String groupFilter, String host) th +rows SQLException; + public abstract void removeSlaveHost(String groupFilter, String host) + throws SQLException; + public abstract void promoteSlaveToMaster(String groupFilter, String +host) throws SQLException; + public abstract void removeMasterHost(String groupFilter, String host +) throws SQLException; + public abstract String getMasterHostsList(String group); + public abstract String getSlaveHostsList(String group); + public abstract String getRegisteredConnectionGroups(); + public abstract int getActiveMasterHostCount(String group); + public abstract int getActiveSlaveHostCount(String group); + public abstract int getSlavePromotionCount(String group); + public abstract long getTotalLogicalConnectionCount(String group); + public abstract long getActiveLogicalConnectionCount(String group); + +8.4 Advanced Load-balancing and Failover Configuration + + Connector/J provides a useful load-balancing implementation + for MySQL Cluster or multi-master deployments, as explained + in Section 8.2, "Configuring Load Balancing with Connector/J" + and Section 8.3, "." As of Connector/J 5.1.12, this same + implementation is used for balancing load between read-only + slaves with ReplicationDriver. + + When trying to balance workload between multiple servers, the + driver has to determine when it is safe to swap servers, + doing so in the middle of a transaction, for example, could + cause problems. It is important not to lose state + information. For this reason, Connector/J will only try to + pick a new server when one of the following happens: + + 1. At transaction boundaries (transactions are explicitly + committed or rolled back). + + 2. A communication exception (SQL State starting with "08") + is encountered. + + 3. When a SQLException matches conditions defined by user, + using the extension points defined by the + loadBalanceSQLStateFailover, + loadBalanceSQLExceptionSubclassFailover or + loadBalanceExceptionChecker properties. + + The third condition revolves around three new properties + introduced with Connector/J 5.1.13. It allows you to control + which SQLExceptions trigger failover. + + * loadBalanceExceptionChecker - The + loadBalanceExceptionChecker property is really the key. + This takes a fully-qualified class name which implements + the new com.mysql.jdbc.LoadBalanceExceptionChecker + interface. This interface is very simple, and you only + need to implement the following method: +public boolean shouldExceptionTriggerFailover(SQLException ex) + + A SQLException is passed in, and a boolean returned. A + value of true triggers a failover, false does not. + You can use this to implement your own custom logic. An + example where this might be useful is when dealing with + transient errors when using MySQL Cluster, where certain + buffers may become overloaded. The following code snippet + illustrates this: + +public class NdbLoadBalanceExceptionChecker + extends StandardLoadBalanceExceptionChecker { + + public boolean shouldExceptionTriggerFailover(SQLException ex) { + return super.shouldExceptionTriggerFailover(ex) + || checkNdbException(ex); + } + + private boolean checkNdbException(SQLException ex){ + // Have to parse the message since most NDB errors + // are mapped to the same DEMC. + return (ex.getMessage().startsWith("Lock wait timeout exceeded") || + (ex.getMessage().startsWith("Got temporary error") + && ex.getMessage().endsWith("from NDB"))); + } +} + + + The code above extends + com.mysql.jdbc.StandardLoadBalanceExceptionChecker, which + is the default implementation. There are a few convenient + shortcuts built into this, for those who want to have + some level of control using properties, without writing + Java code. This default implementation uses the two + remaining properties: loadBalanceSQLStateFailover and + loadBalanceSQLExceptionSubclassFailover. + + * loadBalanceSQLStateFailover - allows you to define a + comma-delimited list of SQLState code prefixes, against + which a SQLException is compared. If the prefix matches, + failover is triggered. So, for example, the following + would trigger a failover if a given SQLException starts + with "00", or is "12345": +loadBalanceSQLStateFailover=00,12345 + + + * loadBalanceSQLExceptionSubclassFailover - can be used in + conjunction with loadBalanceSQLStateFailover or on its + own. If you want certain subclasses of SQLException to + trigger failover, simply provide a comma-delimited list + of fully-qualified class or interface names to check + against. For example, if you want all + SQLTransientConnectionExceptions to trigger failover, you + would specify: +loadBalanceSQLExceptionSubclassFailover=java.sql.SQLTransientConnectio +nException + + While the three failover conditions enumerated earlier suit + most situations, if autocommit is enabled, Connector/J never + re-balances, and continues using the same physical + connection. This can be problematic, particularly when + load-balancing is being used to distribute read-only load + across multiple slaves. However, Connector/J can be + configured to re-balance after a certain number of statements + are executed, when autocommit is enabled. This functionality + is dependent upon the following properties: + + * loadBalanceAutoCommitStatementThreshold - defines the + number of matching statements which will trigger the + driver to potentially swap physical server connections. + The default value, 0, retains the behavior that + connections with autocommit enabled are never balanced. + + * loadBalanceAutoCommitStatementRegex - the regular + expression against which statements must match. The + default value, blank, matches all statements. So, for + example, using the following properties will cause + Connector/J to re-balance after every third statement + that contains the string "test": +loadBalanceAutoCommitStatementThreshold=3 +loadBalanceAutoCommitStatementRegex=.*test.* + + loadBalanceAutoCommitStatementRegex can prove useful in a + number of situations. Your application may use temporary + tables, server-side session state variables, or + connection state, where letting the driver arbitrarily + swap physical connections before processing is complete + could cause data loss or other problems. This allows you + to identify a trigger statement that is only executed + when it is safe to swap physical connections. + +Chapter 9 Using the Connector/J Interceptor Classes + + An interceptor is a software design pattern that provides a + transparent way to extend or modify some aspect of a program, + similar to a user exit. No recompiling is required. With + Connector/J, the interceptors are enabled and disabled by + updating the connection string to refer to different sets of + interceptor classes that you instantiate. + + The connection properties that control the interceptors are + explained in Section 5.1, "Driver/Datasource Class Names, URL + Syntax and Configuration Properties for Connector/J:" + + * connectionLifecycleInterceptors, where you specify the + fully qualified names of classes that implement the + com.mysql.jdbc.ConnectionLifecycleInterceptor interface. + In these kinds of interceptor classes, you might log + events such as rollbacks, measure the time between + transaction start and end, or count events such as calls + to setAutoCommit(). + + * exceptionInterceptors, where you specify the fully + qualified names of classes that implement the + com.mysql.jdbc.ExceptionInterceptor interface. In these + kinds of interceptor classes, you might add extra + diagnostic information to exceptions that can have + multiple causes or indicate a problem with server + settings. Because exceptionInterceptors classes are only + called when handling a SQLException thrown from + Connector/J code, they can be used even in production + deployments without substantial performance overhead. + + * statementInterceptors, where you specify the fully + qualified names of classes that implement the + com.mysql.jdbc.StatementInterceptorV2 interface. In these + kinds of interceptor classes, you might change or augment + the processing done by certain kinds of statements, such + as automatically checking for queried data in a memcached + server, rewriting slow queries, logging information about + statement execution, or route requests to remote servers. + +Chapter 10 Using Connector/J with Tomcat + + The following instructions are based on the instructions for + Tomcat-5.x, available at + http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examp + les-howto.html which is current at the time this document was + written. + + First, install the .jar file that comes with Connector/J in + $CATALINA_HOME/common/lib so that it is available to all + applications installed in the container. + + Next, configure the JNDI DataSource by adding a declaration + resource to $CATALINA_HOME/conf/server.xml in the context + that defines your web application: + + + ... + + + + + + factory + org.apache.commons.dbcp.BasicDataSourceFactory + + + + maxActive + 10 + + + + maxIdle + 5 + + + + validationQuery + SELECT 1 + + + + testOnBorrow + true + + + + testWhileIdle + true + + + + timeBetweenEvictionRunsMillis + 10000 + + + + minEvictableIdleTimeMillis + 60000 + + + + username + someuser + + + + password + somepass + + + + driverClassName + com.mysql.jdbc.Driver + + + + url + jdbc:mysql://localhost:3306/test + + + + + + Note that Connector/J 5.1.3 introduced a facility whereby, + rather than use a validationQuery value of SELECT 1, it is + possible to use validationQuery with a value set to /* ping + */. This sends a ping to the server which then returns a fake + result set. This is a lighter weight solution. It also has + the advantage that if using ReplicationConnection or + LoadBalancedConnection type connections, the ping will be + sent across all active connections. The following XML snippet + illustrates how to select this option: + + + validationQuery + /* ping */ + + + + Note that /* ping */ has to be specified exactly. + + In general, follow the installation instructions that come + with your version of Tomcat, as the way you configure + datasources in Tomcat changes from time to time, and if you + use the wrong syntax in your XML file, you will most likely + end up with an exception similar to the following: +Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQ +L +state: null + + Note that the auto-loading of drivers having the + META-INF/service/java.sql.Driver class in JDBC 4.0 and above + causes an improper undeployment of the Connector/J driver in + Tomcat on Windows. Namely, the Connector/J jar remains + locked. This is an initialization problem that is not related + to the driver. The possible workarounds, if viable, are as + follows: use "antiResourceLocking=true" as a Tomcat Context + attribute, or remove the META-INF/ directory. + +Chapter 11 Using Connector/J with JBoss + + These instructions cover JBoss-4.x. To make the JDBC driver + classes available to the application server, copy the .jar + file that comes with Connector/J to the lib directory for + your server configuration (which is usually called default). + Then, in the same configuration directory, in the + subdirectory named deploy, create a datasource configuration + file that ends with -ds.xml, which tells JBoss to deploy this + file as a JDBC Datasource. The file should have the following + contents: + + + + MySQLDB + jdbc:mysql://localhost:3306/dbname + com.mysql.jdbc.Driver + user + pass + + 5 + + 20 + + 5 + + + com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter + + + com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker + + + + + +Chapter 12 Using Connector/J with Spring + + The Spring Framework is a Java-based application framework + designed for assisting in application design by providing a + way to configure components. The technique used by Spring is + a well known design pattern called Dependency Injection (see + Inversion of Control Containers and the Dependency Injection + pattern + (http://www.martinfowler.com/articles/injection.html)). This + article will focus on Java-oriented access to MySQL databases + with Spring 2.0. For those wondering, there is a .NET port of + Spring appropriately named Spring.NET. + + Spring is not only a system for configuring components, but + also includes support for aspect oriented programming (AOP). + This is one of the main benefits and the foundation for + Spring's resource and transaction management. Spring also + provides utilities for integrating resource management with + JDBC and Hibernate. + + For the examples in this section the MySQL world sample + database will be used. The first task is to set up a MySQL + data source through Spring. Components within Spring use the + "bean" terminology. For example, to configure a connection to + a MySQL server supporting the world sample database, you + might use: + + + + + + + + + + In the above example, we are assigning values to properties + that will be used in the configuration. For the datasource + configuration: + + + + + + + + + + The placeholders are used to provide values for properties of + this bean. This means that you can specify all the properties + of the configuration in one place instead of entering the + values for each property on each bean. We do, however, need + one more bean to pull this all together. The last bean is + responsible for actually replacing the placeholders with the + property values. + + + + + + + Now that we have our MySQL data source configured and ready + to go, we write some Java code to access it. The example + below will retrieve three random cities and their + corresponding country using the data source we configured + with Spring. +// Create a new application context. this processes the Spring config +ApplicationContext ctx = + new ClassPathXmlApplicationContext("ex1appContext.xml"); +// Retrieve the data source from the application context + DataSource ds = (DataSource) ctx.getBean("dataSource"); +// Open a database connection using Spring's DataSourceUtils +Connection c = DataSourceUtils.getConnection(ds); +try { + // retrieve a list of three random cities + PreparedStatement ps = c.prepareStatement( + "select City.Name as 'City', Country.Name as 'Country' " + + "from City inner join Country on City.CountryCode = Country.Co +de " + + "order by rand() limit 3"); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + String city = rs.getString("City"); + String country = rs.getString("Country"); + System.out.printf("The city %s is in %s%n", city, country); + } +} catch (SQLException ex) { + // something has failed and we print a stack trace to analyse the +error + ex.printStackTrace(); + // ignore failure closing connection + try { c.close(); } catch (SQLException e) { } +} finally { + // properly release our connection + DataSourceUtils.releaseConnection(c, ds); +} + + This is very similar to normal JDBC access to MySQL with the + main difference being that we are using DataSourceUtils + instead of the DriverManager to create the connection. + + While it may seem like a small difference, the implications + are somewhat far reaching. Spring manages this resource in a + way similar to a container managed data source in a J2EE + application server. When a connection is opened, it can be + subsequently accessed in other parts of the code if it is + synchronized with a transaction. This makes it possible to + treat different parts of your application as transactional + instead of passing around a database connection. + +12.1 Using JdbcTemplate + + Spring makes extensive use of the Template method design + pattern (see Template Method Pattern + (http://en.wikipedia.org/wiki/Template_method_pattern)). Our + immediate focus will be on the JdbcTemplate and related + classes, specifically NamedParameterJdbcTemplate. The + template classes handle obtaining and releasing a connection + for data access when one is needed. + + The next example shows how to use NamedParameterJdbcTemplate + inside of a DAO (Data Access Object) class to retrieve a + random city given a country code. +public class Ex2JdbcDao { + /** + + * Data source reference which will be provided by Spring. + */ + private DataSource dataSource; + + /** + + * Our query to find a random city given a country code. Notice + + * the ":country" parameter toward the end. This is called a + + * named parameter. + */ + private String queryString = "select Name from City " + + "where CountryCode = :country order by rand() limit 1"; + + /** + + * Retrieve a random city using Spring JDBC access classes. + */ + public String getRandomCityByCountryCode(String cntryCode) { + // A template that permits using queries with named parameter +s + NamedParameterJdbcTemplate template = + new NamedParameterJdbcTemplate(dataSource); + // A java.util.Map is used to provide values for the paramete +rs + Map params = new HashMap(); + params.put("country", cntryCode); + // We query for an Object and specify what class we are expec +ting + return (String)template.queryForObject(queryString, params, S +tring.class); + } + + /** + + * A JavaBean setter-style method to allow Spring to inject the dat +a source. + + * @param dataSource + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } +} + + The focus in the above code is on the + getRandomCityByCountryCode() method. We pass a country code + and use the NamedParameterJdbcTemplate to query for a city. + The country code is placed in a Map with the key "country", + which is the parameter is named in the SQL query. + + To access this code, you need to configure it with Spring by + providing a reference to the data source. + + + + + + + At this point, we can just grab a reference to the DAO from + Spring and call getRandomCityByCountryCode(). + // Create the application context + ApplicationContext ctx = + new ClassPathXmlApplicationContext("ex2appContext.xml"); + // Obtain a reference to our DAO + Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao"); + + String countryCode = "USA"; + + // Find a few random cities in the US + for(int i = 0; i < 4; ++i) + System.out.printf("A random city in %s is %s%n", countryCode, + dao.getRandomCityByCountryCode(countryCode)); + + This example shows how to use Spring's JDBC classes to + completely abstract away the use of traditional JDBC classes + including Connection and PreparedStatement. + +12.2 Transactional JDBC Access + + You might be wondering how we can add transactions into our + code if we do not deal directly with the JDBC classes. Spring + provides a transaction management package that not only + replaces JDBC transaction management, but also enables + declarative transaction management (configuration instead of + code). + + To use transactional database access, we will need to change + the storage engine of the tables in the world database. The + downloaded script explicitly creates MyISAM tables which do + not support transactional semantics. The InnoDB storage + engine does support transactions and this is what we will be + using. We can change the storage engine with the following + statements. +ALTER TABLE City ENGINE=InnoDB; +ALTER TABLE Country ENGINE=InnoDB; +ALTER TABLE CountryLanguage ENGINE=InnoDB; + + A good programming practice emphasized by Spring is + separating interfaces and implementations. What this means is + that we can create a Java interface and only use the + operations on this interface without any internal knowledge + of what the actual implementation is. We will let Spring + manage the implementation and with this it will manage the + transactions for our implementation. + + First you create a simple interface: +public interface Ex3Dao { + Integer createCity(String name, String countryCode, + String district, Integer population); +} + + This interface contains one method that will create a new + city record in the database and return the id of the new + record. Next you need to create an implementation of this + interface. +public class Ex3DaoImpl implements Ex3Dao { + protected DataSource dataSource; + protected SqlUpdate updateQuery; + protected SqlFunction idQuery; + + public Integer createCity(String name, String countryCode, + String district, Integer population) { + updateQuery.update(new Object[] { name, countryCode, + district, population }); + return getLastId(); + } + + protected Integer getLastId() { + return idQuery.run(); + } +} + + You can see that we only operate on abstract query objects + here and do not deal directly with the JDBC API. Also, this + is the complete implementation. All of our transaction + management will be dealt with in the configuration. To get + the configuration started, we need to create the DAO. + + + + ... + ... + + + + Now you need to set up the transaction configuration. The + first thing you must do is create transaction manager to + manage the data source and a specification of what + transaction properties are required for the dao methods. + + + + + + + + + + + + + The preceding code creates a transaction manager that handles + transactions for the data source provided to it. The txAdvice + uses this transaction manager and the attributes specify to + create a transaction for all methods. Finally you need to + apply this advice with an AOP pointcut. + + + + + + + + This basically says that all methods called on the Ex3Dao + interface will be wrapped in a transaction. To make use of + this, you only have to retrieve the dao from the application + context and call a method on the dao instance. +Ex3Dao dao = (Ex3Dao) ctx.getBean("dao"); +Integer id = dao.createCity(name, countryCode, district, pop); + + We can verify from this that there is no transaction + management happening in our Java code and it is all + configured with Spring. This is a very powerful notion and + regarded as one of the most beneficial features of Spring. + +12.3 Connection Pooling with Spring + + In many situations, such as web applications, there will be a + large number of small database transactions. When this is the + case, it usually makes sense to create a pool of database + connections available for web requests as needed. Although + MySQL does not spawn an extra process when a connection is + made, there is still a small amount of overhead to create and + set up the connection. Pooling of connections also alleviates + problems such as collecting large amounts of sockets in the + TIME_WAIT state. + + Setting up pooling of MySQL connections with Spring is as + simple as changing the data source configuration in the + application context. There are a number of configurations + that we can use. The first example is based on the Jakarta + Commons DBCP library + (http://jakarta.apache.org/commons/dbcp/). The example below + replaces the source configuration that was based on + DriverManagerDataSource with DBCP's BasicDataSource. + + + + + + + + + + + The configuration of the two solutions is very similar. The + difference is that DBCP will pool connections to the database + instead of creating a new connection every time one is + requested. We have also set a parameter here called + initialSize. This tells DBCP that we want three connections + in the pool when it is created. + + Another way to configure connection pooling is to configure a + data source in our J2EE application server. Using JBoss as an + example, you can set up the MySQL connection pool by creating + a file called mysql-local-ds.xml and placing it in the + server/default/deploy directory in JBoss. Once we have this + setup, we can use JNDI to look it up. With Spring, this + lookup is very simple. The data source configuration looks + like this. + + + + +Chapter 13 Using Connector/J with GlassFish + + This section explains how to use MySQL Connector/J with + GlassFish (tm) Server Open Source Edition 3.0.1. GlassFish + can be downloaded from the GlassFish website + (https://glassfish.dev.java.net/public/downloadsindex.html#to + p). + + Once GlassFish is installed, make sure it can access MySQL + Connector/J. To do this, copy the MySQL Connector/J jar file + to the domain-dir/lib directory. For example, copy + mysql-connector-java-5.1.30-bin.jar to + C:\glassfish-install-path\domains\domain-name\lib. Restart + the GlassFish Application Server. For more information, see + "Integrating the JDBC Driver" in GlassFish Server Open Source + Edition Administration Guide, available at GlassFish Server + Documentation + (https://glassfish.java.net/documentation.html). + + You are now ready to create JDBC Connection Pools and JDBC + Resources. + + Creating a Connection Pool + + 1. In the GlassFish Administration Console, using the + navigation tree navigate to Resources, JDBC, Connection + Pools. + + 2. In the JDBC Connection Pools frame click New. You will + enter a two step wizard. + + 3. In the Name field under General Settings enter the name + for the connection pool, for example enter MySQLConnPool. + + 4. In the Resource Type field, select javax.sql.DataSource + from the drop-down listbox. + + 5. In the Database Vendor field, select MySQL from the + drop-down listbox. Click Next to go to the next page of + the wizard. + + 6. You can accept the default settings for General Settings, + Pool Settings and Transactions for this example. Scroll + down to Additional Properties. + + 7. In Additional Properties you will need to ensure the + following properties are set: + + + ServerName - The server to connect to. For local + testing this will be localhost. + + + User - The user name with which to connect to MySQL. + + + Password - The corresponding password for the user. + + + DatabaseName - The database to connect to, for + example the sample MySQL database World. + + 8. Click Finish to exit the wizard. You will be taken to the + JDBC Connection Pools page where all current connection + pools, including the one you just created, will be + displayed. + + 9. In the JDBC Connection Pools frame click on the + connection pool you just created. Here, you can review + and edit information about the connection pool. Because + Connector/J does not support optimized validation + queries, go to the Advanced tab, and under Connection + Validation, configure the following settings: + + + Connection Validation - select Required. + + + Validation Method - select table from the drop-down + menu. + + + Table Name - enter DUAL. + 10. To test your connection pool click the Ping button at the + top of the frame. A message will be displayed confirming + correct operation or otherwise. If an error message is + received recheck the previous steps, and ensure that + MySQL Connector/J has been correctly copied into the + previously specified location. + + Now that you have created a connection pool you will also + need to create a JDBC Resource (data source) for use by your + application. + + Creating a JDBC Resource + + Your Java application will usually reference a data source + object to establish a connection with the database. This + needs to be created first using the following procedure. + + * Using the navigation tree in the GlassFish Administration + Console, navigate to Resources, JDBC, JDBC Resources. A + list of resources will be displayed in the JDBC Resources + frame. + + * Click New. The New JDBC Resource frame will be displayed. + + * In the JNDI Name field, enter the JNDI name that will be + used to access this resource, for example enter + jdbc/MySQLDataSource. + + * In the Pool Name field, select a connection pool you want + this resource to use from the drop-down listbox. + + * Optionally, you can enter a description into the + Description field. + + * Additional properties can be added if required. + + * Click OK to create the new JDBC resource. The JDBC + Resources frame will list all available JDBC Resources. + +13.1 A Simple JSP Application with GlassFish, Connector/J and MySQL + + This section shows how to deploy a simple JSP application on + GlassFish, that connects to a MySQL database. + + This example assumes you have already set up a suitable + Connection Pool and JDBC Resource, as explained in the + preceding sections. It is also assumed you have a sample + database installed, such as world. + + The main application code, index.jsp is presented here: + +<%@ page import="java.sql.*, javax.sql.*, java.io.*, javax.naming.*" % +> + +Hello world from JSP + +<% + InitialContext ctx; + DataSource ds; + Connection conn; + Statement stmt; + ResultSet rs; + + try { + ctx = new InitialContext(); + ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource") +; + //ds = (DataSource) ctx.lookup("jdbc/MySQLDataSource"); + conn = ds.getConnection(); + stmt = conn.createStatement(); + rs = stmt.executeQuery("SELECT * FROM Country"); + + while(rs.next()) { +%> +

Name: <%= rs.getString("Name") %>

+

Population: <%= rs.getString("Population") %>

+<% + } + } + catch (SQLException se) { +%> + <%= se.getMessage() %> +<% + } + catch (NamingException ne) { +%> + <%= ne.getMessage() %> +<% + } +%> + + + + + In addition two XML files are required: web.xml, and + sun-web.xml. There may be other files present, such as + classes and images. These files are organized into the + directory structure as follows: +index.jsp +WEB-INF + | + - web.xml + - sun-web.xml + + The code for web.xml is: + + + + HelloWebApp + + + jdbc/MySQLDataSource + javax.sql.DataSource + Container + Shareable + + + + + The code for sun-web.xml is: + + + + + HelloWebApp + + jdbc/MySQLDataSource + jdbc/MySQLDataSource + + + + + These XML files illustrate a very important aspect of running + JDBC applications on GlassFish. On GlassFish it is important + to map the string specified for a JDBC resource to its JNDI + name, as set up in the GlassFish administration console. In + this example, the JNDI name for the JDBC resource, as + specified in the GlassFish Administration console when + creating the JDBC Resource, was jdbc/MySQLDataSource. This + must be mapped to the name given in the application. In this + example the name specified in the application, + jdbc/MySQLDataSource, and the JNDI name, happen to be the + same, but this does not necessarily have to be the case. Note + that the XML element is used to specify the + name as used in the application source code, and this is + mapped to the JNDI name specified using the + element, in the file sun-web.xml. The resource also has to be + created in the web.xml file, although the mapping of the + resource to a JNDI name takes place in the sun-web.xml file. + + If you do not have this mapping set up correctly in the XML + files you will not be able to lookup the data source using a + JNDI lookup string such as: +ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource"); + + You will still be able to access the data source directly + using: +ds = (DataSource) ctx.lookup("jdbc/MySQLDataSource"); + + With the source files in place, in the correct directory + structure, you are ready to deploy the application: + + 1. In the navigation tree, navigate to Applications - the + Applications frame will be displayed. Click Deploy. + + 2. You can now deploy an application packaged into a single + WAR file from a remote client, or you can choose a + packaged file or directory that is locally accessible to + the server. If you are simply testing an application + locally you can simply point GlassFish at the directory + that contains your application, without needing to + package the application into a WAR file. + + 3. Now select the application type from the Type drop-down + listbox, which in this example is Web application. + + 4. Click OK. + + Now, when you navigate to the Applications frame, you will + have the option to Launch, Redeploy, or Restart your + application. You can test your application by clicking + Launch. The application will connection to the MySQL database + and display the Name and Population of countries in the + Country table. + +13.2 A Simple Servlet with GlassFish, Connector/J and MySQL + + This section describes a simple servlet that can be used in + the GlassFish environment to access a MySQL database. As with + the previous section, this example assumes the sample + database world is installed. + + The project is set up with the following directory structure: +index.html +WEB-INF + | + - web.xml + - sun-web.xml + - classes + | + - HelloWebServlet.java + - HelloWebServlet.class + + The code for the servlet, located in HelloWebServlet.java, is + as follows: + +import javax.servlet.http.*; +import javax.servlet.*; +import java.io.*; +import java.sql.*; +import javax.sql.*; +import javax.naming.*; + +public class HelloWebServlet extends HttpServlet { + + InitialContext ctx = null; + DataSource ds = null; + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + + String sql = "SELECT Name, Population FROM Country WHERE Name=?"; + + public void init () throws ServletException { + try { + ctx = new InitialContext(); + ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource +"); + conn = ds.getConnection(); + ps = conn.prepareStatement(sql); + } + catch (SQLException se) { + System.out.println("SQLException: "+se.getMessage()); + } + catch (NamingException ne) { + System.out.println("NamingException: "+ne.getMessage()); + } + } + + public void destroy () { + try { + if (rs != null) + rs.close(); + if (ps != null) + ps.close(); + if (conn != null) + conn.close(); + if (ctx != null) + ctx.close(); + } + catch (SQLException se) { + System.out.println("SQLException: "+se.getMessage()); + } + catch (NamingException ne) { + System.out.println("NamingException: "+ne.getMessage()); + } + } + + public void doPost(HttpServletRequest req, HttpServletResponse resp) +{ + try { + String country_name = req.getParameter("country_name"); + resp.setContentType("text/html"); + PrintWriter writer = resp.getWriter(); + writer.println(""); + writer.println("

Country: "+country_name+"

"); + ps.setString(1, country_name); + rs = ps.executeQuery(); + if (!rs.next()){ + writer.println("

Country does not exist!

"); + } + else { + rs.beforeFirst(); + while(rs.next()) { + writer.println("

Name: "+rs.getString("Name")+"

"); + writer.println("

Population: "+rs.getString("Population")+ +"

"); + } + } + writer.println(""); + writer.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public void doGet(HttpServletRequest req, HttpServletResponse resp){ + try { + resp.setContentType("text/html"); + PrintWriter writer = resp.getWriter(); + writer.println(""); + writer.println("

Hello from servlet doGet()

"); + writer.println(""); + writer.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} + + + In the preceding code a basic doGet() method is implemented, + but is not used in the example. The code to establish the + connection with the database is as shown in the previous + example, Section 13.1, "A Simple JSP Application with + GlassFish, Connector/J and MySQL," and is most conveniently + located in the servlet init() method. The corresponding + freeing of resources is located in the destroy method. The + main functionality of the servlet is located in the doPost() + method. If the user enters into the input form a country name + that can be located in the database, the population of the + country is returned. The code is invoked using a POST action + associated with the input form. The form is defined in the + file index.html: + + + HelloWebServlet + + +

HelloWebServlet

+ +

Please enter country name:

+ +
+ + +
+ + + + + + The XML files web.xml and sun-web.xml are as for the example + in the preceding section, Section 13.1, "A Simple JSP + Application with GlassFish, Connector/J and MySQL," no + additional changes are required. + + When compiling the Java source code, you will need to specify + the path to the file javaee.jar. On Windows, this can be done + as follows: +shell> javac -classpath c:\glassfishv3\glassfish\lib\javaee.jar HelloW +ebServlet.java + + Once the code is correctly located within its directory + structure, and compiled, the application can be deployed in + GlassFish. This is done in exactly the same way as described + in the preceding section, Section 13.1, "A Simple JSP + Application with GlassFish, Connector/J and MySQL." + + Once deployed the application can be launched from within the + GlassFish Administration Console. Enter a country name such + as "England", and the application will return "Country does + not exist!". Enter "France", and the application will return + a population of 59225700. + +Chapter 14 Using Connector/J with MySQL Fabric + + MySQL Fabric is a system for managing a farm of MySQL servers + (and other components). Fabric provides an extensible and + easy to use system for managing a MySQL deployment for + sharding and high-availability. + + For more information on MySQL Fabric, see MySQL Fabric + (http://dev.mysql.com/doc/mysql-utilities/1.5/en/fabric.html) + . For instructions on how to use Connector/J with MySQL + Fabric, see Using Connector/J with MySQL Fabric + (http://dev.mysql.com/doc/mysql-utilities/1.5/en/connector-j- + fabric.html). + +Chapter 15 Troubleshooting Connector/J Applications + + This section explains the symptoms and resolutions for the + most commonly encountered issues with applications using + MySQL Connector/J. + + Questions + + * 15.1: When I try to connect to the database with MySQL + Connector/J, I get the following exception: +SQLException: Server configuration denies access to data source +SQLState: 08001 +VendorError: 0 + + What is going on? I can connect just fine with the MySQL + command-line client. + + * 15.2: My application throws an SQLException 'No Suitable + Driver'. Why is this happening? + + * 15.3: I'm trying to use MySQL Connector/J in an applet or + application and I get an exception similar to: +SQLException: Cannot connect to MySQL server on host:3306. +Is there a MySQL server running on the machine/port you +are trying to connect to? + +(java.security.AccessControlException) +SQLState: 08S01 +VendorError: 0 + + + * 15.4: I have a servlet/application that works fine for a + day, and then stops working overnight + + * 15.5: I'm trying to use JDBC 2.0 updatable result sets, + and I get an exception saying my result set is not + updatable. + + * 15.6: I cannot connect to the MySQL server using + Connector/J, and I'm sure the connection parameters are + correct. + + * 15.7: I am trying to connect to my MySQL server within my + application, but I get the following error and stack + trace: +java.net.SocketException +MESSAGE: Software caused connection abort: recv failed + +STACKTRACE: + +java.net.SocketException: Software caused connection abort: recv faile +d +at java.net.SocketInputStream.socketRead0(Native Method) +at java.net.SocketInputStream.read(Unknown Source) +at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392) +at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414) +at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625) +at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926) +at com.mysql.jdbc.Connection.(Connection.java:452) +at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja +va:411) + + + * 15.8: My application is deployed through JBoss and I am + using transactions to handle the statements on the MySQL + database. Under heavy loads, I am getting an error and + stack trace, but these only occur after a fixed period of + heavy activity. + + * 15.9: When using gcj, a java.io.CharConversionException + exception is raised when working with certain character + sequences. + + * 15.10: Updating a table that contains a primary key + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glo + s_primary_key) that is either FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-ty + pes.html) or compound primary key that uses FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-ty + pes.html) fails to update the table and raises an + exception. + + * 15.11: You get an ER_NET_PACKET_TOO_LARGE + (http://dev.mysql.com/doc/refman/5.7/en/error-messages-se + rver.html#error_er_net_packet_too_large) exception, even + though the binary blob size you want to insert using JDBC + is safely below the max_allowed_packet + (http://dev.mysql.com/doc/refman/5.7/en/server-system-var + iables.html#sysvar_max_allowed_packet) size. + + * 15.12: What should you do if you receive error messages + similar to the following: "Communications link failure - + Last packet sent to the server was X ms ago"? + + * 15.13: Why does Connector/J not reconnect to MySQL and + re-issue the statement after a communication failure, + instead of throwing an Exception, even though I use the + autoReconnect connection string option? + + * 15.14: How can I use 3-byte UTF8 with Connector/J? + + * 15.15: How can I use 4-byte UTF8, utf8mb4 with + Connector/J? + + * 15.16: Using useServerPrepStmts=false and certain + character encodings can lead to corruption when inserting + BLOBs. How can this be avoided? + + Questions and Answers + + 15.1: When I try to connect to the database with MySQL + Connector/J, I get the following exception: +SQLException: Server configuration denies access to data source +SQLState: 08001 +VendorError: 0 + + What is going on? I can connect just fine with the MySQL + command-line client. + + MySQL Connector/J must use TCP/IP sockets to connect to + MySQL, as Java does not support Unix Domain Sockets. + Therefore, when MySQL Connector/J connects to MySQL, the + security manager in MySQL server will use its grant tables to + determine whether the connection is permitted. + + You must add the necessary security credentials to the MySQL + server for this to happen, using the GRANT + (http://dev.mysql.com/doc/refman/5.7/en/grant.html) statement + to your MySQL Server. See GRANT Syntax + (http://dev.mysql.com/doc/refman/5.7/en/grant.html), for more + information. + Note + + Testing your connectivity with the mysql command-line client + will not work unless you add the "host" flag, and use + something other than localhost for the host. The mysql + command-line client will use Unix domain sockets if you use + the special host name localhost. If you are testing + connectivity to localhost, use 127.0.0.1 as the host name + instead. + Warning + + Changing privileges and permissions improperly in MySQL can + potentially cause your server installation to not have + optimal security properties. + + 15.2: My application throws an SQLException 'No Suitable + Driver'. Why is this happening? + + There are three possible causes for this error: + + * The Connector/J driver is not in your CLASSPATH, see + Chapter 3, "Connector/J Installation." + + * The format of your connection URL is incorrect, or you + are referencing the wrong JDBC driver. + + * When using DriverManager, the jdbc.drivers system + property has not been populated with the location of the + Connector/J driver. + + 15.3: I'm trying to use MySQL Connector/J in an applet or + application and I get an exception similar to: +SQLException: Cannot connect to MySQL server on host:3306. +Is there a MySQL server running on the machine/port you +are trying to connect to? + +(java.security.AccessControlException) +SQLState: 08S01 +VendorError: 0 + + Either you're running an Applet, your MySQL server has been + installed with the "skip-networking" option set, or your + MySQL server has a firewall sitting in front of it. + + Applets can only make network connections back to the machine + that runs the web server that served the .class files for the + applet. This means that MySQL must run on the same machine + (or you must have some sort of port re-direction) for this to + work. This also means that you will not be able to test + applets from your local file system, you must always deploy + them to a web server. + + MySQL Connector/J can only communicate with MySQL using + TCP/IP, as Java does not support Unix domain sockets. TCP/IP + communication with MySQL might be affected if MySQL was + started with the "skip-networking" flag, or if it is + firewalled. + + If MySQL has been started with the "skip-networking" option + set (the Debian Linux package of MySQL server does this for + example), you need to comment it out in the file + /etc/mysql/my.cnf or /etc/my.cnf. Of course your my.cnf file + might also exist in the data directory of your MySQL server, + or anywhere else (depending on how MySQL was compiled for + your system). Binaries created by us always look in + /etc/my.cnf and datadir/my.cnf. If your MySQL server has been + firewalled, you will need to have the firewall configured to + allow TCP/IP connections from the host where your Java code + is running to the MySQL server on the port that MySQL is + listening to (by default, 3306). + + 15.4: I have a servlet/application that works fine for a day, + and then stops working overnight + + MySQL closes connections after 8 hours of inactivity. You + either need to use a connection pool that handles stale + connections or use the autoReconnect parameter (see Section + 5.1, "Driver/Datasource Class Names, URL Syntax and + Configuration Properties for Connector/J"). + + Also, catch SQLExceptions in your application and deal with + them, rather than propagating them all the way until your + application exits. This is just good programming practice. + MySQL Connector/J will set the SQLState (see + java.sql.SQLException.getSQLState() in your API docs) to + 08S01 when it encounters network-connectivity issues during + the processing of a query. Attempt to reconnect to MySQL at + this point. + + The following (simplistic) example shows what code that can + handle these exceptions might look like: + + Example 15.1 Connector/J: Example of transaction with retry + logic +public void doBusinessOp() throws SQLException { + Connection conn = null; + Statement stmt = null; + ResultSet rs = null; + + // + // How many times do you want to retry the transaction + // (or at least _getting_ a connection)? + // + int retryCount = 5; + + boolean transactionCompleted = false; + + do { + try { + conn = getConnection(); // assume getting this from a + // javax.sql.DataSource, or the + // java.sql.DriverManager + + conn.setAutoCommit(false); + + // + // Okay, at this point, the 'retry-ability' of the + // transaction really depends on your application logic, + // whether or not you're using autocommit (in this case + // not), and whether you're using transactional storage + // engines + // + // For this example, we'll assume that it's _not_ safe + // to retry the entire transaction, so we set retry + // count to 0 at this point + // + // If you were using exclusively transaction-safe tables, + // or your application could recover from a connection goi +ng + // bad in the middle of an operation, then you would not + // touch 'retryCount' here, and just let the loop repeat + // until retryCount == 0. + // + retryCount = 0; + + stmt = conn.createStatement(); + + String query = "SELECT foo FROM bar ORDER BY baz"; + + rs = stmt.executeQuery(query); + + while (rs.next()) { + } + + rs.close(); + rs = null; + + stmt.close(); + stmt = null; + + conn.commit(); + conn.close(); + conn = null; + + transactionCompleted = true; + } catch (SQLException sqlEx) { + + // + // The two SQL states that are 'retry-able' are 08S01 + // for a communications error, and 40001 for deadlock. + // + // Only retry if the error was due to a stale connection, + // communications problem or deadlock + // + + String sqlState = sqlEx.getSQLState(); + + if ("08S01".equals(sqlState) || "40001".equals(sqlState)) +{ + retryCount -= 1; + } else { + retryCount = 0; + } + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { + // You'd probably want to log this... + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // You'd probably want to log this as well... + } + } + + if (conn != null) { + try { + // + // If we got here, and conn is not null, the + // transaction should be rolled back, as not + // all work has been done + + try { + conn.rollback(); + } finally { + conn.close(); + } + } catch (SQLException sqlEx) { + // + // If we got an exception here, something + // pretty serious is going on, so we better + // pass it up the stack, rather than just + // logging it... + + throw sqlEx; + } + } + } + } while (!transactionCompleted && (retryCount > 0)); +} + + Note + + Use of the autoReconnect option is not recommended because + there is no safe method of reconnecting to the MySQL server + without risking some corruption of the connection state or + database state information. Instead, use a connection pool, + which will enable your application to connect to the MySQL + server using an available connection from the pool. The + autoReconnect facility is deprecated, and may be removed in a + future release. + + 15.5: I'm trying to use JDBC 2.0 updatable result sets, and I + get an exception saying my result set is not updatable. + + Because MySQL does not have row identifiers, MySQL + Connector/J can only update result sets that have come from + queries on tables that have at least one primary key + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_pr + imary_key), the query must select every primary key column, + and the query can only span one table (that is, no joins). + This is outlined in the JDBC specification. + + Note that this issue only occurs when using updatable result + sets, and is caused because Connector/J is unable to + guarantee that it can identify the correct rows within the + result set to be updated without having a unique reference to + each row. There is no requirement to have a unique field on a + table if you are using UPDATE + (http://dev.mysql.com/doc/refman/5.7/en/update.html) or + DELETE (http://dev.mysql.com/doc/refman/5.7/en/delete.html) + statements on a table where you can individually specify the + criteria to be matched using a WHERE clause. + + 15.6: I cannot connect to the MySQL server using Connector/J, + and I'm sure the connection parameters are correct. + + Make sure that the skip-networking + (http://dev.mysql.com/doc/refman/5.7/en/server-options.html#o + ption_mysqld_skip-networking) option has not been enabled on + your server. Connector/J must be able to communicate with + your server over TCP/IP; named sockets are not supported. + Also ensure that you are not filtering connections through a + firewall or other network security system. For more + information, see Can't connect to [local] MySQL server + (http://dev.mysql.com/doc/refman/5.7/en/can-not-connect-to-se + rver.html). + + 15.7: I am trying to connect to my MySQL server within my + application, but I get the following error and stack trace: +java.net.SocketException +MESSAGE: Software caused connection abort: recv failed + +STACKTRACE: + +java.net.SocketException: Software caused connection abort: recv faile +d +at java.net.SocketInputStream.socketRead0(Native Method) +at java.net.SocketInputStream.read(Unknown Source) +at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392) +at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414) +at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625) +at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926) +at com.mysql.jdbc.Connection.(Connection.java:452) +at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja +va:411) + + The error probably indicates that you are using a older + version of the Connector/J JDBC driver (2.0.14 or 3.0.x) and + you are trying to connect to a MySQL server with version 4.1x + or newer. The older drivers are not compatible with 4.1 or + newer of MySQL as they do not support the newer + authentication mechanisms. + + It is likely that the older version of the Connector/J driver + exists within your application directory or your CLASSPATH + includes the older Connector/J package. + + 15.8: My application is deployed through JBoss and I am using + transactions to handle the statements on the MySQL database. + Under heavy loads, I am getting an error and stack trace, but + these only occur after a fixed period of heavy activity. + + This is a JBoss, not Connector/J, issue and is connected to + the use of transactions. Under heavy loads the time taken for + transactions to complete can increase, and the error is + caused because you have exceeded the predefined timeout. + + You can increase the timeout value by setting the + TransactionTimeout attribute to the TransactionManagerService + within the /conf/jboss-service.xml file (pre-4.0.3) or + /deploy/jta-service.xml for JBoss 4.0.3 or later. See + TransactionTimeout + (http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout) + within the JBoss wiki for more information. + + 15.9: When using gcj, a java.io.CharConversionException + exception is raised when working with certain character + sequences. + + This is a known issue with gcj which raises an exception when + it reaches an unknown character or one it cannot convert. Add + useJvmCharsetConverters=true to your connection string to + force character conversion outside of the gcj libraries, or + try a different JDK. + + 15.10: Updating a table that contains a primary key + (http://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_pr + imary_key) that is either FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-types. + html) or compound primary key that uses FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-types. + html) fails to update the table and raises an exception. + + Connector/J adds conditions to the WHERE clause during an + UPDATE (http://dev.mysql.com/doc/refman/5.7/en/update.html) + to check the old values of the primary key. If there is no + match, then Connector/J considers this a failure condition + and raises an exception. + + The problem is that rounding differences between supplied + values and the values stored in the database may mean that + the values never match, and hence the update fails. The issue + will affect all queries, not just those from Connector/J. + + To prevent this issue, use a primary key that does not use + FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-types. + html). If you have to use a floating point column in your + primary key, use DOUBLE + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-types. + html) or DECIMAL + (http://dev.mysql.com/doc/refman/5.7/en/fixed-point-types.htm + l) types in place of FLOAT + (http://dev.mysql.com/doc/refman/5.7/en/floating-point-types. + html). + + 15.11: You get an ER_NET_PACKET_TOO_LARGE + (http://dev.mysql.com/doc/refman/5.7/en/error-messages-server + .html#error_er_net_packet_too_large) exception, even though + the binary blob size you want to insert using JDBC is safely + below the max_allowed_packet + (http://dev.mysql.com/doc/refman/5.7/en/server-system-variabl + es.html#sysvar_max_allowed_packet) size. + + This is because the hexEscapeBlock() method in + com.mysql.jdbc.PreparedStatement.streamToBytes() may almost + double the size of your data. + + 15.12: What should you do if you receive error messages + similar to the following: "Communications link failure - Last + packet sent to the server was X ms ago"? + + Generally speaking, this error suggests that the network + connection has been closed. There can be several root causes: + + * Firewalls or routers may clamp down on idle connections + (the MySQL client/server protocol does not ping). + + * The MySQL Server may be closing idle connections that + exceed the wait_timeout or interactive_timeout threshold. + + To help troubleshoot these issues, the following tips can be + used. If a recent (5.1.13+) version of Connector/J is used, + you will see an improved level of information compared to + earlier versions. Older versions simply display the last time + a packet was sent to the server, which is frequently 0 ms + ago. This is of limited use, as it may be that a packet was + just sent, while a packet from the server has not been + received for several hours. Knowing the period of time since + Connector/J last received a packet from the server is useful + information, so if this is not displayed in your exception + message, it is recommended that you update Connector/J. + + Further, if the time a packet was last sent/received exceeds + the wait_timeout or interactive_timeout threshold, this is + noted in the exception message. + + Although network connections can be volatile, the following + can be helpful in avoiding problems: + + * Ensure connections are valid when used from the + connection pool. Use a query that starts with /* ping */ + to execute a lightweight ping instead of full query. + Note, the syntax of the ping needs to be exactly as + specified here. + + * Minimize the duration a connection object is left idle + while other application logic is executed. + + * Explicitly validate the connection before using it if the + connection has been left idle for an extended period of + time. + + * Ensure that wait_timeout and interactive_timeout are set + sufficiently high. + + * Ensure that tcpKeepalive is enabled. + + * Ensure that any configurable firewall or router timeout + settings allow for the maximum expected connection idle + time. + + Note + + Do not expect to be able to reuse a connection without + problems, if it has being lying idle for a period. If a + connection is to be reused after being idle for any length of + time, ensure that you explicitly test it before reusing it. + + 15.13: Why does Connector/J not reconnect to MySQL and + re-issue the statement after a communication failure, instead + of throwing an Exception, even though I use the autoReconnect + connection string option? + + There are several reasons for this. The first is + transactional integrity. The MySQL Reference Manual states + that "there is no safe method of reconnecting to the MySQL + server without risking some corruption of the connection + state or database state information". Consider the following + series of statements for example: + +conn.createStatement().execute( + "UPDATE checking_account SET balance = balance - 1000.00 WHERE custo +mer='Smith'"); +conn.createStatement().execute( + "UPDATE savings_account SET balance = balance + 1000.00 WHERE custom +er='Smith'"); +conn.commit(); + + + Consider the case where the connection to the server fails + after the UPDATE to checking_account. If no exception is + thrown, and the application never learns about the problem, + it will continue executing. However, the server did not + commit the first transaction in this case, so that will get + rolled back. But execution continues with the next + transaction, and increases the savings_account balance by + 1000. The application did not receive an exception, so it + continued regardless, eventually committing the second + transaction, as the commit only applies to the changes made + in the new connection. Rather than a transfer taking place, a + deposit was made in this example. + + Note that running with autocommit enabled does not solve this + problem. When Connector/J encounters a communication problem, + there is no means to determine whether the server processed + the currently executing statement or not. The following + theoretical states are equally possible: + + * The server never received the statement, and therefore no + related processing occurred on the server. + + * The server received the statement, executed it in full, + but the response was not received by the client. + + If you are running with autocommit enabled, it is not + possible to guarantee the state of data on the server when a + communication exception is encountered. The statement may + have reached the server, or it may not. All you know is that + communication failed at some point, before the client + received confirmation (or data) from the server. This does + not only affect autocommit statements though. If the + communication problem occurred during Connection.commit(), + the question arises of whether the transaction was committed + on the server before the communication failed, or whether the + server received the commit request at all. + + The second reason for the generation of exceptions is that + transaction-scoped contextual data may be vulnerable, for + example: + + * Temporary tables. + + * User-defined variables. + + * Server-side prepared statements. + + These items are lost when a connection fails, and if the + connection silently reconnects without generating an + exception, this could be detrimental to the correct execution + of your application. + + In summary, communication errors generate conditions that may + well be unsafe for Connector/J to simply ignore by silently + reconnecting. It is necessary for the application to be + notified. It is then for the application developer to decide + how to proceed in the event of connection errors and + failures. + + 15.14: How can I use 3-byte UTF8 with Connector/J? + + To use 3-byte UTF8 with Connector/J set + characterEncoding=utf8 and set useUnicode=true in the + connection string. + + 15.15: How can I use 4-byte UTF8, utf8mb4 with Connector/J? + + To use 4-byte UTF8 with Connector/J configure the MySQL + server with character_set_server=utf8mb4. Connector/J will + then use that setting as long as characterEncoding has not + been set in the connection string. This is equivalent to + autodetection of the character set. + + 15.16: Using useServerPrepStmts=false and certain character + encodings can lead to corruption when inserting BLOBs. How + can this be avoided? + + When using certain character encodings, such as SJIS, CP932, + and BIG5, it is possible that BLOB data contains characters + that can be interpreted as control characters, for example, + backslash, '\'. This can lead to corrupted data when + inserting BLOBs into the database. There are two things that + need to be done to avoid this: + + 1. Set the connection string option useServerPrepStmts to + true. + + 2. Set SQL_MODE to NO_BACKSLASH_ESCAPES. + +Chapter 16 Known Issues and Limitations + + The following are some known issues and limitations for MySQL + Connector/J: + + * When Connector/J retrieves timestamps for a daylight + saving time (DST) switch day using the getTimeStamp() + method on the result set, some of the returned values + might be wrong. The errors can be avoided by using the + following connection options when connecting to a + database: + useTimezone=true + useLegacyDatetimeCode=false + serverTimezone=UTC + +Chapter 17 Connector/J Support + +17.1 Connector/J Community Support + + Oracle provides assistance to the user community by means of + its mailing lists. For Connector/J related issues, you can + get help from experienced users by using the MySQL and Java + mailing list. Archives and subscription information is + available online at http://lists.mysql.com/java. + + For information about subscribing to MySQL mailing lists or + to browse list archives, visit http://lists.mysql.com/. See + MySQL Mailing Lists + (http://dev.mysql.com/doc/refman/5.7/en/mailing-lists.html). + + Community support from experienced users is also available + through the JDBC Forum (http://forums.mysql.com/list.php?39). + You may also find help from other users in the other MySQL + Forums, located at http://forums.mysql.com. See MySQL + Community Support at the MySQL Forums + (http://dev.mysql.com/doc/refman/5.7/en/forums.html). + +17.2 How to Report Connector/J Bugs or Problems + + The normal place to report bugs is http://bugs.mysql.com/, + which is the address for our bugs database. This database is + public, and can be browsed and searched by anyone. If you log + in to the system, you will also be able to enter new reports. + + If you find a sensitive security bug in MySQL Server, please + let us know immediately by sending an email message to + secalert_us@oracle.com. Exception: Support customers should + report all problems, including security bugs, to Oracle + Support at http://support.oracle.com/. + + Writing a good bug report takes patience, but doing it right + the first time saves time both for us and for yourself. A + good bug report, containing a full test case for the bug, + makes it very likely that we will fix the bug in the next + release. + + This section will help you write your report correctly so + that you do not waste your time doing things that may not + help us much or at all. + + If you have a repeatable bug report, please report it to the + bugs database at http://bugs.mysql.com/. Any bug that we are + able to repeat has a high chance of being fixed in the next + MySQL release. + + To report other problems, you can use one of the MySQL + mailing lists. + + Remember that it is possible for us to respond to a message + containing too much information, but not to one containing + too little. People often omit facts because they think they + know the cause of a problem and assume that some details do + not matter. + + A good principle is this: If you are in doubt about stating + something, state it. It is faster and less troublesome to + write a couple more lines in your report than to wait longer + for the answer if we must ask you to provide information that + was missing from the initial report. + + The most common errors made in bug reports are (a) not + including the version number of Connector/J or MySQL used, + and (b) not fully describing the platform on which + Connector/J is installed (including the JVM version, and the + platform type and version number that MySQL itself is + installed on). + + This is highly relevant information, and in 99 cases out of + 100, the bug report is useless without it. Very often we get + questions like, "Why doesn't this work for me?" Then we find + that the feature requested wasn't implemented in that MySQL + version, or that a bug described in a report has already been + fixed in newer MySQL versions. + + Sometimes the error is platform-dependent; in such cases, it + is next to impossible for us to fix anything without knowing + the operating system and the version number of the platform. + + If at all possible, create a repeatable, standalone testcase + that doesn't involve any third-party classes. + + To streamline this process, we ship a base class for + testcases with Connector/J, named + 'com.mysql.jdbc.util.BaseBugReport'. To create a testcase for + Connector/J using this class, create your own class that + inherits from com.mysql.jdbc.util.BaseBugReport and override + the methods setUp(), tearDown() and runTest(). + + In the setUp() method, create code that creates your tables, + and populates them with any data needed to demonstrate the + bug. + + In the runTest() method, create code that demonstrates the + bug using the tables and data you created in the setUp + method. + + In the tearDown() method, drop any tables you created in the + setUp() method. + + In any of the above three methods, use one of the variants of + the getConnection() method to create a JDBC connection to + MySQL: + + * getConnection() - Provides a connection to the JDBC URL + specified in getUrl(). If a connection already exists, + that connection is returned, otherwise a new connection + is created. + + * getNewConnection() - Use this if you need to get a new + connection for your bug report (that is, there is more + than one connection involved). + + * getConnection(String url) - Returns a connection using + the given URL. + + * getConnection(String url, Properties props) - Returns a + connection using the given URL and properties. + + If you need to use a JDBC URL that is different from + 'jdbc:mysql:///test', override the method getUrl() as well. + + Use the assertTrue(boolean expression) and assertTrue(String + failureMessage, boolean expression) methods to create + conditions that must be met in your testcase demonstrating + the behavior you are expecting (vs. the behavior you are + observing, which is why you are most likely filing a bug + report). + + Finally, create a main() method that creates a new instance + of your testcase, and calls the run method: +public static void main(String[] args) throws Exception { + new MyBugReport().run(); + } + + Once you have finished your testcase, and have verified that + it demonstrates the bug you are reporting, upload it with + your bug report to http://bugs.mysql.com/. diff --git a/mysql-connector-java-5.1.40/docs/connector-j.html b/mysql-connector-java-5.1.40/docs/connector-j.html new file mode 100644 index 0000000..8ccc181 --- /dev/null +++ b/mysql-connector-java-5.1.40/docs/connector-j.html @@ -0,0 +1,10321 @@ +MySQL Connector/J 5.1 Developer Guide + + +

MySQL Connector/J 5.1 Developer Guide

Abstract

+ This manual describes how to install, configure, and develop + database applications using MySQL Connector/J, the JDBC driver + for communicating with MySQL servers. +

+ For notes detailing the changes in each release of Connector/J, + see MySQL + Connector/J Release Notes. +

+ For legal information, see the Legal + Notices. +

+ For help with using MySQL, please visit either the + MySQL Forums or + MySQL Mailing Lists, + where you can discuss your issues with other MySQL users. + +

+ For additional documentation on MySQL products, including + translations of the documentation into other languages, and + downloadable versions in variety of formats, including HTML and PDF + formats, see the MySQL + Documentation Library. +

Licensing information.  + This product may include third-party software, used under + license. If you are using a Commercial + release of MySQL Connector/J 5.1, see + this + document for licensing information, including + licensing information relating to third-party software that + may be included in this Commercial release. If you are using a + Community release of MySQL Connector/J + 5.1, see + this + document for licensing information, including + licensing information relating to third-party software that + may be included in this Community release. +

+ Document generated on: + + 2016-03-28 + + + + (revision: 47177) +


Table of Contents

Preface and Legal Notices
1 Overview of MySQL Connector/J
2 Connector/J Versions
2.1 Connector/J Release Notes and Change History
2.2 Java Versions Supported
3 Connector/J Installation
3.1 Installing Connector/J from a Binary Distribution
3.2 Installing the Driver and Configuring the CLASSPATH
3.3 Upgrading from an Older Version
3.3.1 Upgrading to MySQL Connector/J 5.1.x
3.3.2 JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or Newer
3.3.3 Upgrading from MySQL Connector/J 3.0 to 3.1
3.4 Installing from Source
3.5 Testing Connector/J
4 Connector/J Examples
5 Connector/J (JDBC) Reference
5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J
5.1.1 Properties Files for the useConfigs Option
5.2 JDBC API Implementation Notes
5.3 Java, JDBC and MySQL Types
5.4 Using Character Sets and Unicode
5.5 Connecting Securely Using SSL
5.6 Connecting Using PAM Authentication
5.7 Using Master/Slave Replication with ReplicationConnection
5.8 Mapping MySQL Error Numbers to JDBC SQLState Codes
6 JDBC Concepts
6.1 Connecting to MySQL Using the JDBC DriverManager + Interface
6.2 Using JDBC Statement Objects to Execute SQL
6.3 Using JDBC CallableStatements to Execute Stored + Procedures
6.4 Retrieving AUTO_INCREMENT Column Values through JDBC
7 Connection Pooling with Connector/J
8 Multi-Host Connections
8.1 Configuring Server Failover
8.2 Configuring Load Balancing with Connector/J
8.3 Configuring Master/Slave Replication with Connector/J
8.4 Advanced Load-balancing and Failover Configuration
9 Using the Connector/J Interceptor Classes
10 Using Connector/J with Tomcat
11 Using Connector/J with JBoss
12 Using Connector/J with Spring
12.1 Using JdbcTemplate
12.2 Transactional JDBC Access
12.3 Connection Pooling with Spring
13 Using Connector/J with GlassFish
13.1 A Simple JSP Application with GlassFish, Connector/J and MySQL
13.2 A Simple Servlet with GlassFish, Connector/J and MySQL
14 Using Connector/J with MySQL Fabric
15 Troubleshooting Connector/J Applications
16 Known Issues and Limitations
17 Connector/J Support
17.1 Connector/J Community Support
17.2 How to Report Connector/J Bugs or Problems

Preface and Legal Notices

+ This manual describes how to install, configure, and develop + database applications using MySQL Connector/J, the JDBC driver for + communicating with MySQL servers. +

Legal Notices

+ Copyright © 1998, 2016, Oracle and/or its affiliates. All + rights reserved. +

+ This software and related documentation are provided under a license + agreement containing restrictions on use and disclosure and are + protected by intellectual property laws. Except as expressly + permitted in your license agreement or allowed by law, you may not + use, copy, reproduce, translate, broadcast, modify, license, + transmit, distribute, exhibit, perform, publish, or display any + part, in any form, or by any means. Reverse engineering, + disassembly, or decompilation of this software, unless required by + law for interoperability, is prohibited. +

+ The information contained herein is subject to change without notice + and is not warranted to be error-free. If you find any errors, + please report them to us in writing. +

+ If this is software or related documentation that is delivered to + the U.S. Government or anyone licensing it on behalf of the U.S. + Government, then the following notice is applicable: +

+ U.S. GOVERNMENT END USERS: Oracle programs, including any operating + system, integrated software, any programs installed on the hardware, + and/or documentation, delivered to U.S. Government end users are + "commercial computer software" pursuant to the applicable Federal + Acquisition Regulation and agency-specific supplemental regulations. + As such, use, duplication, disclosure, modification, and adaptation + of the programs, including any operating system, integrated + software, any programs installed on the hardware, and/or + documentation, shall be subject to license terms and license + restrictions applicable to the programs. No other rights are granted + to the U.S. Government. +

+ This software or hardware is developed for general use in a variety + of information management applications. It is not developed or + intended for use in any inherently dangerous applications, including + applications that may create a risk of personal injury. If you use + this software or hardware in dangerous applications, then you shall + be responsible to take all appropriate fail-safe, backup, + redundancy, and other measures to ensure its safe use. Oracle + Corporation and its affiliates disclaim any liability for any + damages caused by use of this software or hardware in dangerous + applications. +

+ Oracle and Java are registered trademarks of Oracle and/or its + affiliates. Other names may be trademarks of their respective + owners. +

+ Intel and Intel Xeon are trademarks or registered trademarks of + Intel Corporation. All SPARC trademarks are used under license and + are trademarks or registered trademarks of SPARC International, Inc. + AMD, Opteron, the AMD logo, and the AMD Opteron logo are trademarks + or registered trademarks of Advanced Micro Devices. UNIX is a + registered trademark of The Open Group. +

+ This software or hardware and documentation may provide access to or + information about content, products, and services from third + parties. Oracle Corporation and its affiliates are not responsible + for and expressly disclaim all warranties of any kind with respect + to third-party content, products, and services unless otherwise set + forth in an applicable agreement between you and Oracle. Oracle + Corporation and its affiliates will not be responsible for any loss, + costs, or damages incurred due to your access to or use of + third-party content, products, or services, except as set forth in + an applicable agreement between you and Oracle. +

+ For information about Oracle's commitment to accessibility, visit + the Oracle Accessibility Program website at + + + + http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc. +

+ Oracle customers that have purchased support have access to + electronic support through My Oracle Support. For information, + visit + + + + http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info + or visit + http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs + if you are hearing impaired. +

+ This documentation is NOT distributed under a GPL license. Use of + this documentation is subject to the following terms: +

+ You may create a printed copy of this documentation solely for your + own personal use. Conversion to other formats is allowed as long as + the actual content is not altered or edited in any way. You shall + not publish or distribute this documentation in any form or on any + media, except if you distribute the documentation in a manner + similar to how Oracle disseminates it (that is, electronically for + download on a Web site with the software) or on a CD-ROM or similar + medium, provided however that the documentation is disseminated + together with the software on the same medium. Any other use, such + as any dissemination of printed copies or use of this documentation, + in whole or in part, in another publication, requires the prior + written consent from an authorized representative of Oracle. Oracle + and/or its affiliates reserve any and all rights to this + documentation not expressly granted above. +

Chapter 1 Overview of MySQL Connector/J

+ MySQL provides connectivity for client applications developed in + the Java programming language with MySQL Connector/J, a driver + that implements the + Java + Database Connectivity (JDBC) API. +

+ MySQL Connector/J is a JDBC Type 4 driver. Different versions are + available that are compatible with the JDBC 3.0 and JDBC 4.x + specifications (see Chapter 2, Connector/J Versions). The + Type 4 designation means that the driver is a pure Java + implementation of the MySQL protocol and does not rely on the + MySQL client libraries. +

+ For large-scale programs that use common design patterns of data + access, consider using one of the popular persistence frameworks + such as Hibernate, + Spring's JDBC + templates or Ibatis + SQL Maps to reduce the amount of JDBC code for you to + debug, tune, secure, and maintain. +

Key Topics

Chapter 2 Connector/J Versions

+ There are currently four versions of MySQL Connector/J available: +

  • + Connector/J 5.1 is a Type 4 pure Java JDBC driver, which + conforms to the JDBC 3.0, 4.0, 4.1, and 4.2 specifications. It + provides compatibility with all the functionality of MySQL, + including 4.1, 5.0, 5.1, 5.5, 5.6, and 5.7. Connector/J 5.1 + provides ease of development features, including + auto-registration with the Driver Manager, standardized + validity checks, categorized SQLExceptions, support for large + update counts, support for local and offset date-time variants + from the java.time package, support for + JDBC-4.x XML processing, support for per connection client + information, and support for the + NCHAR, + NVARCHAR and + NCLOB data types. This release also + includes all bug fixes up to and including Connector/J 5.0.6. +

  • + Connector/J 5.0 provides support for all the functionality + offered by Connector/J 3.1 and includes distributed + transaction (XA) support. +

  • + Connector/J 3.1 was designed for connectivity to MySQL 4.1 and + MySQL 5.0 servers and provides support for all the + functionality in MySQL 5.0 except distributed transaction (XA) + support. +

  • + Connector/J 3.0 provides core functionality and was designed + for connectivity to MySQL 3.x or MySQL 4.1 servers, although + it provides basic compatibility with later versions of MySQL. + Connector/J 3.0 does not support server-side prepared + statements, and does not support any of the features in + versions of MySQL later than 4.1. +

+ The following table summarizes the Connector/J versions available, + along with the details of JDBC driver type, what version of the + JDBC API it supports, what versions of MySQL Server it works with, + and whether it is currently supported or not: +

+

Table 2.1 Summary of Connector/J Versions

Connector/J versionDriver TypeJDBC versionMySQL Server versionStatus
5.143.0, 4.0, 4.1, 4.24.1, 5.0, 5.1, 5.5, 5.6, 5.7Recommended version
5.043.04.1, 5.0Released version
3.143.04.1, 5.0Obsolete
3.043.03.x, 4.1Obsolete


+

+ The current recommended version for Connector/J is 5.1. This guide + covers all four connector versions, with specific notes given + where a setting applies to a specific option. +

2.1 Connector/J Release Notes and Change History

+ For details of new features and bug fixes in each Connector/J + release, see the + MySQL + Connector/J Release Notes. +

2.2 Java Versions Supported

+ The following table summarizes what version of JRE is required + to use Connector/J with Java applications, and what version of + JDK is required to build Connector/J source code: +

+

Table 2.2 Summary of Java Versions Required by Connector/J

Connector/J versionJRE SupportedJDK required (to build source code)
5.11.5.x, 1.6.x, 1.7.x, 1.8.x1.6.x and 1.5.x
5.01.3.x, 1.4.x, 1.5.x, 1.6.x1.4.2, 1.5.x, 1.6.x
3.11.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x1.4.2, 1.5.x, 1.6.x
3.01.2.x, 1.3.x, 1.4.x, 1.5.x, 1.6.x1.4.2, 1.5.x, 1.6.x


+

+ If you are building Connector/J from source code using the + source distribution (see + Section 3.4, “Installing from Source”), you must use + JDK 1.4.2 or newer to compile the package for Connector/J 5.0 or + earlier. For Connector/J 5.1, you must have both JDK-1.6.x + AND JDK-1.5.x installed to be able to build + the source code. +

+ JRE 1.7 support requires Connector/J 5.1.21 and higher. +

+ JRE 1.8 is required for Connector/J 5.1 to connect to MySQL + 5.6.27 and later and 5.7 with SSL/TLS when using some cipher + suites. +

+ Several JDBC 4.1 methods were implemented for the first time in + Connector/J 5.1.21. +

+ Because of the implementation of + java.sql.Savepoint, Connector/J 3.1.0 and + newer will not run on a Java runtime older than 1.4 unless the + class verifier is turned off (by setting the + -Xverify:none option to the Java runtime). This + is because the class verifier will try to load the class + definition for java.sql.Savepoint even + though it is not accessed by the driver unless you actually use + savepoint functionality. +

+ Caching functionality provided by Connector/J 3.1.0 or newer is + also not available on JVMs older than 1.4.x, as it relies on + java.util.LinkedHashMap, which was first + available in JDK-1.4.0. +

+ MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x. +

Chapter 3 Connector/J Installation

+ You can install the Connector/J package using either the binary or + source distribution. The binary distribution provides the easiest + method for installation; the source distribution lets you + customize your installation further. With either solution, you + manually add the Connector/J location to your Java + CLASSPATH. +

+ If you are upgrading from a previous version, read the upgrade + information in Section 3.3, “Upgrading from an Older Version” + before continuing. +

+ Connector/J is also available as part of the Maven project. For + more information and to download the Connector/J JAR files, see + the + Maven + repository. +

3.1 Installing Connector/J from a Binary Distribution

+ For the easiest method of installation, use the binary + distribution of the Connector/J package. The binary distribution + is available either as a tar/gzip or zip file. Extract it to a + suitable location, then optionally make the information about + the package available by changing your + CLASSPATH (see + Section 3.2, “Installing the Driver and Configuring the CLASSPATH”). +

+ MySQL Connector/J is distributed as a .zip or + .tar.gz archive containing the sources, the + class files, and the JAR archive named + mysql-connector-java-version-bin.jar. +

+ Starting with Connector/J 3.1.9, the .class + files that constitute the JAR files are only included as part of + the driver JAR file. +

+ Starting with Connector/J 3.1.8, the archive also includes a + debug build of the driver in a file named + mysql-connector-java-version-bin-g.jar. + Do not use the debug build of the driver unless instructed to do + so when reporting a problem or a bug, as it is not designed to + be run in production environments, and will have adverse + performance impact when used. The debug binary also depends on + the Aspect/J runtime library, which is located in the + src/lib/aspectjrt.jar file that comes with + the Connector/J distribution. +

+ Use the appropriate graphical or command-line utility to extract + the distribution (for example, WinZip for the .zip archive, and + tar for the .tar.gz archive). Because there + are potentially long file names in the distribution, we use the + GNU tar archive format. Use GNU tar (or an application that + understands the GNU tar archive format) to unpack the .tar.gz + variant of the distribution. +

3.2 Installing the Driver and Configuring the CLASSPATH

+ Once you have extracted the distribution archive, you can + install the driver by placing + mysql-connector-java-version-bin.jar + in your classpath, either by adding the full path to + it to your CLASSPATH environment variable, or + by directly specifying it with the command line switch + -cp when starting the JVM. +

+ To use the driver with the JDBC + DriverManager, use + com.mysql.jdbc.Driver as the class that + implements java.sql.Driver. +

+ You can set the CLASSPATH environment + variable under Unix, Linux, or OS X either locally for a user + within their .profile, + .login or other login file. You can also set + it globally by editing the global + /etc/profile file. +

+ For example, add the Connector/J driver to your + CLASSPATH using one of the following forms, + depending on your command shell: +

+# Bourne-compatible shell (sh, ksh, bash, zsh):
+shell> export CLASSPATH=/path/mysql-connector-java-ver-bin.jar:$CLASSPATH
+
+# C shell (csh, tcsh):
+shell> setenv CLASSPATH /path/mysql-connector-java-ver-bin.jar:$CLASSPATH
+

+ For Windows platforms, you set the environment variable through + the System Control Panel. +

+ To use MySQL Connector/J with an application server such as + GlassFish, Tomcat, or JBoss, read your vendor's documentation + for more information on how to configure third-party class + libraries, as most application servers ignore the + CLASSPATH environment variable. For + configuration examples for some J2EE application servers, see + Chapter 7, Connection Pooling with Connector/J, + Section 8.2, “Configuring Load Balancing with Connector/J”, + and + Section 8.4, “Advanced Load-balancing and Failover Configuration”. + + + + However, the authoritative source for JDBC connection pool + configuration information for your particular application server + is the documentation for that application server. +

+ If you are developing servlets or JSPs, and your application + server is J2EE-compliant, you can put the driver's + .jar file in the + WEB-INF/lib subdirectory of your webapp, as + this is a standard location for third party class libraries in + J2EE web applications. +

+ You can also use the MysqlDataSource or + MysqlConnectionPoolDataSource classes in + the com.mysql.jdbc.jdbc2.optional package, if + your J2EE application server supports or requires them. Starting + with Connector/J 5.0.0, the + javax.sql.XADataSource interface is + implemented using the + com.mysql.jdbc.jdbc2.optional.MysqlXADataSource + class, which supports XA distributed transactions when used in + combination with MySQL server version 5.0 and later. +

+ The various MysqlDataSource classes + support the following parameters (through standard set + mutators): +

  • + user +

  • + password +

  • + serverName (see the previous section + about failover hosts) +

  • + databaseName +

  • + port +

3.3 Upgrading from an Older Version

+ This section has information for users who are upgrading from + one version of Connector/J to another, or to a new version of + the MySQL server that supports a more recent level of JDBC. A + newer version of Connector/J might include changes to support + new features, improve existing functionality, or comply with new + standards. +

3.3.1 Upgrading to MySQL Connector/J 5.1.x

  • + In Connector/J 5.0.x and earlier, the alias for a table in + a SELECT statement is + returned when accessing the result set metadata using + ResultSetMetaData.getColumnName(). + This behavior however is not JDBC compliant, and in + Connector/J 5.1, this behavior has been changed so that + the original table name, rather than the alias, is + returned. +

    + The JDBC-compliant behavior is designed to let API users + reconstruct the DML statement based on the metadata within + ResultSet and + ResultSetMetaData. +

    + You can get the alias for a column in a result set by + calling + ResultSetMetaData.getColumnLabel(). + To use the old noncompliant behavior with + ResultSetMetaData.getColumnName(), + use the useOldAliasMetadataBehavior + option and set the value to true. +

    + In Connector/J 5.0.x, the default value of + useOldAliasMetadataBehavior was + true, but in Connector/J 5.1 this was + changed to a default value of false. +

3.3.2 JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or Newer

  • + Using the UTF-8 Character Encoding - + Prior to MySQL server version 4.1, the UTF-8 character + encoding was not supported by the server, however the JDBC + driver could use it, allowing storage of multiple + character sets in latin1 tables on the + server. +

    + Starting with MySQL-4.1, this functionality is deprecated. + If you have applications that rely on this functionality, + and can not upgrade them to use the official Unicode + character support in MySQL server version 4.1 or newer, + add the following property to your connection URL: +

    + useOldUTF8Behavior=true +

  • + Server-side Prepared Statements - + Connector/J 3.1 will automatically detect and use + server-side prepared statements when they are available + (MySQL server version 4.1.0 and newer). If your + application encounters issues with server-side prepared + statements, you can revert to the older client-side + emulated prepared statement code that is still presently + used for MySQL servers older than 4.1.0 with the following + connection property: +

    + useServerPrepStmts=false +

3.3.3 Upgrading from MySQL Connector/J 3.0 to 3.1

+ Connector/J 3.1 is designed to be backward-compatible with + Connector/J 3.0 as much as possible. Major changes are + isolated to new functionality exposed in MySQL-4.1 and newer, + which includes Unicode character sets, server-side prepared + statements, SQLState codes returned in + error messages by the server and various performance + enhancements that can be enabled or disabled using + configuration properties. +

  • + Unicode Character Sets: + See the next section, as well as + Character Set Support, for information on this MySQL + feature. If you have something misconfigured, it will + usually show up as an error with a message similar to + Illegal mix of collations. +

  • + Server-side Prepared + Statements: Connector/J 3.1 will automatically + detect and use server-side prepared statements when they + are available (MySQL server version 4.1.0 and newer). +

    + Starting with version 3.1.7, the driver scans SQL you are + preparing using all variants of + Connection.prepareStatement() to + determine if it is a supported type of statement to + prepare on the server side, and if it is not supported by + the server, it instead prepares it as a client-side + emulated prepared statement. You can disable this feature + by passing + emulateUnsupportedPstmts=false in your + JDBC URL. +

    + If your application encounters issues with server-side + prepared statements, you can revert to the older + client-side emulated prepared statement code that is still + presently used for MySQL servers older than 4.1.0 with the + connection property + useServerPrepStmts=false. +

  • + Datetimes with all-zero + components (0000-00-00 ...): These + values cannot be represented reliably in Java. Connector/J + 3.0.x always converted them to NULL + when being read from a ResultSet. +

    + Connector/J 3.1 throws an exception by default when these + values are encountered, as this is the most correct + behavior according to the JDBC and SQL standards. This + behavior can be modified using the + zeroDateTimeBehavior configuration + property. The permissible values are: +

    • + exception (the default), which + throws an SQLException with an SQLState of + S1009. +

    • + convertToNull, which returns + NULL instead of the date. +

    • + round, which rounds the date to the + nearest closest value which is + 0001-01-01. +

    + Starting with Connector/J 3.1.7, + ResultSet.getString() can be decoupled + from this behavior using + noDatetimeStringSync=true (the default + value is false) so that you can + retrieve the unaltered all-zero value as a String. Note + that this also precludes using any time zone conversions, + therefore the driver will not allow you to enable + noDatetimeStringSync and + useTimezone at the same time. +

  • + New SQLState Codes: + Connector/J 3.1 uses SQL:1999 SQLState codes returned by + the MySQL server (if supported), which are different from + the legacy X/Open state codes that Connector/J 3.0 uses. + If connected to a MySQL server older than MySQL-4.1.0 (the + oldest version to return SQLStates as part of the error + code), the driver will use a built-in mapping. You can + revert to the old mapping by using the configuration + property useSqlStateCodes=false. +

  • + ResultSet.getString(): + Calling ResultSet.getString() on a + BLOB column will now return + the address of the byte[] array that + represents it, instead of a String + representation of the BLOB. + BLOB values have no + character set, so they cannot be converted to + java.lang.Strings without data loss or + corruption. +

    + To store strings in MySQL with LOB behavior, use one of + the TEXT types, which the + driver will treat as a java.sql.Clob. +

  • + Debug builds: Starting + with Connector/J 3.1.8 a debug build of the driver in a + file named + mysql-connector-java-version-bin-g.jar + is shipped alongside the normal binary jar file that is + named + mysql-connector-java-version-bin.jar. +

    + Starting with Connector/J 3.1.9, we do not ship the + .class files unbundled, they are only + available in the JAR archives that ship with the driver. +

    + Do not use the debug build of the driver unless instructed + to do so when reporting a problem or bug, as it is not + designed to be run in production environments, and will + have adverse performance impact when used. The debug + binary also depends on the Aspect/J runtime library, which + is located in the + src/lib/aspectjrt.jar file that comes + with the Connector/J distribution. +

3.4 Installing from Source

Caution

+ To just get MySQL Connector/J up and running on your system, + install Connector/J + using a + standard binary release distribution. Instructions in + this section is only for users who, for various reasons, want + to compile Connector/J from source. +

+ The requirements and steps for installing from source + Connector/J 5.1.37 + or later, + 5.1.34 to + 5.1.36, and + 5.1.33 or + earlier are different; check the section below that is + relevant for the version you want. +

Installing Connector/J 5.1.37 or later from source.  + To install MySQL Connector/J from its source tree on GitHub, + you need to have the following software on your system: +

+ To check out and compile MySQL Connector/J, follow these steps: +

  1. + Check out the code from the source code repository for MySQL + Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j; + for the latest release of the Connector/J 5.1 series, use + the following command: +

    +shell> git clone https://github.com/mysql/mysql-connector-j.git
    +

    + To check out a release other than the latest one, use the + --branch option to specify the revision tag + for it: + +

    +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connector-j.git
    +

    +

    + Under the current directory, the commands create a + mysql-connector-j subdirectory , which + contains the code you want. +

  2. + Make sure that you have both JDK 1.8.x + AND JDK 1.5.x installed. You need both + JDKs because besides supporting JDBC from 4.0 to 4.2, + Connector/J 5.1 also supports JDBC 3.0, which is an older + version and requires the older JDK 1.5.x. +

  3. + Consider also having JRE 1.6.x installed. This is optional: + if JRE 1.6.x is not available or not supplied to Ant with + the property com.mysql.jdbc.java6.rtjar, + the Java 8 bootstrap classes will be used. A warning will be + returned, saying that the bootstrap class path was not set + with the option to compile sources written for Java 6. +

  4. + Place the required junit.jar file in a + separate directory—for example, + /home/username/ant-extralibs. +

  5. + In the same directory for extra libraries described in the + last step, create a directory named + hibernate4, and put under it all the + .jar files you can find under the + /lib/required/ folder in the Hibernate + ORM 4 Final release bundle. +

  6. + Change your current working directory to the + mysql-connector-j directory created in + step 1 above. +

  7. + In the directory, create a file named + build.properties to indicate to Ant the + locations of the root directories for your JDK 1.8.x and JDK + 1.5.x installations, the location of the + rt.jar of your JRE 1.6.x (optional), + and the location of the extra libraries. The file should + contain the following property settings, with the + “path_to_*” parts + replaced by the appropriate filepaths: +

    +com.mysql.jdbc.jdk8=path_to_jdk_1.8
    +com.mysql.jdbc.jdk5=path_to_jdk_1.5
    +com.mysql.jdbc.java6.rtjar=path_to_rt.jar_under_jre_1.6/rt.jar
    +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries 
    +

    + Alternatively, you can set the values of those properties + through the Ant -D options. +

  8. + Issue the following command to compile the driver and create + a .jar file for Connector/J: +

    +shell> ant dist
    +

    + This creates a build directory in the + current directory, where all the build output goes. A + directory is created under the build + directory, whose name includes the version number of the + release you are building. That directory contains the + sources, the compiled .class files, and + a .jar file for deployment. For more + information and other possible targets, including those that + create a fully packaged distribution, issue the following + command: +

    +shell> ant -projecthelp
    +
  9. + Install the newly created .jar file for + the JDBC driver as you would install a binary + .jar file you download from MySQL by + following the instructions given in + Section 3.2, “Installing the Driver and Configuring the CLASSPATH”. +

+ Note that a package containing both the binary and source code + for Connector/J 5.1 can also be found at + + Connector/J 5.1 Download. +

Installing Connector/J 5.1.34 to 5.1.36 from source.  + To install MySQL Connector/J 5.1.34 to 5.1.36 from the + Connector/J source tree on GitHub, make sure that you have the + following software on your system: +

+ To check out and compile MySQL Connector/J, follow these steps: +

  1. + Check out the code from the source code repository for MySQL + Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j, + using the --branch option to specify the + revision tag for release 5.1.xx: + +

    +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connector-j.git
    +

    +

    + Under the current directory, the commands create a + mysql-connector-j subdirectory , which + contains the code you want. +

  2. + Make sure that you have both JDK 1.6.x + AND JDK 1.5.x installed. You need both + JDKs because Connector/J 5.1 supports both JDBC 3.0 (which + has existed prior to JDK 1.6.x) and JDBC 4.0. +

  3. + Place the required junit.jar file in a + separate directory—for example, + /home/username/ant-extralibs. +

  4. + In the same directory for extra libraries described in the + last step, create a directory named + hibernate4, and put under it all the + .jar files you can find under the + /lib/required/ folder in the Hibernate + ORM 4 Final release bundle. +

  5. + Change your current working directory to the + mysql-connector-j directory created in + step 1 above. +

  6. + In the directory, create a file named + build.properties to indicate to Ant the + locations of the root directories for your JDK 1.5.x and JDK + 1.6.x installations, as well as the location of the extra + libraries. The file should contain the following property + settings, with the + “path_to_*” parts + replaced by the appropriate filepaths: +

    +com.mysql.jdbc.jdk5=path_to_jdk_1.5
    +com.mysql.jdbc.jdk6=path_to_jdk_1.6
    +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries 
    +

    + Alternatively, you can set the values of those properties + through the Ant -D options. +

  7. + Issue the following command to compile the driver and create + a .jar file for Connector/J: +

    +shell> ant dist
    +

    + This creates a build directory in the + current directory, where all the build output goes. A + directory is created under the build + directory, whose name includes the version number of the + release you are building. That directory contains the + sources, the compiled .class files, and + a .jar file for deployment. For more + information and other possible targets, including those that + create a fully packaged distribution, issue the following + command: +

    +shell> ant -projecthelp
    +
  8. + Install the newly created .jar file for + the JDBC driver as you would install a binary + .jar file you download from MySQL by + following the instructions given in + Section 3.2, “Installing the Driver and Configuring the CLASSPATH”. +

Installing Connector/J 5.1.33 or earlier from the source tree.  + To install MySQL Connector/J 5.1.33 or earlier from the + Connector/J source tree on GitHub, make sure that you have the + following software on your system: +

+ To check out and compile a specific branch of MySQL Connector/J, + follow these steps: +

  1. + Check out the code from the source code repository for MySQL + Connector/J located on GitHub at + https://github.com/mysql/mysql-connector-j, + using the --branch option to specify the + revision tag for release 5.1.xx: + +

    +shell> git clone --branch 5.1.xx https://github.com/mysql/mysql-connector-j.git
    +

    +

    + Under the current directory, the commands create a + mysql-connector-j subdirectory , which + contains the code you want. +

  2. + To build Connector/J 5.1, make sure that you have both JDK + 1.6.x AND JDK 1.5.x installed. You need + both JDKs because Connector/J 5.1 supports both JDBC 3.0 + (which has existed prior to JDK 1.6.x) and JDBC 4.0. Set + your JAVA_HOME environment variable to + the path to the JDK 1.5.x installation. +

  3. + Place the required ant-contrib.jar file + (in exactly that name, without the version number in it; + rename the jar file if needed) and + junit.jar file in a separate + directory—for example, + /home/username/ant-extralibs. +

  4. + In the same directory for extra libraries described in the + last step, create a directory named + hibernate4, and put under it all the + .jar files you can find under the + /lib/required/ folder in the Hibernate + ORM 4 Final release bundle. +

  5. + Change your current working directory to the + mysql-connector-j directory created in + step 1 above. +

  6. + In the directory, create a file named + build.properties to indicate to Ant the + locations of the Javac and rt.jar of + your JDK 1.6.x, as well as the location of the extra + libraries. The file should contain the following property + settings, with the + “path_to_*” parts + replaced by the appropriate filepaths: +

    +com.mysql.jdbc.java6.javac=path_to_javac_1.6/javac
    +com.mysql.jdbc.java6.rtjar=path_to_rt.jar_under_jdk_1.6/rt.jar
    +com.mysql.jdbc.extra.libs=path_to_folder_for_extra_libraries
    +

    + Alternatively, you can set the values of those properties + through the Ant -D options. +

  7. + Issue the following command to compile the driver and create + a .jar file for Connector/J: +

    +shell> ant dist
    +

    + This creates a build directory in the + current directory, where all the build output goes. A + directory is created under the build + directory, whose name includes the version number of the + release you are building. That directory contains the + sources, the compiled .class files, and + a .jar file for deployment. For more + information and other possible targets, including those that + create a fully packaged distribution, issue the following + command: +

    +shell> ant -projecthelp
    +
  8. + Install the newly created .jar file for + the JDBC driver as you would install a binary + .jar file you download from MySQL by + following the instructions given in + Section 3.2, “Installing the Driver and Configuring the CLASSPATH”. +

3.5 Testing Connector/J

+ The Connector/J source code repository or packages that are + shipped with source code include an extensive test suite, + containing test cases that can be executed independently. The + test cases are divided into the following categories: + +

  • + Functional or unit tests: Classes + from the package testsuite.simple. + Include test code for the main features of the + Connector/J. +

  • + Performance tests: Classes from the + package testsuite.perf. Include + test code to make measurements for the performance of + Connector/J. +

  • + Fabric tests: Classes from the + package testsuite.fabric. Includes + the code to test Fabric-specific features. These tests + require the setting of some special properties that are + not documented here. Consult the code or the + Fabric-related targets in the bundled Ant build file, + build.xml. +

  • + Regression tests: Classes from the + package testsuite.regression. + Includes code for testing bug and regression fixes. +

+

+ The bundled Ant build file contains targets like + test and test-multijvm, + which can facilitate the process of running the Connector/J + tests; see the target descriptions in the build file for + details. Besides the requirements for building Connector/J from + the source code described in + Section 3.4, “Installing from Source”, a number of the + tests also require the File System Service Provider 1.2 for the + Java Naming and Directory Interface (JNDI), available at + http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html)—place + the jar files downloaded from there into the + lib directory or in the directory pointed + to by the property com.mysql.jdbc.extra.libs. +

+ To run the test using Ant, in addition to the properties + required for Section 3.4, “Installing from Source”, + you must set the following properties in the + build.properties file or through the Ant + -D options: + +

  • + com.mysql.jdbc.testsuite.url: it + specifies the JDBC URL for connection to a MySQL test + server; see + Section 5.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J”. +

  • + com.mysql.jdbc.testsuite.jvm: the JVM + to be used for the tests. If the property is set, the + specified JVM will be used for all test cases except if it + points to a Java 5 directory, in which case any test cases + for JDBC 4.0 and later are run with the JVM supplied with + the property com.mysql.jdbc.jdk8 (for + 5.1.36 and earlier, supplied with the property + com.mysql.jdbc.jdk6). If the property + is not set, the JVM supplied with + com.mysql.jdbc.jdk5 will be used to run + test cases for JDBC 3.0 and the one supplied with + com.mysql.jdbc.jdk8 (for 5.1.36 and + earlier, supplied with the property + com.mysql.jdbc.jdk6) will be used to + run test cases for JDBC 4.0 and later. +

+

+ After setting these parameters, run the tests with Ant in the + following ways: + +

  • + Building the test target with + ant test runs all test cases by default + on a single server instance. If you want to run a + particular test case, put the test's fully qualified class + names in the test variable; for + example: + +

    +shell > ant -Dtest=testsuite.simple.StringUtilsTest test

    + + You can also run individual tests in a test case by + specifying the names of the corresponding methods in the + methods variable, separating multiple + methods by commas; for example: + +

    +shell > ant -Dtest=testsuite.simple.StringUtilsTest -Dmethods=testIndexOfIgnoreCase,testGetBytes test
    +

    +

  • + Building the test-multijvm target with + ant test-multijvm runs all the test + cases + + + using multiple JVMs of different versions on multiple + server instances. For example, if you want to run the + tests using a Java 7 and a Java 8 JVM on three server + instances with different configurations, you will need to + use the following properties: + +

    +com.mysql.jdbc.testsuite.jvm.1=path_to_Java_7
    +com.mysql.jdbc.testsuite.jvm.2=path_to_Java_8
    +com.mysql.jdbc.testsuite.url.1=URL_to_1st_server
    +com.mysql.jdbc.testsuite.url.2=URL_to_2nd_server 
    +com.mysql.jdbc.testsuite.url.3=URL_to_3rd_server

    +

    + Unlike the target test, the target + test-multijvm only recognizes the + properties + com.mysql.jdbc.testsuite.jvm.N + and + com.mysql.jdbc.testsuite.url.N, + where N is a numeric suffice; the + same properties without the suffices are ignored by + test-multijvm. As with the target + test, if any of the + com.mysql.jdbc.testsuite.jvm.N + settings points to Java 5, then Ant relies on the property + com.mysql.jdbc.jdk8 to run the tests + specific to JDBC 4.0 and later. +

    + You can choose to run individual test cases or specific + tests by using the test or + methods property, as explained in the + last bullet for the target test. Each + test is run once per possible combination of JVMs and + server instances (that is, 6 times in total for in this + example). +

    + When a test for a certain JVM-server combination has + failed, test-multijvm does not throw an + error, but moves on to the next combination, until all + tests for all combinations are finished. +

+

+ While the test results are partially reported by the console, + complete reports in HTML and XML formats are provided: + +

  • + For results of test: view the HTML + report by opening + build/junit/unitregress/report/index.html. + XML version of the reports are located in the folder + build/junit/unitregress. +

  • + For results of test-multijvm: view the + HTML report for each JVM-server combination by opening + build/junit/MySQLN.server_version/operating_system_version/jvm-version/unitregress/report/index.html. + XML version of the reports are located in the folder + build/junit/MySQLN.server_version/operating_system_version/jvm-version/unitregress. +

+

Chapter 5 Connector/J (JDBC) Reference

+ This section of the manual contains reference material for MySQL + Connector/J. +

5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J

+ The name of the class that implements + java.sql.Driver in MySQL Connector/J is + com.mysql.jdbc.Driver. The + org.gjt.mm.mysql.Driver class name is also + usable for backward compatibility with MM.MySQL, the predecessor + of Connector/J. Use this class name when registering the driver, + or when configuring a software to use MySQL Connector/J. +

JDBC URL Format

+ The general format for a JDBC URL for connecting to a MySQL + server is as follows, with items in square brackets ([ ]) being + optional: +

+jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]] »
+[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
+

+ Here is a simple example for a connection URL: +

+jdbc:mysql://localhost:3306/sakila?profileSQL=true
+

+ Supply multiple hosts for a server failover setup (see + Chapter 8, Multi-Host Connections for + details): +

+# Connection URL for a server failover setup: 
+jdbc:mysql//primaryhost,secondaryhost1,secondaryhost2/test

+ There are specialized URL schemes for configuring Connector/J's + multi-host functions like load balancing and replication; here + are some examples (see + Chapter 8, Multi-Host Connections for + details): + +

+# Connection URL for load balancing: 
+jdbc:mysql:loadbalance://localhost:3306,localhost:3310/sakila
+
+# Connection URL for server replication: 
+jdbc:mysql:replication://master,slave1,slave2,slave3/test
+

+

Host and Port

+ If no hosts are not specified, the host name defaults to + 127.0.0.1. If the port for a host is not + specified, it defaults to 3306, the default + port number for MySQL servers. +

Initial Database for Connection

+ If the database is not specified, the connection is made with no + default database. In this case, either call the + setCatalog() method on the Connection + instance, or fully specify table names using the database name + (that is, SELECT + dbname.tablename.colname + FROM dbname.tablename...) in your SQL. Opening a + connection without specifying the database to use is generally + only useful when building tools that work with multiple + databases, such as GUI database managers. +

Note

+ Always use the Connection.setCatalog() + method to specify the desired database in JDBC applications, + rather than the USE + database statement. +

IPv6 Connections

+ For IPv6 connections, use this alternative syntax to specify + hosts in the URL (the same syntax can also be used for IPv4 + connections): + +

+jdbc:mysql://address=(key1=value)[(key2=value)]...[,address=(key3=value)[(key4=value)]...]...[/[database]]»
+[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...] 
+

+ + Supported keys include: +

  • + (protocol=tcp), or + (protocol=pipe) for named pipes on + Windows. +

  • + (path=path_to_pipe) + for named pipes. +

  • + (host=hostname) + for TCP connections. +

  • + (port=port_number) + for TCP connections. +

+ For example: +

+jdbc:mysql://address=(protocol=tcp)(host=localhost)(port=3306)/db 
+

+ Keys other than the four mentioned above are treated as + host-specific configuration properties, which allow per-host + overrides of any configuration property set for multi-host + connections (that is, when using failover, load balancing, or + replication). For example: + +

+# IPv6 Connection URL for a server failover setup: 
+jdbc:mysql//address=(protocol=tcp)(host=primaryhost)(port=3306),»
+address=(protocol=tcp)(host=secondaryhost1)(port=3310)(user=test2)/test
+
+# IPv6 Connection URL for load balancing: 
+jdbc:mysql:loadbalance://address=(protocol=tcp)(host=localhost)(port=3306)(user=test1),»
+address=(protocol=tcp)(host=localhost)(port=3310)(user=test2)/sakila
+
+# IPv6 Connection URL for server replication: 
+jdbc:mysql:replication://address=(protocol=tcp)(host=master)(port=3306)(user=test1),»
+address=(protocol=tcp)(host=slave1)(port=3310)(user=test2)/test
+

+ + Limit the overrides to user, password, network timeouts, and + statement and metadata cache sizes; the effects of other + per-host overrides are not defined. +

+ The ways to set the other configuration properties are the same + for IPv6 and IPv4 URLs; see + Setting Configuration Properties. +

Setting Configuration Properties

+ Configuration properties define how Connector/J will make a + connection to a MySQL server. Unless otherwise noted, properties + can be set for a DataSource object or for a + Connection object. +

+ Configuration properties can be set in one of the following + ways: +

  • + Using the set*() methods on MySQL + implementations of java.sql.DataSource + (which is the preferred method when using implementations of + java.sql.DataSource): +

    • + com.mysql.jdbc.jdbc2.optional.MysqlDataSource +

    • + com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource +

  • + As a key/value pair in the + java.util.Properties instance passed to + DriverManager.getConnection() or + Driver.connect() +

  • + As a JDBC URL parameter in the URL given to + java.sql.DriverManager.getConnection(), + java.sql.Driver.connect() or the MySQL + implementations of the + javax.sql.DataSource + setURL() method. If you specify a + configuration property in the URL without providing a value + for it, nothing will be set; for example, adding + useServerPrepStmts alone to the URL does + not make Connector/J use server-side prepared statements; + you need to add useServerPrepStmts=true. +

    Note

    + If the mechanism you use to configure a JDBC URL is + XML-based, use the XML character literal + &amp; to separate configuration + parameters, as the ampersand is a reserved character for + XML. +

+ The properties are listed in the following tables. +

Connection/Authentication.  +

Properties and Descriptions

+ user +

+ + + +

+ The user to connect as +

+ + + +

+ Since version: all versions +

+ password +

+ + + +

+ The password to use when connecting +

+ + + +

+ Since version: all versions +

+ socketFactory +

+ + + +

+ The name of the class that the driver should use for + creating socket connections to the server. This class + must implement the interface + 'com.mysql.jdbc.SocketFactory' and have public no-args + constructor. +

+ + + +

+ Default: com.mysql.jdbc.StandardSocketFactory +

+ + + +

+ Since version: 3.0.3 +

+ connectTimeout +

+ + + +

+ Timeout for socket connect (in milliseconds), with 0 + being no timeout. Only works on JDK-1.4 or newer. + Defaults to '0'. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 3.0.1 +

+ socketTimeout +

+ + + +

+ Timeout on network socket operations (0, the default + means no timeout). +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 3.0.1 +

+ connectionLifecycleInterceptors +

+ + + +

+ A comma-delimited list of classes that implement + "com.mysql.jdbc.ConnectionLifecycleInterceptor" that + should notified of connection lifecycle events + (creation, destruction, commit, rollback, setCatalog + and setAutoCommit) and potentially alter the execution + of these commands. ConnectionLifecycleInterceptors are + "stackable", more than one interceptor may be + specified via the configuration property as a + comma-delimited list, with the interceptors executed + in order from left to right. +

+ + + +

+ Since version: 5.1.4 +

+ useConfigs +

+ + + +

+ Load the comma-delimited list of configuration + properties before parsing the URL or applying + user-specified properties. These configurations are + explained in the 'Configurations' of the + documentation. +

+ + + +

+ Since version: 3.1.5 +

+ authenticationPlugins +

+ + + +

+ Comma-delimited list of classes that implement + com.mysql.jdbc.AuthenticationPlugin and which will be + used for authentication unless disabled by + "disabledAuthenticationPlugins" property. +

+ + + +

+ Since version: 5.1.19 +

+ defaultAuthenticationPlugin +

+ + + +

+ Name of a class implementing + com.mysql.jdbc.AuthenticationPlugin which will be used + as the default authentication plugin (see below). It + is an error to use a class which is not listed in + "authenticationPlugins" nor it is one of the built-in + plugins. It is an error to set as default a plugin + which was disabled with + "disabledAuthenticationPlugins" property. It is an + error to set this value to null or the empty string + (i.e. there must be at least a valid default + authentication plugin specified for the connection, + meeting all constraints listed above). +

+ + + +

+ Default: + com.mysql.jdbc.authentication.MysqlNativePasswordPlugin +

+ + + +

+ Since version: 5.1.19 +

+ disabledAuthenticationPlugins +

+ + + +

+ Comma-delimited list of classes implementing + com.mysql.jdbc.AuthenticationPlugin or mechanisms, + i.e. "mysql_native_password". The authentication + plugins or mechanisms listed will not be used for + authentication which will fail if it requires one of + them. It is an error to disable the default + authentication plugin (either the one named by + "defaultAuthenticationPlugin" property or the + hard-coded one if "defaultAuthenticationPlugin" + property is not set). +

+ + + +

+ Since version: 5.1.19 +

+ disconnectOnExpiredPasswords +

+ + + +

+ If "disconnectOnExpiredPasswords" is set to "false" + and password is expired then server enters "sandbox" + mode and sends ERR(08001, ER_MUST_CHANGE_PASSWORD) for + all commands that are not needed to set a new password + until a new password is set. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.23 +

+ interactiveClient +

+ + + +

+ Set the CLIENT_INTERACTIVE flag, which tells MySQL to + timeout connections based on INTERACTIVE_TIMEOUT + instead of WAIT_TIMEOUT +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.0 +

+ localSocketAddress +

+ + + +

+ Hostname or IP address given to explicitly configure + the interface that the driver will bind the client + side of the TCP/IP connection to when connecting. +

+ + + +

+ Since version: 5.0.5 +

+ propertiesTransform +

+ + + +

+ An implementation of + com.mysql.jdbc.ConnectionPropertiesTransform that the + driver will use to modify URL properties passed to the + driver before attempting a connection +

+ + + +

+ Since version: 3.1.4 +

+ useCompression +

+ + + +

+ Use zlib compression when communicating with the + server (true/false)? Defaults to 'false'. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.17 +

+

Networking.  +

Properties and Descriptions

+ socksProxyHost +

+ + + +

+ Name or IP address of SOCKS host to connect through. +

+ + + +

+ Since version: 5.1.34 +

+ socksProxyPort +

+ + + +

+ Port of SOCKS server. +

+ + + +

+ Default: 1080 +

+ + + +

+ Since version: 5.1.34 +

+ maxAllowedPacket +

+ + + +

+ Maximum allowed packet size to send to server. If not + set, the value of system variable 'max_allowed_packet' + will be used to initialize this upon connecting. This + value will not take effect if set larger than the + value of 'max_allowed_packet'. Also, due to an + internal dependency with the property + "blobSendChunkSize", this setting has a minimum value + of "8203" if "useServerPrepStmts" is set to "true". +

+ + + +

+ Default: -1 +

+ + + +

+ Since version: 5.1.8 +

+ tcpKeepAlive +

+ + + +

+ If connecting using TCP/IP, should the driver set + SO_KEEPALIVE? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.7 +

+ tcpNoDelay +

+ + + +

+ If connecting using TCP/IP, should the driver set + SO_TCP_NODELAY (disabling the Nagle Algorithm)? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.7 +

+ tcpRcvBuf +

+ + + +

+ If connecting using TCP/IP, should the driver set + SO_RCV_BUF to the given value? The default value of + '0', means use the platform default value for this + property) +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.0.7 +

+ tcpSndBuf +

+ + + +

+ If connecting using TCP/IP, should the driver set + SO_SND_BUF to the given value? The default value of + '0', means use the platform default value for this + property) +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.0.7 +

+ tcpTrafficClass +

+ + + +

+ If connecting using TCP/IP, should the driver set + traffic class or type-of-service fields ?See the + documentation for java.net.Socket.setTrafficClass() + for more information. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.0.7 +

+

High Availability and Clustering.  +

Properties and Descriptions

+ autoReconnect +

+ + + +

+ Should the driver try to re-establish stale and/or + dead connections? If enabled the driver will throw an + exception for a queries issued on a stale or dead + connection, which belong to the current transaction, + but will attempt reconnect before the next query + issued on the connection in a new transaction. The use + of this feature is not recommended, because it has + side effects related to session state and data + consistency when applications don't handle + SQLExceptions properly, and is only designed to be + used when you are unable to configure your application + to handle SQLExceptions resulting from dead and stale + connections properly. Alternatively, as a last option, + investigate setting the MySQL server variable + "wait_timeout" to a high value, rather than the + default of 8 hours. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 1.1 +

+ autoReconnectForPools +

+ + + +

+ Use a reconnection strategy appropriate for connection + pools (defaults to 'false') +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.3 +

+ failOverReadOnly +

+ + + +

+ When failing over in autoReconnect mode, should the + connection be set to 'read-only'? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.12 +

+ maxReconnects +

+ + + +

+ Maximum number of reconnects to attempt if + autoReconnect is true, default is '3'. +

+ + + +

+ Default: 3 +

+ + + +

+ Since version: 1.1 +

+ reconnectAtTxEnd +

+ + + +

+ If autoReconnect is set to true, should the driver + attempt reconnections at the end of every transaction? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.10 +

+ retriesAllDown +

+ + + +

+ When using loadbalancing or failover, the number of + times the driver should cycle through available hosts, + attempting to connect. Between cycles, the driver will + pause for 250ms if no servers are available. +

+ + + +

+ Default: 120 +

+ + + +

+ Since version: 5.1.6 +

+ initialTimeout +

+ + + +

+ If autoReconnect is enabled, the initial time to wait + between re-connect attempts (in seconds, defaults to + '2'). +

+ + + +

+ Default: 2 +

+ + + +

+ Since version: 1.1 +

+ roundRobinLoadBalance +

+ + + +

+ When autoReconnect is enabled, and failoverReadonly is + false, should we pick hosts to connect to on a + round-robin basis? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ queriesBeforeRetryMaster +

+ + + +

+ Number of queries to issue before falling back to the + primary host when failed over (when using multi-host + failover). Whichever condition is met first, + 'queriesBeforeRetryMaster' or + 'secondsBeforeRetryMaster' will cause an attempt to be + made to reconnect to the primary host. Setting both + properties to 0 disables the automatic fall back to + the primary host at transaction boundaries. Defaults + to 50. +

+ + + +

+ Default: 50 +

+ + + +

+ Since version: 3.0.2 +

+ secondsBeforeRetryMaster +

+ + + +

+ How long should the driver wait, when failed over, + before attempting to reconnect to the primary host? + Whichever condition is met first, + 'queriesBeforeRetryMaster' or + 'secondsBeforeRetryMaster' will cause an attempt to be + made to reconnect to the master. Setting both + properties to 0 disables the automatic fall back to + the primary host at transaction boundaries. Time in + seconds, defaults to 30 +

+ + + +

+ Default: 30 +

+ + + +

+ Since version: 3.0.2 +

+ allowMasterDownConnections +

+ + + +

+ By default, a replication-aware connection will fail + to connect when configured master hosts are all + unavailable at initial connection. Setting this + property to 'true' allows to establish the initial + connection, by failing over to the slave servers, in + read-only state. It won't prevent subsequent failures + when switching back to the master hosts i.e. by + setting the replication connection to read/write + state. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.27 +

+ allowSlaveDownConnections +

+ + + +

+ By default, a replication-aware connection will fail + to connect when configured slave hosts are all + unavailable at initial connection. Setting this + property to 'true' allows to establish the initial + connection. It won't prevent failures when switching + to slaves i.e. by setting the replication connection + to read-only state. The property + 'readFromMasterWhenNoSlaves' should be used for this + purpose. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.38 +

+ readFromMasterWhenNoSlaves +

+ + + +

+ Replication-aware connections distribute load by using + the master hosts when in read/write state and by using + the slave hosts when in read-only state. If, when + setting the connection to read-only state, none of the + slave hosts are available, an SQLExeception is thrown + back. Setting this property to 'true' allows to fail + over to the master hosts, while setting the connection + state to read-only, when no slave hosts are available + at switch instant. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.38 +

+ replicationEnableJMX +

+ + + +

+ Enables JMX-based management of load-balanced + connection groups, including live addition/removal of + hosts from load-balancing pool. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.27 +

+ selfDestructOnPingMaxOperations +

+ + + +

+ =If set to a non-zero value, the driver will report + close the connection and report failure when + Connection.ping() or Connection.isValid(int) is called + if the connection's count of commands sent to the + server exceeds this value. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.1.6 +

+ selfDestructOnPingSecondsLifetime +

+ + + +

+ If set to a non-zero value, the driver will report + close the connection and report failure when + Connection.ping() or Connection.isValid(int) is called + if the connection's lifetime exceeds this value. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.1.6 +

+ resourceId +

+ + + +

+ A globally unique name that identifies the resource + that this datasource or connection is connected to, + used for XAResource.isSameRM() when the driver can't + determine this value based on hostnames used in the + URL +

+ + + +

+ Since version: 5.0.1 +

+

Security.  +

Properties and Descriptions

+ allowMultiQueries +

+ + + +

+ Allow the use of ';' to delimit multiple queries + during one statement (true/false), defaults to + 'false', and does not affect the addBatch() and + executeBatch() methods, which instead rely on + rewriteBatchStatements. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.1 +

+ useSSL +

+ + + +

+ Use SSL when communicating with the server + (true/false), default is 'true' when connecting to + MySQL 5.5.45+, 5.6.26+ or 5.7.6+, otherwise default is + 'false' +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.2 +

+ requireSSL +

+ + + +

+ Require server support of SSL connection if + useSSL=true? (defaults to 'false'). +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.0 +

+ verifyServerCertificate +

+ + + +

+ If "useSSL" is set to "true", should the driver verify + the server's certificate? When using this feature, the + keystore parameters should be specified by the + "clientCertificateKeyStore*" properties, rather than + system properties. Default is 'false' when connecting + to MySQL 5.5.45+, 5.6.26+ or 5.7.6+ and "useSSL" was + not explicitly set to "true". Otherwise default is + 'true' +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.6 +

+ clientCertificateKeyStoreUrl +

+ + + +

+ URL to the client certificate KeyStore (if not + specified, use defaults) +

+ + + +

+ Since version: 5.1.0 +

+ clientCertificateKeyStoreType +

+ + + +

+ KeyStore type for client certificates (NULL or empty + means use the default, which is "JKS". Standard + keystore types supported by the JVM are "JKS" and + "PKCS12", your environment may have more available + depending on what security products are installed and + available to the JVM. +

+ + + +

+ Default: JKS +

+ + + +

+ Since version: 5.1.0 +

+ clientCertificateKeyStorePassword +

+ + + +

+ Password for the client certificates KeyStore +

+ + + +

+ Since version: 5.1.0 +

+ trustCertificateKeyStoreUrl +

+ + + +

+ URL to the trusted root certificate KeyStore (if not + specified, use defaults) +

+ + + +

+ Since version: 5.1.0 +

+ trustCertificateKeyStoreType +

+ + + +

+ KeyStore type for trusted root certificates (NULL or + empty means use the default, which is "JKS". Standard + keystore types supported by the JVM are "JKS" and + "PKCS12", your environment may have more available + depending on what security products are installed and + available to the JVM. +

+ + + +

+ Default: JKS +

+ + + +

+ Since version: 5.1.0 +

+ trustCertificateKeyStorePassword +

+ + + +

+ Password for the trusted root certificates KeyStore +

+ + + +

+ Since version: 5.1.0 +

+ enabledSSLCipherSuites +

+ + + +

+ If "useSSL" is set to "true", overrides the cipher + suites enabled for use on the underlying SSL sockets. + This may be required when using external JSSE + providers or to specify cipher suites compatible with + both MySQL server and used JVM. +

+ + + +

+ Since version: 5.1.35 +

+ allowLoadLocalInfile +

+ + + +

+ Should the driver allow use of 'LOAD DATA LOCAL + INFILE...' (defaults to 'true'). +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.3 +

+ allowUrlInLocalInfile +

+ + + +

+ Should the driver allow URLs in 'LOAD DATA LOCAL + INFILE' statements? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.4 +

+ allowPublicKeyRetrieval +

+ + + +

+ Allows special handshake roundtrip to get server RSA + public key directly from server. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.31 +

+ paranoid +

+ + + +

+ Take measures to prevent exposure sensitive + information in error messages and clear data + structures holding sensitive data when possible? + (defaults to 'false') +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.1 +

+ passwordCharacterEncoding +

+ + + +

+ What character encoding is used for passwords? Leaving + this set to the default value (null), uses the value + set in "characterEncoding" if there is one, otherwise + uses UTF-8 as default encoding. If the password + contains non-ASCII characters, the password encoding + must match what server encoding was set to when the + password was created. For passwords in other character + encodings, the encoding will have to be specified with + this property (or with "characterEncoding"), as it's + not possible for the driver to auto-detect this. +

+ + + +

+ Since version: 5.1.7 +

+ serverRSAPublicKeyFile +

+ + + +

+ File path to the server RSA public key file for + sha256_password authentication. If not specified, the + public key will be retrieved from the server. +

+ + + +

+ Since version: 5.1.31 +

+

Performance Extensions.  +

Properties and Descriptions

+ callableStmtCacheSize +

+ + + +

+ If 'cacheCallableStmts' is enabled, how many callable + statements should be cached? +

+ + + +

+ Default: 100 +

+ + + +

+ Since version: 3.1.2 +

+ metadataCacheSize +

+ + + +

+ The number of queries to cache ResultSetMetadata for + if cacheResultSetMetaData is set to 'true' (default + 50) +

+ + + +

+ Default: 50 +

+ + + +

+ Since version: 3.1.1 +

+ useLocalSessionState +

+ + + +

+ Should the driver refer to the internal values of + autocommit and transaction isolation that are set by + Connection.setAutoCommit() and + Connection.setTransactionIsolation() and transaction + state as maintained by the protocol, rather than + querying the database or blindly sending commands to + the database for commit() or rollback() method calls? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.7 +

+ useLocalTransactionState +

+ + + +

+ Should the driver use the in-transaction state + provided by the MySQL protocol to determine if a + commit() or rollback() should actually be sent to the + database? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.7 +

+ prepStmtCacheSize +

+ + + +

+ If prepared statement caching is enabled, how many + prepared statements should be cached? +

+ + + +

+ Default: 25 +

+ + + +

+ Since version: 3.0.10 +

+ prepStmtCacheSqlLimit +

+ + + +

+ If prepared statement caching is enabled, what's the + largest SQL the driver will cache the parsing for? +

+ + + +

+ Default: 256 +

+ + + +

+ Since version: 3.0.10 +

+ parseInfoCacheFactory +

+ + + +

+ Name of a class implementing + com.mysql.jdbc.CacheAdapterFactory, which will be used + to create caches for the parsed representation of + client-side prepared statements. +

+ + + +

+ Default: com.mysql.jdbc.PerConnectionLRUFactory +

+ + + +

+ Since version: 5.1.1 +

+ serverConfigCacheFactory +

+ + + +

+ Name of a class implementing + com.mysql.jdbc.CacheAdapterFactory<String, + Map<String, String>>, which will be used to + create caches for MySQL server configuration values +

+ + + +

+ Default: com.mysql.jdbc.PerVmServerConfigCacheFactory +

+ + + +

+ Since version: 5.1.1 +

+ alwaysSendSetIsolation +

+ + + +

+ Should the driver always communicate with the database + when Connection.setTransactionIsolation() is called? + If set to false, the driver will only communicate with + the database when the requested transaction isolation + is different than the whichever is newer, the last + value that was set via + Connection.setTransactionIsolation(), or the value + that was read from the server when the connection was + established. Note that useLocalSessionState=true will + force the same behavior as + alwaysSendSetIsolation=false, regardless of how + alwaysSendSetIsolation is set. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.7 +

+ maintainTimeStats +

+ + + +

+ Should the driver maintain various internal timers to + enable idle time calculations as well as more verbose + error messages when the connection to the server + fails? Setting this property to false removes at least + two calls to System.getCurrentTimeMillis() per query. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.9 +

+ useCursorFetch +

+ + + +

+ If connected to MySQL > 5.0.2, and setFetchSize() + > 0 on a statement, should that statement use + cursor-based fetching to retrieve rows? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.0 +

+ blobSendChunkSize +

+ + + +

+ Chunk size to use when sending BLOB/CLOBs via + ServerPreparedStatements. Note that this value cannot + exceed the value of "maxAllowedPacket" and, if that is + the case, then this value will be corrected + automatically. +

+ + + +

+ Default: 1048576 +

+ + + +

+ Since version: 3.1.9 +

+ cacheCallableStmts +

+ + + +

+ Should the driver cache the parsing stage of + CallableStatements +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ cachePrepStmts +

+ + + +

+ Should the driver cache the parsing stage of + PreparedStatements of client-side prepared statements, + the "check" for suitability of server-side prepared + and server-side prepared statements themselves? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.10 +

+ cacheResultSetMetadata +

+ + + +

+ Should the driver cache ResultSetMetaData for + Statements and PreparedStatements? (Req. JDK-1.4+, + true/false, default 'false') +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.1 +

+ cacheServerConfiguration +

+ + + +

+ Should the driver cache the results of 'SHOW + VARIABLES' and 'SHOW COLLATION' on a per-URL basis? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.5 +

+ defaultFetchSize +

+ + + +

+ The driver will call setFetchSize(n) with this value + on all newly-created Statements +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 3.1.9 +

+ dontCheckOnDuplicateKeyUpdateInSQL +

+ + + +

+ Stops checking if every INSERT statement contains the + "ON DUPLICATE KEY UPDATE" clause. As a side effect, + obtaining the statement's generated keys information + will return a list where normally it wouldn't. Also be + aware that, in this case, the list of generated keys + returned may not be accurate. The effect of this + property is canceled if set simultaneously with + 'rewriteBatchedStatements=true'. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.32 +

+ dontTrackOpenResources +

+ + + +

+ The JDBC specification requires the driver to + automatically track and close resources, however if + your application doesn't do a good job of explicitly + calling close() on statements or result sets, this can + cause memory leakage. Setting this property to true + relaxes this constraint, and can be more memory + efficient for some applications. Also the automatic + closing of the Statement and current ResultSet in + Statement.closeOnCompletion() and + Statement.getMoreResults + ([Statement.CLOSE_CURRENT_RESULT | + Statement.CLOSE_ALL_RESULTS]), respectively, ceases to + happen. This property automatically sets + holdResultsOpenOverStatementClose=true. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.7 +

+ dynamicCalendars +

+ + + +

+ Should the driver retrieve the default calendar when + required, or cache it per connection/session? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.5 +

+ elideSetAutoCommits +

+ + + +

+ If using MySQL-4.1 or newer, should the driver only + issue 'set autocommit=n' queries when the server's + state doesn't match the requested state by + Connection.setAutoCommit(boolean)? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.3 +

+ enableEscapeProcessing +

+ + + +

+ Sets the default escape processing behavior for + Statement objects. The method + Statement.setEscapeProcessing() can be used to specify + the escape processing behavior for an individual + Statement object. Default escape processing behavior + in prepared statements must be defined with the + property 'processEscapeCodesForPrepStmts'. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.37 +

+ enableQueryTimeouts +

+ + + +

+ When enabled, query timeouts set via + Statement.setQueryTimeout() use a shared + java.util.Timer instance for scheduling. Even if the + timeout doesn't expire before the query is processed, + there will be memory used by the TimerTask for the + given timeout which won't be reclaimed until the time + the timeout would have expired if it hadn't been + cancelled by the driver. High-load environments might + want to consider disabling this functionality. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.6 +

+ holdResultsOpenOverStatementClose +

+ + + +

+ Should the driver close result sets on + Statement.close() as required by the JDBC + specification? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.7 +

+ largeRowSizeThreshold +

+ + + +

+ What size result set row should the JDBC driver + consider "large", and thus use a more memory-efficient + way of representing the row internally? +

+ + + +

+ Default: 2048 +

+ + + +

+ Since version: 5.1.1 +

+ loadBalanceStrategy +

+ + + +

+ If using a load-balanced connection to connect to SQL + nodes in a MySQL Cluster/NDB configuration (by using + the URL prefix "jdbc:mysql:loadbalance://"), which + load balancing algorithm should the driver use: (1) + "random" - the driver will pick a random host for each + request. This tends to work better than round-robin, + as the randomness will somewhat account for spreading + loads where requests vary in response time, while + round-robin can sometimes lead to overloaded nodes if + there are variations in response times across the + workload. (2) "bestResponseTime" - the driver will + route the request to the host that had the best + response time for the previous transaction. +

+ + + +

+ Default: random +

+ + + +

+ Since version: 5.0.6 +

+ locatorFetchBufferSize +

+ + + +

+ If 'emulateLocators' is configured to 'true', what + size buffer should be used when fetching BLOB data for + getBinaryInputStream? +

+ + + +

+ Default: 1048576 +

+ + + +

+ Since version: 3.2.1 +

+ readOnlyPropagatesToServer +

+ + + +

+ Should the driver issue appropriate statements to + implicitly set the transaction access mode on server + side when Connection.setReadOnly() is called? Setting + this property to 'true' enables InnoDB read-only + potential optimizations but also requires an extra + roundtrip to set the right transaction state. Even if + this property is set to 'false', the driver will do + its best effort to prevent the execution of + database-state-changing queries. Requires minimum of + MySQL 5.6. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.35 +

+ rewriteBatchedStatements +

+ + + +

+ Should the driver use multiqueries (irregardless of + the setting of "allowMultiQueries") as well as + rewriting of prepared statements for INSERT into + multi-value inserts when executeBatch() is called? + Notice that this has the potential for SQL injection + if using plain java.sql.Statements and your code + doesn't sanitize input correctly. Notice that for + prepared statements, server-side prepared statements + can not currently take advantage of this rewrite + option, and that if you don't specify stream lengths + when using PreparedStatement.set*Stream(), the driver + won't be able to determine the optimum number of + parameters per batch and you might receive an error + from the driver that the resultant packet is too + large. Statement.getGeneratedKeys() for these + rewritten statements only works when the entire batch + includes INSERT statements. Please be aware using + rewriteBatchedStatements=true with INSERT .. ON + DUPLICATE KEY UPDATE that for rewritten statement + server returns only one value as sum of all affected + (or found) rows in batch and it isn't possible to map + it correctly to initial statements; in this case + driver returns 0 as a result of each batch statement + if total count was 0, and the + Statement.SUCCESS_NO_INFO as a result of each batch + statement if total count was > 0. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.13 +

+ useDirectRowUnpack +

+ + + +

+ Use newer result set row unpacking code that skips a + copy from network buffers to a MySQL packet instance + and instead reads directly into the result set row + data buffers. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.1 +

+ useDynamicCharsetInfo +

+ + + +

+ Should the driver use a per-connection cache of + character set information queried from the server when + necessary, or use a built-in static mapping that is + more efficient, but isn't aware of custom character + sets or character sets implemented after the release + of the JDBC driver? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.6 +

+ useFastDateParsing +

+ + + +

+ Use internal String->Date/Time/Timestamp conversion + routines to avoid excessive object creation? This is + part of the legacy date-time code, thus the property + has an effect only when "useLegacyDatetimeCode=true." +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.5 +

+ useFastIntParsing +

+ + + +

+ Use internal String->Integer conversion routines to + avoid excessive object creation? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.4 +

+ useJvmCharsetConverters +

+ + + +

+ Always use the character encoding routines built into + the JVM, rather than using lookup tables for + single-byte character sets? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.1 +

+ useReadAheadInput +

+ + + +

+ Use newer, optimized non-blocking, buffered input + stream when reading from the server? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.5 +

+

Debugging/Profiling.  +

Properties and Descriptions

+ logger +

+ + + +

+ The name of a class that implements + "com.mysql.jdbc.log.Log" that will be used to log + messages to. (default is + "com.mysql.jdbc.log.StandardLogger", which logs to + STDERR) +

+ + + +

+ Default: com.mysql.jdbc.log.StandardLogger +

+ + + +

+ Since version: 3.1.1 +

+ gatherPerfMetrics +

+ + + +

+ Should the driver gather performance metrics, and + report them via the configured logger every + 'reportMetricsIntervalMillis' milliseconds? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ profileSQL +

+ + + +

+ Trace queries and their execution/fetch times to the + configured logger (true/false) defaults to 'false' +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.0 +

+ profileSql +

+ + + +

+ Deprecated, use 'profileSQL' instead. Trace queries + and their execution/fetch times on STDERR (true/false) + defaults to 'false' +

+ + + +

+ Since version: 2.0.14 +

+ reportMetricsIntervalMillis +

+ + + +

+ If 'gatherPerfMetrics' is enabled, how often should + they be logged (in ms)? +

+ + + +

+ Default: 30000 +

+ + + +

+ Since version: 3.1.2 +

+ maxQuerySizeToLog +

+ + + +

+ Controls the maximum length/size of a query that will + get logged when profiling or tracing +

+ + + +

+ Default: 2048 +

+ + + +

+ Since version: 3.1.3 +

+ packetDebugBufferSize +

+ + + +

+ The maximum number of packets to retain when + 'enablePacketDebug' is true +

+ + + +

+ Default: 20 +

+ + + +

+ Since version: 3.1.3 +

+ slowQueryThresholdMillis +

+ + + +

+ If 'logSlowQueries' is enabled, how long should a + query (in ms) before it is logged as 'slow'? +

+ + + +

+ Default: 2000 +

+ + + +

+ Since version: 3.1.2 +

+ slowQueryThresholdNanos +

+ + + +

+ If 'useNanosForElapsedTime' is set to true, and this + property is set to a non-zero value, the driver will + use this threshold (in nanosecond units) to determine + if a query was slow. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.0.7 +

+ useUsageAdvisor +

+ + + +

+ Should the driver issue 'usage' warnings advising + proper and efficient usage of JDBC and MySQL + Connector/J to the log (true/false, defaults to + 'false')? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.1 +

+ autoGenerateTestcaseScript +

+ + + +

+ Should the driver dump the SQL it is executing, + including server-side prepared statements to STDERR? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.9 +

+ autoSlowLog +

+ + + +

+ Instead of using slowQueryThreshold* to determine if a + query is slow enough to be logged, maintain statistics + that allow the driver to determine queries that are + outside the 99th percentile? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.4 +

+ clientInfoProvider +

+ + + +

+ The name of a class that implements the + com.mysql.jdbc.JDBC4ClientInfoProvider interface in + order to support JDBC-4.0's + Connection.get/setClientInfo() methods +

+ + + +

+ Default: com.mysql.jdbc.JDBC4CommentClientInfoProvider +

+ + + +

+ Since version: 5.1.0 +

+ dumpMetadataOnColumnNotFound +

+ + + +

+ Should the driver dump the field-level metadata of a + result set into the exception message when + ResultSet.findColumn() fails? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.13 +

+ dumpQueriesOnException +

+ + + +

+ Should the driver dump the contents of the query sent + to the server in the message for SQLExceptions? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.3 +

+ enablePacketDebug +

+ + + +

+ When enabled, a ring-buffer of 'packetDebugBufferSize' + packets will be kept, and dumped when exceptions are + thrown in key areas in the driver's code +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.3 +

+ explainSlowQueries +

+ + + +

+ If 'logSlowQueries' is enabled, should the driver + automatically issue an 'EXPLAIN' on the server and + send the results to the configured log at a WARN + level? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ includeInnodbStatusInDeadlockExceptions +

+ + + +

+ Include the output of "SHOW ENGINE INNODB STATUS" in + exception messages when deadlock exceptions are + detected? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.7 +

+ includeThreadDumpInDeadlockExceptions +

+ + + +

+ Include a current Java thread dump in exception + messages when deadlock exceptions are detected? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.15 +

+ includeThreadNamesAsStatementComment +

+ + + +

+ Include the name of the current thread as a comment + visible in "SHOW PROCESSLIST", or in Innodb deadlock + dumps, useful in correlation with + "includeInnodbStatusInDeadlockExceptions=true" and + "includeThreadDumpInDeadlockExceptions=true". +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.15 +

+ logSlowQueries +

+ + + +

+ Should queries that take longer than + 'slowQueryThresholdMillis' be logged? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ logXaCommands +

+ + + +

+ Should the driver log XA commands sent by + MysqlXaConnection to the server, at the DEBUG level of + logging? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.5 +

+ profilerEventHandler +

+ + + +

+ Name of a class that implements the interface + com.mysql.jdbc.profiler.ProfilerEventHandler that will + be used to handle profiling/tracing events. +

+ + + +

+ Default: + com.mysql.jdbc.profiler.LoggingProfilerEventHandler +

+ + + +

+ Since version: 5.1.6 +

+ resultSetSizeThreshold +

+ + + +

+ If the usage advisor is enabled, how many rows should + a result set contain before the driver warns that it + is suspiciously large? +

+ + + +

+ Default: 100 +

+ + + +

+ Since version: 5.0.5 +

+ traceProtocol +

+ + + +

+ Should trace-level network protocol be logged? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.2 +

+ useNanosForElapsedTime +

+ + + +

+ For profiling/debugging functionality that measures + elapsed time, should the driver try to use nanoseconds + resolution if available (JDK >= 1.5)? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.7 +

+

Miscellaneous.  +

Properties and Descriptions

+ useUnicode +

+ + + +

+ Should the driver use Unicode character encodings when + handling strings? Should only be used when the driver + can't determine the character set mapping, or you are + trying to 'force' the driver to use a character set + that MySQL either doesn't natively support (such as + UTF-8), true/false, defaults to 'true' +

+ + + +

+ Default: true +

+ + + +

+ Since version: 1.1g +

+ characterEncoding +

+ + + +

+ If 'useUnicode' is set to true, what character + encoding should the driver use when dealing with + strings? (defaults is to 'autodetect') +

+ + + +

+ Since version: 1.1g +

+ characterSetResults +

+ + + +

+ Character set to tell the server to return results as. +

+ + + +

+ Since version: 3.0.13 +

+ connectionAttributes +

+ + + +

+ A comma-delimited list of user-defined key:value pairs + (in addition to standard MySQL-defined key:value + pairs) to be passed to MySQL Server for display as + connection attributes in the + PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS table. + Example usage: + connectionAttributes=key1:value1,key2:value2 This + functionality is available for use with MySQL Server + version 5.6 or later only. Earlier versions of MySQL + Server do not support connection attributes, causing + this configuration option to be ignored. Setting + connectionAttributes=none will cause connection + attribute processing to be bypassed, for situations + where Connection creation/initialization speed is + critical. +

+ + + +

+ Since version: 5.1.25 +

+ connectionCollation +

+ + + +

+ If set, tells the server to use this collation via + 'set collation_connection' +

+ + + +

+ Since version: 3.0.13 +

+ useBlobToStoreUTF8OutsideBMP +

+ + + +

+ Tells the driver to treat [MEDIUM/LONG]BLOB columns as + [LONG]VARCHAR columns holding text encoded in UTF-8 + that has characters outside the BMP (4-byte + encodings), which MySQL server can't handle natively. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.3 +

+ utf8OutsideBmpExcludedColumnNamePattern +

+ + + +

+ When "useBlobToStoreUTF8OutsideBMP" is set to "true", + column names matching the given regex will still be + treated as BLOBs unless they match the regex specified + for "utf8OutsideBmpIncludedColumnNamePattern". The + regex must follow the patterns used for the + java.util.regex package. +

+ + + +

+ Since version: 5.1.3 +

+ utf8OutsideBmpIncludedColumnNamePattern +

+ + + +

+ Used to specify exclusion rules to + "utf8OutsideBmpExcludedColumnNamePattern". The regex + must follow the patterns used for the java.util.regex + package. +

+ + + +

+ Since version: 5.1.3 +

+ loadBalanceEnableJMX +

+ + + +

+ Enables JMX-based management of load-balanced + connection groups, including live addition/removal of + hosts from load-balancing pool. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.13 +

+ sessionVariables +

+ + + +

+ A comma-separated list of name/value pairs to be sent + as SET SESSION ... to the server when the driver + connects. +

+ + + +

+ Since version: 3.1.8 +

+ useColumnNamesInFindColumn +

+ + + +

+ Prior to JDBC-4.0, the JDBC specification had a bug + related to what could be given as a "column name" to + ResultSet methods like findColumn(), or getters that + took a String property. JDBC-4.0 clarified "column + name" to mean the label, as given in an "AS" clause + and returned by ResultSetMetaData.getColumnLabel(), + and if no AS clause, the column name. Setting this + property to "true" will give behavior that is + congruent to JDBC-3.0 and earlier versions of the JDBC + specification, but which because of the specification + bug could give unexpected results. This property is + preferred over "useOldAliasMetadataBehavior" unless + you need the specific behavior that it provides with + respect to ResultSetMetadata. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.7 +

+ allowNanAndInf +

+ + + +

+ Should the driver allow NaN or +/- INF values in + PreparedStatement.setDouble()? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.5 +

+ autoClosePStmtStreams +

+ + + +

+ Should the driver automatically call .close() on + streams/readers passed as arguments via set*() + methods? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.12 +

+ autoDeserialize +

+ + + +

+ Should the driver automatically detect and + de-serialize objects stored in BLOB fields? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.5 +

+ blobsAreStrings +

+ + + +

+ Should the driver always treat BLOBs as Strings - + specifically to work around dubious metadata returned + by the server for GROUP BY clauses? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.8 +

+ cacheDefaultTimezone +

+ + + +

+ Caches client's default time zone. This results in + better performance when dealing with time zone + conversions in Date and Time data types, however it + won't be aware of time zone changes if they happen at + runtime. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.35 +

+ capitalizeTypeNames +

+ + + +

+ Capitalize type names in DatabaseMetaData? (usually + only useful when using WebObjects, true/false, + defaults to 'false') +

+ + + +

+ Default: true +

+ + + +

+ Since version: 2.0.7 +

+ clobCharacterEncoding +

+ + + +

+ The character encoding to use for sending and + retrieving TEXT, MEDIUMTEXT and LONGTEXT values + instead of the configured connection characterEncoding +

+ + + +

+ Since version: 5.0.0 +

+ clobberStreamingResults +

+ + + +

+ This will cause a 'streaming' ResultSet to be + automatically closed, and any outstanding data still + streaming from the server to be discarded if another + query is executed before all the data has been read + from the server. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.9 +

+ compensateOnDuplicateKeyUpdateCounts +

+ + + +

+ Should the driver compensate for the update counts of + "ON DUPLICATE KEY" INSERT statements (2 = 1, 0 = 1) + when using prepared statements? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.7 +

+ continueBatchOnError +

+ + + +

+ Should the driver continue processing batch commands + if one statement fails. The JDBC spec allows either + way (defaults to 'true'). +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.3 +

+ createDatabaseIfNotExist +

+ + + +

+ Creates the database given in the URL if it doesn't + yet exist. Assumes the configured user has permissions + to create databases. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.9 +

+ detectCustomCollations +

+ + + +

+ Should the driver detect custom charsets/collations + installed on server (true/false, defaults to 'false'). + If this option set to 'true' driver gets actual + charsets/collations from server each time connection + establishes. This could slow down connection + initialization significantly. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.29 +

+ emptyStringsConvertToZero +

+ + + +

+ Should the driver allow conversions from empty string + fields to numeric values of '0'? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.8 +

+ emulateLocators +

+ + + +

+ Should the driver emulate java.sql.Blobs with + locators? With this feature enabled, the driver will + delay loading the actual Blob data until the one of + the retrieval methods (getInputStream(), getBytes(), + and so forth) on the blob data stream has been + accessed. For this to work, you must use a column + alias with the value of the column to the actual name + of the Blob. The feature also has the following + restrictions: The SELECT that created the result set + must reference only one table, the table must have a + primary key; the SELECT must alias the original blob + column name, specified as a string, to an alternate + name; the SELECT must cover all columns that make up + the primary key. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.0 +

+ emulateUnsupportedPstmts +

+ + + +

+ Should the driver detect prepared statements that are + not supported by the server, and replace them with + client-side emulated versions? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.7 +

+ exceptionInterceptors +

+ + + +

+ Comma-delimited list of classes that implement + com.mysql.jdbc.ExceptionInterceptor. These classes + will be instantiated one per Connection instance, and + all SQLExceptions thrown by the driver will be allowed + to be intercepted by these interceptors, in a chained + fashion, with the first class listed as the head of + the chain. +

+ + + +

+ Since version: 5.1.8 +

+ functionsNeverReturnBlobs +

+ + + +

+ Should the driver always treat data from functions + returning BLOBs as Strings - specifically to work + around dubious metadata returned by the server for + GROUP BY clauses? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.8 +

+ generateSimpleParameterMetadata +

+ + + +

+ Should the driver generate simplified parameter + metadata for PreparedStatements when no metadata is + available either because the server couldn't support + preparing the statement, or server-side prepared + statements are disabled? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.5 +

+ getProceduresReturnsFunctions +

+ + + +

+ Pre-JDBC4 DatabaseMetaData API has only the + getProcedures() and getProcedureColumns() methods, so + they return metadata info for both stored procedures + and functions. JDBC4 was extended with the + getFunctions() and getFunctionColumns() methods and + the expected behaviours of previous methods are not + well defined. For JDBC4 and higher, default 'true' + value of the option means that calls of + DatabaseMetaData.getProcedures() and + DatabaseMetaData.getProcedureColumns() return metadata + for both procedures and functions as before, keeping + backward compatibility. Setting this property to + 'false' decouples Connector/J from its pre-JDBC4 + behaviours for DatabaseMetaData.getProcedures() and + DatabaseMetaData.getProcedureColumns(), forcing them + to return metadata for procedures only. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.26 +

+ ignoreNonTxTables +

+ + + +

+ Ignore non-transactional table warning for rollback? + (defaults to 'false'). +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.9 +

+ jdbcCompliantTruncation +

+ + + +

+ Should the driver throw java.sql.DataTruncation + exceptions when data is truncated as is required by + the JDBC specification when connected to a server that + supports warnings (MySQL 4.1.0 and newer)? This + property has no effect if the server sql-mode includes + STRICT_TRANS_TABLES. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.2 +

+ loadBalanceAutoCommitStatementRegex +

+ + + +

+ When load-balancing is enabled for auto-commit + statements (via + loadBalanceAutoCommitStatementThreshold), the + statement counter will only increment when the SQL + matches the regular expression. By default, every + statement issued matches. +

+ + + +

+ Since version: 5.1.15 +

+ loadBalanceAutoCommitStatementThreshold +

+ + + +

+ When auto-commit is enabled, the number of statements + which should be executed before triggering + load-balancing to rebalance. Default value of 0 causes + load-balanced connections to only rebalance when + exceptions are encountered, or auto-commit is disabled + and transactions are explicitly committed or rolled + back. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.1.15 +

+ loadBalanceBlacklistTimeout +

+ + + +

+ Time in milliseconds between checks of servers which + are unavailable, by controlling how long a server + lives in the global blacklist. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.1.0 +

+ loadBalanceConnectionGroup +

+ + + +

+ Logical group of load-balanced connections within a + classloader, used to manage different groups + independently. If not specified, live management of + load-balanced connections is disabled. +

+ + + +

+ Since version: 5.1.13 +

+ loadBalanceExceptionChecker +

+ + + +

+ Fully-qualified class name of custom exception + checker. The class must implement + com.mysql.jdbc.LoadBalanceExceptionChecker interface, + and is used to inspect SQLExceptions and determine + whether they should trigger fail-over to another host + in a load-balanced deployment. +

+ + + +

+ Default: + com.mysql.jdbc.StandardLoadBalanceExceptionChecker +

+ + + +

+ Since version: 5.1.13 +

+ loadBalancePingTimeout +

+ + + +

+ Time in milliseconds to wait for ping response from + each of load-balanced physical connections when using + load-balanced Connection. +

+ + + +

+ Default: 0 +

+ + + +

+ Since version: 5.1.13 +

+ loadBalanceSQLExceptionSubclassFailover +

+ + + +

+ Comma-delimited list of classes/interfaces used by + default load-balanced exception checker to determine + whether a given SQLException should trigger failover. + The comparison is done using + Class.isInstance(SQLException) using the thrown + SQLException. +

+ + + +

+ Since version: 5.1.13 +

+ loadBalanceSQLStateFailover +

+ + + +

+ Comma-delimited list of SQLState codes used by default + load-balanced exception checker to determine whether a + given SQLException should trigger failover. The + SQLState of a given SQLException is evaluated to + determine whether it begins with any value in the + comma-delimited list. +

+ + + +

+ Since version: 5.1.13 +

+ loadBalanceValidateConnectionOnSwapServer +

+ + + +

+ Should the load-balanced Connection explicitly check + whether the connection is live when swapping to a new + physical connection at commit/rollback? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.13 +

+ maxRows +

+ + + +

+ The maximum number of rows to return (0, the default + means return all rows). +

+ + + +

+ Default: -1 +

+ + + +

+ Since version: all versions +

+ netTimeoutForStreamingResults +

+ + + +

+ What value should the driver automatically set the + server setting 'net_write_timeout' to when the + streaming result sets feature is in use? (value has + unit of seconds, the value '0' means the driver will + not try and adjust this value) +

+ + + +

+ Default: 600 +

+ + + +

+ Since version: 5.1.0 +

+ noAccessToProcedureBodies +

+ + + +

+ When determining procedure parameter types for + CallableStatements, and the connected user can't + access procedure bodies through "SHOW CREATE + PROCEDURE" or select on mysql.proc should the driver + instead create basic metadata (all parameters reported + as IN VARCHARs, but allowing registerOutParameter() to + be called on them anyway) instead of throwing an + exception? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.3 +

+ noDatetimeStringSync +

+ + + +

+ Don't ensure that + ResultSet.getDatetimeType().toString().equals(ResultSet.getString()) +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.7 +

+ noTimezoneConversionForDateType +

+ + + +

+ Don't convert DATE values using the server time zone + if 'useTimezone'='true' or + 'useLegacyDatetimeCode'='false' +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.35 +

+ noTimezoneConversionForTimeType +

+ + + +

+ Don't convert TIME values using the server time zone + if 'useTimezone'='true' +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.0 +

+ nullCatalogMeansCurrent +

+ + + +

+ When DatabaseMetadataMethods ask for a 'catalog' + parameter, does the value null mean use the current + catalog? (this is not JDBC-compliant, but follows + legacy behavior from earlier versions of the driver) +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.8 +

+ nullNamePatternMatchesAll +

+ + + +

+ Should DatabaseMetaData methods that accept *pattern + parameters treat null the same as '%' (this is not + JDBC-compliant, however older versions of the driver + accepted this departure from the specification) +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.8 +

+ overrideSupportsIntegrityEnhancementFacility +

+ + + +

+ Should the driver return "true" for + DatabaseMetaData.supportsIntegrityEnhancementFacility() + even if the database doesn't support it to workaround + applications that require this method to return "true" + to signal support of foreign keys, even though the SQL + specification states that this facility contains much + more than just foreign key support (one such + application being OpenOffice)? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.12 +

+ padCharsWithSpace +

+ + + +

+ If a result set column has the CHAR type and the value + does not fill the amount of characters specified in + the DDL for the column, should the driver pad the + remaining characters with space (for ANSI compliance)? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.6 +

+ pedantic +

+ + + +

+ Follow the JDBC spec to the letter. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.0 +

+ pinGlobalTxToPhysicalConnection +

+ + + +

+ When using XAConnections, should the driver ensure + that operations on a given XID are always routed to + the same physical connection? This allows the + XAConnection to support "XA START ... JOIN" after "XA + END" has been called +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.1 +

+ populateInsertRowWithDefaultValues +

+ + + +

+ When using ResultSets that are CONCUR_UPDATABLE, + should the driver pre-populate the "insert" row with + default values from the DDL for the table used in the + query so those values are immediately available for + ResultSet accessors? This functionality requires a + call to the database for metadata each time a result + set of this type is created. If disabled (the + default), the default values will be populated by the + an internal call to refreshRow() which pulls back + default values and/or values changed by triggers. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.5 +

+ processEscapeCodesForPrepStmts +

+ + + +

+ Should the driver process escape codes in queries that + are prepared? Default escape processing behavior in + non-prepared statements must be defined with the + property 'enableEscapeProcessing'. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.12 +

+ queryTimeoutKillsConnection +

+ + + +

+ If the timeout given in Statement.setQueryTimeout() + expires, should the driver forcibly abort the + Connection instead of attempting to abort the query? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.9 +

+ relaxAutoCommit +

+ + + +

+ If the version of MySQL the driver connects to does + not support transactions, still allow calls to + commit(), rollback() and setAutoCommit() (true/false, + defaults to 'false')? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 2.0.13 +

+ retainStatementAfterResultSetClose +

+ + + +

+ Should the driver retain the Statement reference in a + ResultSet after ResultSet.close() has been called. + This is not JDBC-compliant after JDBC-4.0. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.11 +

+ rollbackOnPooledClose +

+ + + +

+ Should the driver issue a rollback() when the logical + connection in a pool is closed? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.15 +

+ runningCTS13 +

+ + + +

+ Enables workarounds for bugs in Sun's JDBC compliance + testsuite version 1.3 +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.7 +

+ sendFractionalSeconds +

+ + + +

+ Send fractional part from TIMESTAMP seconds. If set to + false, the nanoseconds value of TIMESTAMP values will + be truncated before sending any data to the server. + This option applies only to prepared statements, + callable statements or updatable result sets. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.37 +

+ serverTimezone +

+ + + +

+ Override detection/mapping of time zone. Used when + time zone from server doesn't map to Java time zone +

+ + + +

+ Since version: 3.0.2 +

+ statementInterceptors +

+ + + +

+ A comma-delimited list of classes that implement + "com.mysql.jdbc.StatementInterceptor" that should be + placed "in between" query execution to influence the + results. StatementInterceptors are "chainable", the + results returned by the "current" interceptor will be + passed on to the next in in the chain, from + left-to-right order, as specified in this property. +

+ + + +

+ Since version: 5.1.1 +

+ strictFloatingPoint +

+ + + +

+ Used only in older versions of compliance test +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.0 +

+ strictUpdates +

+ + + +

+ Should the driver do strict checking (all primary keys + selected) of updatable result sets (true, false, + defaults to 'true')? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.4 +

+ tinyInt1isBit +

+ + + +

+ Should the driver treat the datatype TINYINT(1) as the + BIT type (because the server silently converts BIT + -> TINYINT(1) when creating tables)? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.16 +

+ transformedBitIsBoolean +

+ + + +

+ If the driver converts TINYINT(1) to a different type, + should it use BOOLEAN instead of BIT for future + compatibility with MySQL-5.0, as MySQL-5.0 has a BIT + type? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.9 +

+ treatUtilDateAsTimestamp +

+ + + +

+ Should the driver treat java.util.Date as a TIMESTAMP + for the purposes of PreparedStatement.setObject()? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.0.5 +

+ ultraDevHack +

+ + + +

+ Create PreparedStatements for prepareCall() when + required, because UltraDev is broken and issues a + prepareCall() for _all_ statements? (true/false, + defaults to 'false') +

+ + + +

+ Default: false +

+ + + +

+ Since version: 2.0.3 +

+ useAffectedRows +

+ + + +

+ Don't set the CLIENT_FOUND_ROWS flag when connecting + to the server (not JDBC-compliant, will break most + applications that rely on "found" rows vs. "affected + rows" for DML statements), but does cause "correct" + update counts from "INSERT ... ON DUPLICATE KEY + UPDATE" statements to be returned by the server. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.1.7 +

+ useGmtMillisForDatetimes +

+ + + +

+ Convert between session time zone and GMT before + creating Date and Timestamp instances (value of + 'false' leads to legacy behavior, 'true' leads to more + JDBC-compliant behavior)? This is part of the legacy + date-time code, thus the property has an effect only + when "useLegacyDatetimeCode=true." +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.12 +

+ useHostsInPrivileges +

+ + + +

+ Add '@hostname' to users in + DatabaseMetaData.getColumn/TablePrivileges() + (true/false), defaults to 'true'. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.2 +

+ useInformationSchema +

+ + + +

+ When connected to MySQL-5.0.7 or newer, should the + driver use the INFORMATION_SCHEMA to derive + information used by DatabaseMetaData? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.0 +

+ useJDBCCompliantTimezoneShift +

+ + + +

+ Should the driver use JDBC-compliant rules when + converting TIME/TIMESTAMP/DATETIME values' time zone + information for those JDBC arguments which take a + java.util.Calendar argument? This is part of the + legacy date-time code, thus the property has an effect + only when "useLegacyDatetimeCode=true." +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.0 +

+ useLegacyDatetimeCode +

+ + + +

+ Use code for DATE/TIME/DATETIME/TIMESTAMP handling in + result sets and statements that consistently handles + time zone conversions from client to server and back + again, or use the legacy code for these datatypes that + has been in the driver for backwards-compatibility? + Setting this property to 'false' voids the effects of + "useTimezone," "useJDBCCompliantTimezoneShift," + "useGmtMillisForDatetimes," and "useFastDateParsing." +

+ + + +

+ Default: true +

+ + + +

+ Since version: 5.1.6 +

+ useOldAliasMetadataBehavior +

+ + + +

+ Should the driver use the legacy behavior for "AS" + clauses on columns and tables, and only return aliases + (if any) for ResultSetMetaData.getColumnName() or + ResultSetMetaData.getTableName() rather than the + original column/table name? In 5.0.x, the default + value was true. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.4 +

+ useOldUTF8Behavior +

+ + + +

+ Use the UTF-8 behavior the driver did when + communicating with 4.0 and older servers +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.6 +

+ useOnlyServerErrorMessages +

+ + + +

+ Don't prepend 'standard' SQLState error messages to + error messages returned by the server. +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.15 +

+ useSSPSCompatibleTimezoneShift +

+ + + +

+ If migrating from an environment that was using + server-side prepared statements, and the configuration + property "useJDBCCompliantTimeZoneShift" set to + "true", use compatible behavior when not using + server-side prepared statements when sending TIMESTAMP + values to the MySQL server. +

+ + + +

+ Default: false +

+ + + +

+ Since version: 5.0.5 +

+ useServerPrepStmts +

+ + + +

+ Use server-side prepared statements if the server + supports them? +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.1.0 +

+ useSqlStateCodes +

+ + + +

+ Use SQL Standard state codes instead of 'legacy' + X/Open/SQL state codes (true/false), default is 'true' +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.3 +

+ useStreamLengthsInPrepStmts +

+ + + +

+ Honor stream length parameter in + PreparedStatement/ResultSet.setXXXStream() method + calls (true/false, defaults to 'true')? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.2 +

+ useTimezone +

+ + + +

+ Convert time/date types between client and server time + zones (true/false, defaults to 'false')? This is part + of the legacy date-time code, thus the property has an + effect only when "useLegacyDatetimeCode=true." +

+ + + +

+ Default: false +

+ + + +

+ Since version: 3.0.2 +

+ useUnbufferedInput +

+ + + +

+ Don't use BufferedInputStream for reading data from + the server +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.0.11 +

+ yearIsDateType +

+ + + +

+ Should the JDBC driver treat the MySQL type "YEAR" as + a java.sql.Date, or as a SHORT? +

+ + + +

+ Default: true +

+ + + +

+ Since version: 3.1.9 +

+ zeroDateTimeBehavior +

+ + + +

+ What should happen when the driver encounters DATETIME + values that are composed entirely of zeros (used by + MySQL to represent invalid dates)? Valid values are + "exception", "round" and "convertToNull". +

+ + + +

+ Default: exception +

+ + + +

+ Since version: 3.1.4 +

+

+ Connector/J also supports access to MySQL using named pipes on + Windows platforms with the + NamedPipeSocketFactory as a plugin-socket + factory. If you do not use a namedPipePath + property, the default of '\\.\pipe\MySQL' is + used. If you use the NamedPipeSocketFactory, + the host name and port number values in the JDBC URL are + ignored. To enable this feature, set the + socketFactory property: + +

+socketFactory=com.mysql.jdbc.NamedPipeSocketFactory
+

+

+ Named pipes only work when connecting to a MySQL server on the + same physical machine where the JDBC driver is running. In + simple performance tests, named pipe access is between 30%-50% + faster than the standard TCP/IP access. However, this varies per + system, and named pipes are slower than TCP/IP in many Windows + configurations. +

+ To create your own socket factories, follow the example code in + com.mysql.jdbc.NamedPipeSocketFactory, or + com.mysql.jdbc.StandardSocketFactory. +

5.1.1 Properties Files for the useConfigs Option

+ The useConfigs connection option is + convenient shorthand for specifying combinations of options + for particular scenarios. The argument values you can use with + this option correspond to the names of + .properties files within the Connector/J + mysql-connector-java-version-bin.jar + JAR file. For example, the Connector/J 5.1.9 driver includes + the following configuration properties files: +

+$ unzip mysql-connector-java-5.1.19-bin.jar '*/configs/*'
+Archive:  mysql-connector-java-5.1.19-bin.jar
+   creating: com/mysql/jdbc/configs/
+  inflating: com/mysql/jdbc/configs/3-0-Compat.properties  
+  inflating: com/mysql/jdbc/configs/5-0-Compat.properties  
+  inflating: com/mysql/jdbc/configs/clusterBase.properties  
+  inflating: com/mysql/jdbc/configs/coldFusion.properties  
+  inflating: com/mysql/jdbc/configs/fullDebug.properties  
+  inflating: com/mysql/jdbc/configs/maxPerformance.properties  
+  inflating: com/mysql/jdbc/configs/solarisMaxPerformance.properties 
+

+ To specify one of these combinations of options, specify + useConfigs=3-0-Compat, + useConfigs=maxPerformance, and so on. The + following sections show the options that are part of each + useConfigs setting. For the details of why + each one is included, see the comments in the + .properties files. +

3-0-Compat

+emptyStringsConvertToZero=true
+jdbcCompliantTruncation=false
+noDatetimeStringSync=true
+nullCatalogMeansCurrent=true
+nullNamePatternMatchesAll=true
+transformedBitIsBoolean=false
+dontTrackOpenResources=true
+zeroDateTimeBehavior=convertToNull
+useServerPrepStmts=false
+autoClosePStmtStreams=true
+processEscapeCodesForPrepStmts=false
+useFastDateParsing=false
+populateInsertRowWithDefaultValues=false
+useDirectRowUnpack=false
+

5-0-Compat

+useDirectRowUnpack=false
+

clusterBase

+autoReconnect=true
+failOverReadOnly=false
+roundRobinLoadBalance=true
+

coldFusion

+useDynamicCharsetInfo=false
+alwaysSendSetIsolation=false
+useLocalSessionState=true
+autoReconnect=true
+

fullDebug

+profileSQL=true
+gatherPerfMetrics=true
+useUsageAdvisor=true
+logSlowQueries=true
+explainSlowQueries=true
+

maxPerformance

+cachePrepStmts=true
+cacheCallableStmts=true
+cacheServerConfiguration=true
+useLocalSessionState=true
+elideSetAutoCommits=true
+alwaysSendSetIsolation=false
+enableQueryTimeouts=false
+

solarisMaxPerformance

+useUnbufferedInput=false
+useReadAheadInput=false
+maintainTimeStats=false
+

5.2 JDBC API Implementation Notes

+ MySQL Connector/J, as a rigorous implementation of the + JDBC + API, passes all of the tests in the publicly available + version of Oracle's JDBC compliance test suite. The JDBC + specification is flexible on how certain functionality should be + implemented. This section gives details on an + interface-by-interface level about implementation decisions that + might affect how you code applications with MySQL Connector/J. +

  • + BLOB +

    + Starting with Connector/J version 3.1.0, you can emulate + BLOBs with locators by adding the property + emulateLocators=true to your JDBC URL. + Using this method, the driver will delay loading the actual + BLOB data until you retrieve the other data and then use + retrieval methods (getInputStream(), + getBytes(), and so forth) on the BLOB + data stream. +

    + You must use a column alias with the value of the column to + the actual name of the BLOB, for example: +

    +SELECT id, 'data' as blob_data from blobtable
    +

    + You must also follow these rules: +

    • + The SELECT must reference + only one table. The table must have a + primary key. +

    • + The SELECT must alias the + original BLOB column name, specified as a string, to an + alternate name. +

    • + The SELECT must cover all + columns that make up the primary key. +

    + The BLOB implementation does not allow in-place modification + (they are copies, as reported by the + DatabaseMetaData.locatorsUpdateCopies() + method). Because of this, use the corresponding + PreparedStatement.setBlob() or + ResultSet.updateBlob() (in the case of + updatable result sets) methods to save changes back to the + database. +

  • + CallableStatement +

    + Starting with Connector/J 3.1.1, stored procedures are + supported when connecting to MySQL version 5.0 or newer + using the CallableStatement + interface. Currently, the + getParameterMetaData() method of + CallableStatement is not supported. +

  • + CLOB +

    + The CLOB implementation does not allow in-place modification + (they are copies, as reported by the + DatabaseMetaData.locatorsUpdateCopies() + method). Because of this, use the + PreparedStatement.setClob() method to + save changes back to the database. The JDBC API does not + have a ResultSet.updateClob() method. +

  • + Connection +

    + Unlike the pre-Connector/J JDBC driver + (MM.MySQL), the + isClosed() method does not ping the + server to determine if it is available. In accordance with + the JDBC specification, it only returns true if + closed() has been called on the + connection. If you need to determine if the connection is + still valid, issue a simple query, such as SELECT + 1. The driver will throw an exception if the + connection is no longer valid. +

  • + DatabaseMetaData +

    + Foreign key + information + (getImportedKeys()/getExportedKeys() + and getCrossReference()) is only + available from InnoDB tables. + The driver uses SHOW CREATE + TABLE to retrieve this information, so if any + other storage engines add support for foreign keys, the + driver would transparently support them as well. +

  • + PreparedStatement +

    + PreparedStatements are implemented by the driver, as MySQL + does not have a prepared statement feature. Because of this, + the driver does not implement + getParameterMetaData() or + getMetaData() as it would require the + driver to have a complete SQL parser in the client. +

    + Starting with version 3.1.0 MySQL Connector/J, server-side + prepared statements and binary-encoded result sets are used + when the server supports them. +

    + Take care when using a server-side prepared statement with + large parameters that are + set using setBinaryStream(), + setAsciiStream(), + setUnicodeStream(), + setBlob(), or + setClob(). To re-execute the statement + with any large parameter changed to a nonlarge parameter, + call clearParameters() and set all + parameters again. The reason for this is as follows: +

    • + During both server-side prepared statements and + client-side emulation, large data is exchanged only when + PreparedStatement.execute() is + called. +

    • + Once that has been done, the stream used to read the + data on the client side is closed (as per the JDBC + spec), and cannot be read from again. +

    • + If a parameter changes from large to nonlarge, the + driver must reset the server-side state of the prepared + statement to allow the parameter that is being changed + to take the place of the prior large value. This removes + all of the large data that has already been sent to the + server, thus requiring the data to be re-sent, using the + setBinaryStream(), + setAsciiStream(), + setUnicodeStream(), + setBlob() or + setClob() method. +

    + Consequently, to change the type of a parameter to a + nonlarge one, you must call + clearParameters() and set all + parameters of the prepared statement again before it can be + re-executed. +

  • + ResultSet +

    + By default, ResultSets are completely retrieved and stored + in memory. In most cases this is the most efficient way to + operate and, due to the design of the MySQL network + protocol, is easier to implement. If you are working with + ResultSets that have a large number of rows or large values + and cannot allocate heap space in your JVM for the memory + required, you can tell the driver to stream the results back + one row at a time. +

    + To enable this functionality, create a + Statement instance in the following + manner: +

    +stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
    +              java.sql.ResultSet.CONCUR_READ_ONLY);
    +stmt.setFetchSize(Integer.MIN_VALUE);
    +

    + The combination of a forward-only, read-only result set, + with a fetch size of Integer.MIN_VALUE + serves as a signal to the driver to stream result sets + row-by-row. After this, any result sets created with the + statement will be retrieved row-by-row. +

    + There are some caveats with this approach. You must read all + of the rows in the result set (or close it) before you can + issue any other queries on the connection, or an exception + will be thrown. +

    + The earliest the locks these statements hold can be released + (whether they be MyISAM table-level locks + or row-level locks in some other storage engine such as + InnoDB) is when the statement completes. +

    + If the statement is within scope of a transaction, then + locks are released when the transaction completes (which + implies that the statement needs to complete first). As with + most other databases, statements are not complete until all + the results pending on the statement are read or the active + result set for the statement is closed. +

    + Therefore, if using streaming results, process them as + quickly as possible if you want to maintain concurrent + access to the tables referenced by the statement producing + the result set. +

  • + ResultSetMetaData +

    + The isAutoIncrement() method only works + when using MySQL servers 4.0 and newer. +

  • + Statement +

    + When using versions of the JDBC driver earlier than 3.2.1, + and connected to server versions earlier than 5.0.3, the + setFetchSize() method has no effect, + other than to toggle result set streaming as described + above. +

    + Connector/J 5.0.0 and later include support for both + Statement.cancel() and + Statement.setQueryTimeout(). Both require + MySQL 5.0.0 or newer server, and require a separate + connection to issue the + KILL QUERY + statement. In the case of + setQueryTimeout(), the implementation + creates an additional thread to handle the timeout + functionality. +

    Note

    + Failures to cancel the statement for + setQueryTimeout() may manifest + themselves as RuntimeException rather + than failing silently, as there is currently no way to + unblock the thread that is executing the query being + cancelled due to timeout expiration and have it throw the + exception instead. +

    Note

    + The MySQL statement + KILL QUERY + (which is what the driver uses to implement + Statement.cancel()) is + non-deterministic; thus, avoid the use of + Statement.cancel() if possible. If no + query is in process, the next query issued will be killed + by the server. This race condition is guarded against as + of Connector/J 5.1.18. +

    + MySQL does not support SQL cursors, and the JDBC driver + doesn't emulate them, so setCursorName() + has no effect. +

    + Connector/J 5.1.3 and later include two additional methods: +

    • + setLocalInfileInputStream() sets an + InputStream instance that will be + used to send data to the MySQL server for a + LOAD DATA + LOCAL INFILE statement rather than a + FileInputStream or + URLInputStream that represents the + path given as an argument to the statement. +

      + This stream will be read to completion upon execution of + a LOAD DATA + LOCAL INFILE statement, and will automatically + be closed by the driver, so it needs to be reset before + each call to execute*() that would + cause the MySQL server to request data to fulfill the + request for + LOAD DATA + LOCAL INFILE. +

      + If this value is set to NULL, the + driver will revert to using a + FileInputStream or + URLInputStream as required. +

    • + getLocalInfileInputStream() returns + the InputStream instance that will be + used to send data in response to a + LOAD DATA + LOCAL INFILE statement. +

      + This method returns NULL if no such + stream has been set using + setLocalInfileInputStream(). +

5.3 Java, JDBC and MySQL Types

+ MySQL Connector/J is flexible in the way it handles conversions + between MySQL data types and Java data types. +

+ In general, any MySQL data type can be converted to a + java.lang.String, and any numeric type can be + converted to any of the Java numeric types, although round-off, + overflow, or loss of precision may occur. +

Note

+ All TEXT types return + Types.LONGVARCHAR with different + getPrecision() values (65535, 255, + 16777215, and 2147483647 respectively) with + getColumnType() returning + -1. This behavior is intentional even + though TINYTEXT does not fall, regarding to + its size, within the LONGVARCHAR category. + This is to avoid different handling inside the same base type. + And getColumnType() returns + -1 because the internal server handling is + of type TEXT, which is similar to + BLOB. +

+ Also note that getColumnTypeName() will + return VARCHAR even though + getColumnType() returns + Types.LONGVARCHAR, because + VARCHAR is the designated column + database-specific name for this type. +

+ Starting with Connector/J 3.1.0, the JDBC driver issues warnings + or throws DataTruncation exceptions as is + required by the JDBC specification unless the connection was + configured not to do so by using the property + jdbcCompliantTruncation and setting it to + false. +

+ The conversions that are always guaranteed to work are listed in + the following table. The first column lists one or more MySQL + data types, and the second column lists one or more Java types + to which the MySQL types can be converted. +

Table 5.1 Connection Properties - Miscellaneous

These MySQL Data TypesCan always be converted to these Java types
CHAR, VARCHAR, BLOB, TEXT, ENUM, and SETjava.lang.String, java.io.InputStream, java.io.Reader, + java.sql.Blob, java.sql.Clob
FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT, + SMALLINT, MEDIUMINT, INTEGER, BIGINTjava.lang.String, java.lang.Short, java.lang.Integer, + java.lang.Long, java.lang.Double, + java.math.BigDecimal
DATE, TIME, DATETIME, TIMESTAMPjava.lang.String, java.sql.Date, java.sql.Timestamp

Note

+ Round-off, overflow or loss of precision may occur if you + choose a Java numeric data type that has less precision or + capacity than the MySQL data type you are converting to/from. +

+ The ResultSet.getObject() method uses the + type conversions between MySQL and Java types, following the + JDBC specification where appropriate. The value returned by + ResultSetMetaData.GetColumnClassName() is + also shown below. For more information on the JDBC types, see + the reference on the + java.sql.Types + class. +

Table 5.2 MySQL Types to Java Types for ResultSet.getObject()

MySQL Type NameReturn value of GetColumnClassNameReturned as Java Class
BIT(1) (new in MySQL-5.0)BITjava.lang.Boolean
BIT( > 1) (new in MySQL-5.0)BITbyte[]
TINYINTTINYINTjava.lang.Boolean if the configuration property + tinyInt1isBit is set to + true (the default) and the storage + size is 1, or java.lang.Integer + if not.
BOOL, BOOLEANTINYINTSee TINYINT, above as these are aliases for + TINYINT(1), currently.
SMALLINT[(M)] [UNSIGNED]SMALLINT [UNSIGNED]java.lang.Integer (regardless if + UNSIGNED or not)
MEDIUMINT[(M)] [UNSIGNED]MEDIUMINT [UNSIGNED]java.lang.Integer, if UNSIGNED + java.lang.Long (C/J 3.1 and + earlier), or java.lang.Integer + for C/J 5.0 and later
INT,INTEGER[(M)] [UNSIGNED]INTEGER [UNSIGNED]java.lang.Integer, if UNSIGNED + java.lang.Long
BIGINT[(M)] [UNSIGNED]BIGINT [UNSIGNED]java.lang.Long, if UNSIGNED + java.math.BigInteger
FLOAT[(M,D)]FLOATjava.lang.Float
DOUBLE[(M,B)]DOUBLEjava.lang.Double
DECIMAL[(M[,D])]DECIMALjava.math.BigDecimal
DATEDATEjava.sql.Date
DATETIMEDATETIMEjava.sql.Timestamp
TIMESTAMP[(M)]TIMESTAMPjava.sql.Timestamp
TIMETIMEjava.sql.Time
YEAR[(2|4)]YEARIf yearIsDateType configuration property is set to + false, then the returned object type + is java.sql.Short. If set to + true (the default), then the returned + object is of type java.sql.Date + with the date + + + set to January 1st, at midnight.
CHAR(M)CHARjava.lang.String (unless the character set for + the column is BINARY, then + byte[] is returned.
VARCHAR(M) [BINARY]VARCHARjava.lang.String (unless the character set for + the column is BINARY, then + byte[] is returned.
BINARY(M)BINARYbyte[]
VARBINARY(M)VARBINARYbyte[]
TINYBLOBTINYBLOBbyte[]
TINYTEXTVARCHARjava.lang.String
BLOBBLOBbyte[]
TEXTVARCHARjava.lang.String
MEDIUMBLOBMEDIUMBLOBbyte[]
MEDIUMTEXTVARCHARjava.lang.String
LONGBLOBLONGBLOBbyte[]
LONGTEXTVARCHARjava.lang.String
ENUM('value1','value2',...)CHARjava.lang.String
SET('value1','value2',...)CHARjava.lang.String

5.4 Using Character Sets and Unicode

+ All strings sent from the JDBC driver to the server are + converted automatically from native Java Unicode form to the + client character encoding, including all queries sent using + Statement.execute(), + Statement.executeUpdate(), + Statement.executeQuery() as well as all + PreparedStatement and + CallableStatement parameters with + the exclusion of parameters set using + setBytes(), + setBinaryStream(), + setAsciiStream(), + setUnicodeStream() and + setBlob(). +

Number of Encodings Per Connection

+ In MySQL Server 4.1 and higher, Connector/J supports a single + character encoding between client and server, and any number of + character encodings for data returned by the server to the + client in ResultSets. +

+ Prior to MySQL Server 4.1, Connector/J supported a single + character encoding per connection, which could either be + automatically detected from the server configuration, or could + be configured by the user through the + useUnicode and + characterEncoding properties. +

Setting the Character Encoding

+ The character encoding between client and server is + automatically detected upon connection. You specify the encoding + on the server using the + character_set_server for server + versions 4.1.0 and newer, and character_set + system variable for server versions older than 4.1.0. The driver + automatically uses the encoding specified by the server. For + more information, see Server Character Set and Collation. +

+ For example, to use 4-byte UTF-8 character sets with + Connector/J, configure the MySQL server with + character_set_server=utf8mb4, + and leave characterEncoding out of the + Connector/J connection string. Connector/J will then autodetect + the UTF-8 setting. +

+ To override the automatically detected encoding on the client + side, use the characterEncoding property + in the URL used to connect to the server. +

+ To allow multiple character sets to be sent from the client, use + the UTF-8 encoding, either by configuring + utf8 as the default server character set, or + by configuring the JDBC driver to use UTF-8 through the + characterEncoding property. +

+ When specifying character encodings on the client side, use + Java-style names. The following table lists MySQL character set + names and the corresponding Java-style names: +

Table 5.3 MySQL to Java Encoding Name Translations

MySQL Character Set NameJava-Style Character Encoding Name
asciiUS-ASCII
big5Big5
gbkGBK
sjisSJIS (or Cp932 or MS932 for MySQL Server < 4.1.11)
cp932Cp932 or MS932 (MySQL Server > 4.1.11)
gb2312EUC_CN
ujisEUC_JP
euckrEUC_KR
latin1Cp1252
latin2ISO8859_2
greekISO8859_7
hebrewISO8859_8
cp866Cp866
tis620TIS620
cp1250Cp1250
cp1251Cp1251
cp1257Cp1257
macromanMacRoman
macceMacCentralEurope
utf8UTF-8
ucs2UnicodeBig

Warning

+ Do not issue the query set names with + Connector/J, as the driver will not detect that the character + set has changed, and will continue to use the character set + detected during the initial connection setup. +

5.5 Connecting Securely Using SSL

+ SSL in MySQL Connector/J encrypts all data (other than the + initial handshake) between the JDBC driver and the server. There + is a performance penalty for enabling SSL, the severity of which + depends on multiple factors including (but not limited to) the + size of the query, the amount of data returned, the server + hardware, the SSL library used, the network bandwidth, and so + on. +

+ For SSL support to work, you must have the following: +

+ The system works through two Java truststore files, one file + contains the certificate information for the server + (truststore in the examples below). The + other file contains the certificate for the client + (keystore in the examples below). All Java + truststore files are password protected by supplying a suitable + password to the keytool when you create the + files. You need the file names and associated passwords to + create an SSL connection. +

+ You will first need to import the MySQL server CA Certificate + into a Java truststore. A sample MySQL server CA Certificate is + located in the SSL subdirectory of the + MySQL source distribution. This is what SSL will use to + determine if you are communicating with a secure MySQL server. + Alternatively, use the CA Certificate that you have generated or + been provided with by your SSL provider. +

+ To use Java's keytool to create a truststore + in the current directory , and import the server's CA + certificate (cacert.pem), you can do the + following (assuming that keytool is in your + path. The keytool is typically located in the + bin subdirectory of your JDK or JRE): +

+shell> keytool -import -alias mysqlServerCACert \
+         -file cacert.pem -keystore truststore
+

+ Enter the password when prompted for the keystore file. + Interaction with keytool looks like this: +

+Enter keystore password:  *********
+Owner: EMAILADDRESS=walrus@example.com, CN=Walrus,
+       O=My Company, L=Orenburg, ST=Some-State, C=RU
+Issuer: EMAILADDRESS=walrus@example.com, CN=Walrus,
+       O=My Company, L=Orenburg, ST=Some-State, C=RU
+Serial number: 0
+Valid from:
+   Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003
+Certificate fingerprints:
+    MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
+    SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C
+Trust this certificate? [no]:  yes
+Certificate was added to keystore
+

+ You then have two options: either import the client certificate + that matches the CA certificate you just imported, or create a + new client certificate. +

+ Importing an existing certificate requires the certificate to be + in DER format. You can use openssl to convert + an existing certificate into the new format. For example: +

+shell> openssl x509 -outform DER -in client-cert.pem -out client.cert
+

+ Now import the converted certificate into your keystore using + keytool: +

+shell> keytool -import -file client.cert -keystore keystore -alias mysqlClientCertificate
+

+ To generate your own client certificate, use + keytool to create a suitable certificate and + add it to the keystore file: +

+shell> keytool -genkey -keyalg rsa \
+     -alias mysqlClientCertificate -keystore keystore 
+

+ Keytool will prompt you for the following information, and + create a keystore named keystore in the + current directory. +

+ Respond with information that is appropriate for your situation: +

+Enter keystore password:  *********
+What is your first and last name?
+  [Unknown]:  Matthews
+What is the name of your organizational unit?
+  [Unknown]:  Software Development
+What is the name of your organization?
+  [Unknown]:  My Company
+What is the name of your City or Locality?
+  [Unknown]:  Flossmoor
+What is the name of your State or Province?
+  [Unknown]:  IL
+What is the two-letter country code for this unit?
+  [Unknown]:  US
+Is <CN=Matthews, OU=Software Development, O=My Company,
+ L=Flossmoor, ST=IL, C=US> correct?
+  [no]:  y
+
+Enter key password for <mysqlClientCertificate>
+        (RETURN if same as keystore password):
+

+ Finally, to get JSSE to use the keystore and truststore that you + have generated, you need to set the following system properties + when you start your JVM, replacing + path_to_keystore_file with the full + path to the keystore file you created, + path_to_truststore_file with the path + to the truststore file you created, and using the appropriate + password values for each property. You can do this either on the + command line: +

+-Djavax.net.ssl.keyStore=path_to_keystore_file
+-Djavax.net.ssl.keyStorePassword=password
+-Djavax.net.ssl.trustStore=path_to_truststore_file
+-Djavax.net.ssl.trustStorePassword=password
+

+ Or you can set the values directly within the application: +

+System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file");
+System.setProperty("javax.net.ssl.keyStorePassword","password");
+System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file");
+System.setProperty("javax.net.ssl.trustStorePassword","password");
+

+ You will also need to set useSSL to + true in your connection parameters for MySQL + Connector/J, either by adding useSSL=true to + your URL, or by setting the property useSSL + to true in the + java.util.Properties instance you pass to + DriverManager.getConnection(). +

+ You can test that SSL is working by turning on JSSE debugging + (as detailed below), and look for the following key events: +

+...
+*** ClientHello, v3.1
+RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, »
+  54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, »
+  217, 219, 239, 202, 19, 121, 78 }
+Session ID:  {}
+Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 }
+Compression Methods:  { 0 }
+***
+[write] MD5 and SHA1 hashes:  len = 59
+0000: 01 00 00 37 03 01 3D B6 90 FA C7 94 B4 D7 4A 0C  ...7..=.......J.
+0010: 36 F4 00 A8 37 67 D7 40 10 8A E1 BE 84 99 02 D9  6...7g.@........
+0020: DB EF CA 13 79 4E 00 00 10 00 05 00 04 00 09 00  ....yN..........
+0030: 0A 00 12 00 13 00 03 00 11 01 00                 ...........
+main, WRITE:  SSL v3.1 Handshake, length = 59
+main, READ:  SSL v3.1 Handshake, length = 74
+*** ServerHello, v3.1
+RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58, »
+   202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3, »
+   132, 110, 82, 148, 160, 92 }
+Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, »
+   182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, »
+   219, 158, 177, 187, 143}
+Cipher Suite:  { 0, 5 }
+Compression Method: 0
+***
+%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
+** SSL_RSA_WITH_RC4_128_SHA
+[read] MD5 and SHA1 hashes:  len = 74
+0000: 02 00 00 46 03 01 3D B6 43 98 74 32 04 67 19 64  ...F..=.C.t2.g.d
+0010: 3A CA 4F B9 B2 64 D7 42 FE 15 53 BB BE 2A AA 03  :.O..d.B..S..*..
+0020: 84 6E 52 94 A0 5C 20 A3 E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q....
+0030: B3 44 3F B6 9E 1E 0B 96 4F AA 4C FF 5C 0F E2 18  .D?.....O.L.\...
+0040: 11 B1 DB 9E B1 BB 8F 00 05 00                    ..........
+main, READ:  SSL v3.1 Handshake, length = 1712
+...
+

+ JSSE provides debugging (to stdout) when you + set the following system property: + -Djavax.net.debug=all This will tell you what + keystores and truststores are being used, as well as what is + going on during the SSL handshake and certificate exchange. It + will be helpful when trying to determine what is not working + when trying to get an SSL connection to happen. +

5.6 Connecting Using PAM Authentication

+ Java applications using Connector/J 5.1.21 and higher can + connect to MySQL servers that use the pluggable authentication + module (PAM) authentication scheme. +

+ For PAM authentication to work, you must have the following: +

+ PAM authentication support is enabled by default in Connector/J + 5.1.21 and up, so no extra configuration is needed. +

+ To disable the PAM authentication feature, specify + mysql_clear_password (the method) or + com.mysql.jdbc.authentication.MysqlClearPasswordPlugin + (the class name) in the comma-separated list of arguments for + the disabledAuthenticationPlugins connection + option. See + Section 5.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J” + for details about that connection option. +

5.7 Using Master/Slave Replication with ReplicationConnection

+ See + Section 8.3, “Configuring Master/Slave Replication with Connector/J” + for details on the topic. +

5.8 Mapping MySQL Error Numbers to JDBC SQLState Codes

+ The table below provides a mapping of the MySQL error numbers to + JDBC SQLState values. +

Table 5.4 Mapping of MySQL Error Numbers to SQLStates

MySQL Error NumberMySQL Error NameLegacy (X/Open) SQLStateSQL Standard SQLState
1022ER_DUP_KEY2300023000
1037ER_OUTOFMEMORYS1001HY001
1038ER_OUT_OF_SORTMEMORYS1001HY001
1040ER_CON_COUNT_ERROR0800408004
1042ER_BAD_HOST_ERROR0800408S01
1043ER_HANDSHAKE_ERROR0800408S01
1044ER_DBACCESS_DENIED_ERROR4200042000
1045ER_ACCESS_DENIED_ERROR2800028000
1046ER_NO_DB_ERROR3D0003D000
1047ER_UNKNOWN_COM_ERROR08S0108S01
1048ER_BAD_NULL_ERROR2300023000
1049ER_BAD_DB_ERROR4200042000
1050ER_TABLE_EXISTS_ERROR42S0142S01
1051ER_BAD_TABLE_ERROR42S0242S02
1052ER_NON_UNIQ_ERROR2300023000
1053ER_SERVER_SHUTDOWN08S0108S01
1054ER_BAD_FIELD_ERRORS002242S22
1055ER_WRONG_FIELD_WITH_GROUPS100942000
1056ER_WRONG_GROUP_FIELDS100942000
1057ER_WRONG_SUM_SELECTS100942000
1058ER_WRONG_VALUE_COUNT21S0121S01
1059ER_TOO_LONG_IDENTS100942000
1060ER_DUP_FIELDNAMES100942S21
1061ER_DUP_KEYNAMES100942000
1062ER_DUP_ENTRYS100923000
1063ER_WRONG_FIELD_SPECS100942000
1064ER_PARSE_ERROR4200042000
1065ER_EMPTY_QUERY4200042000
1066ER_NONUNIQ_TABLES100942000
1067ER_INVALID_DEFAULTS100942000
1068ER_MULTIPLE_PRI_KEYS100942000
1069ER_TOO_MANY_KEYSS100942000
1070ER_TOO_MANY_KEY_PARTSS100942000
1071ER_TOO_LONG_KEYS100942000
1072ER_KEY_COLUMN_DOES_NOT_EXITSS100942000
1073ER_BLOB_USED_AS_KEYS100942000
1074ER_TOO_BIG_FIELDLENGTHS100942000
1075ER_WRONG_AUTO_KEYS100942000
1080ER_FORCING_CLOSE08S0108S01
1081ER_IPSOCK_ERROR08S0108S01
1082ER_NO_SUCH_INDEXS100942S12
1083ER_WRONG_FIELD_TERMINATORSS100942000
1084ER_BLOBS_AND_NO_TERMINATEDS100942000
1090ER_CANT_REMOVE_ALL_FIELDS4200042000
1091ER_CANT_DROP_FIELD_OR_KEY4200042000
1101ER_BLOB_CANT_HAVE_DEFAULT4200042000
1102ER_WRONG_DB_NAME4200042000
1103ER_WRONG_TABLE_NAME4200042000
1104ER_TOO_BIG_SELECT4200042000
1106ER_UNKNOWN_PROCEDURE4200042000
1107ER_WRONG_PARAMCOUNT_TO_PROCEDURE4200042000
1109ER_UNKNOWN_TABLE42S0242S02
1110ER_FIELD_SPECIFIED_TWICE4200042000
1112ER_UNSUPPORTED_EXTENSION4200042000
1113ER_TABLE_MUST_HAVE_COLUMNS4200042000
1115ER_UNKNOWN_CHARACTER_SET4200042000
1118ER_TOO_BIG_ROWSIZE4200042000
1120ER_WRONG_OUTER_JOIN4200042000
1121ER_NULL_COLUMN_IN_INDEX4200042000
1129ER_HOST_IS_BLOCKED08004HY000
1130ER_HOST_NOT_PRIVILEGED08004HY000
1131ER_PASSWORD_ANONYMOUS_USER4200042000
1132ER_PASSWORD_NOT_ALLOWED4200042000
1133ER_PASSWORD_NO_MATCH4200042000
1136ER_WRONG_VALUE_COUNT_ON_ROW21S0121S01
1138ER_INVALID_USE_OF_NULLS100042000
1139ER_REGEXP_ERROR4200042000
1140ER_MIX_OF_GROUP_FUNC_AND_FIELDS4200042000
1141ER_NONEXISTING_GRANT4200042000
1142ER_TABLEACCESS_DENIED_ERROR4200042000
1143ER_COLUMNACCESS_DENIED_ERROR4200042000
1144ER_ILLEGAL_GRANT_FOR_TABLE4200042000
1145ER_GRANT_WRONG_HOST_OR_USER4200042000
1146ER_NO_SUCH_TABLE42S0242S02
1147ER_NONEXISTING_TABLE_GRANT4200042000
1148ER_NOT_ALLOWED_COMMAND4200042000
1149ER_SYNTAX_ERROR4200042000
1152ER_ABORTING_CONNECTION08S0108S01
1153ER_NET_PACKET_TOO_LARGE08S0108S01
1154ER_NET_READ_ERROR_FROM_PIPE08S0108S01
1155ER_NET_FCNTL_ERROR08S0108S01
1156ER_NET_PACKETS_OUT_OF_ORDER08S0108S01
1157ER_NET_UNCOMPRESS_ERROR08S0108S01
1158ER_NET_READ_ERROR08S0108S01
1159ER_NET_READ_INTERRUPTED08S0108S01
1160ER_NET_ERROR_ON_WRITE08S0108S01
1161ER_NET_WRITE_INTERRUPTED08S0108S01
1162ER_TOO_LONG_STRING4200042000
1163ER_TABLE_CANT_HANDLE_BLOB4200042000
1164ER_TABLE_CANT_HANDLE_AUTO_INCREMENT4200042000
1166ER_WRONG_COLUMN_NAME4200042000
1167ER_WRONG_KEY_COLUMN4200042000
1169ER_DUP_UNIQUE2300023000
1170ER_BLOB_KEY_WITHOUT_LENGTH4200042000
1171ER_PRIMARY_CANT_HAVE_NULL4200042000
1172ER_TOO_MANY_ROWS4200042000
1173ER_REQUIRES_PRIMARY_KEY4200042000
1176ER_KEY_DOES_NOT_EXITS4200042000
1177ER_CHECK_NO_SUCH_TABLE4200042000
1178ER_CHECK_NOT_IMPLEMENTED4200042000
1179ER_CANT_DO_THIS_DURING_AN_TRANSACTION2500025000
1184ER_NEW_ABORTING_CONNECTION08S0108S01
1189ER_MASTER_NET_READ08S0108S01
1190ER_MASTER_NET_WRITE08S0108S01
1203ER_TOO_MANY_USER_CONNECTIONS4200042000
1205ER_LOCK_WAIT_TIMEOUT4000140001
1207ER_READ_ONLY_TRANSACTION2500025000
1211ER_NO_PERMISSION_TO_CREATE_USER4200042000
1213ER_LOCK_DEADLOCK4000140001
1216ER_NO_REFERENCED_ROW2300023000
1217ER_ROW_IS_REFERENCED2300023000
1218ER_CONNECT_TO_MASTER08S0108S01
1222ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT2100021000
1226ER_USER_LIMIT_REACHED4200042000
1227ER_SPECIFIC_ACCESS_DENIED_ERROR4200042000
1230ER_NO_DEFAULT4200042000
1231ER_WRONG_VALUE_FOR_VAR4200042000
1232ER_WRONG_TYPE_FOR_VAR4200042000
1234ER_CANT_USE_OPTION_HERE4200042000
1235ER_NOT_SUPPORTED_YET4200042000
1239ER_WRONG_FK_DEF4200042000
1241ER_OPERAND_COLUMNS2100021000
1242ER_SUBQUERY_NO_1_ROW2100021000
1247ER_ILLEGAL_REFERENCE42S2242S22
1248ER_DERIVED_MUST_HAVE_ALIAS4200042000
1249ER_SELECT_REDUCED0100001000
1250ER_TABLENAME_NOT_ALLOWED_HERE4200042000
1251ER_NOT_SUPPORTED_AUTH_MODE0800408004
1252ER_SPATIAL_CANT_HAVE_NULL4200042000
1253ER_COLLATION_CHARSET_MISMATCH4200042000
1261ER_WARN_TOO_FEW_RECORDS0100001000
1262ER_WARN_TOO_MANY_RECORDS0100001000
1263ER_WARN_NULL_TO_NOTNULLS100001000
1264ER_WARN_DATA_OUT_OF_RANGE0100001000
1265ER_WARN_DATA_TRUNCATED0100001000
1280ER_WRONG_NAME_FOR_INDEX4200042000
1281ER_WRONG_NAME_FOR_CATALOG4200042000
1286ER_UNKNOWN_STORAGE_ENGINE4200042000
1292ER_TRUNCATED_WRONG_VALUE2200722007
1303ER_SP_NO_RECURSIVE_CREATES10002F003
1304ER_SP_ALREADY_EXISTS4200042000
1305ER_SP_DOES_NOT_EXIST4200042000
1308ER_SP_LILABEL_MISMATCH4200042000
1309ER_SP_LABEL_REDEFINE4200042000
1310ER_SP_LABEL_MISMATCH4200042000
1311ER_SP_UNINIT_VAR0100001000
1312ER_SP_BADSELECT0A0000A000
1313ER_SP_BADRETURN4200042000
1314ER_SP_BADSTATEMENT0A0000A000
1315ER_UPDATE_LOG_DEPRECATED_IGNORED4200042000
1316ER_UPDATE_LOG_DEPRECATED_TRANSLATED4200042000
1317ER_QUERY_INTERRUPTEDS100070100
1318ER_SP_WRONG_NO_OF_ARGS4200042000
1319ER_SP_COND_MISMATCH4200042000
1320ER_SP_NORETURN4200042000
1321ER_SP_NORETURNENDS10002F005
1322ER_SP_BAD_CURSOR_QUERY4200042000
1323ER_SP_BAD_CURSOR_SELECT4200042000
1324ER_SP_CURSOR_MISMATCH4200042000
1325ER_SP_CURSOR_ALREADY_OPEN2400024000
1326ER_SP_CURSOR_NOT_OPEN2400024000
1327ER_SP_UNDECLARED_VAR4200042000
1329ER_SP_FETCH_NO_DATAS100002000
1330ER_SP_DUP_PARAM4200042000
1331ER_SP_DUP_VAR4200042000
1332ER_SP_DUP_COND4200042000
1333ER_SP_DUP_CURS4200042000
1335ER_SP_SUBSELECT_NYI0A0000A000
1336ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG0A0000A000
1337ER_SP_VARCOND_AFTER_CURSHNDLR4200042000
1338ER_SP_CURSOR_AFTER_HANDLER4200042000
1339ER_SP_CASE_NOT_FOUNDS100020000
1365ER_DIVISION_BY_ZERO2201222012
1367ER_ILLEGAL_VALUE_FOR_TYPE2200722007
1370ER_PROCACCESS_DENIED_ERROR4200042000
1397ER_XAER_NOTAS1000XAE04
1398ER_XAER_INVALS1000XAE05
1399ER_XAER_RMFAILS1000XAE07
1400ER_XAER_OUTSIDES1000XAE09
1401ER_XA_RMERRS1000XAE03
1402ER_XA_RBROLLBACKS1000XA100
1403ER_NONEXISTING_PROC_GRANT4200042000
1406ER_DATA_TOO_LONG2200122001
1407ER_SP_BAD_SQLSTATE4200042000
1410ER_CANT_CREATE_USER_WITH_GRANT4200042000
1413ER_SP_DUP_HANDLER4200042000
1414ER_SP_NOT_VAR_ARG4200042000
1415ER_SP_NO_RETSET0A0000A000
1416ER_CANT_CREATE_GEOMETRY_OBJECT2200322003
1425ER_TOO_BIG_SCALE4200042000
1426ER_TOO_BIG_PRECISION4200042000
1427ER_M_BIGGER_THAN_D4200042000
1437ER_TOO_LONG_BODY4200042000
1439ER_TOO_BIG_DISPLAYWIDTH4200042000
1440ER_XAER_DUPIDS1000XAE08
1441ER_DATETIME_FUNCTION_OVERFLOW2200822008
1451ER_ROW_IS_REFERENCED_22300023000
1452ER_NO_REFERENCED_ROW_22300023000
1453ER_SP_BAD_VAR_SHADOW4200042000
1458ER_SP_WRONG_NAME4200042000
1460ER_SP_NO_AGGREGATE4200042000
1461ER_MAX_PREPARED_STMT_COUNT_REACHED4200042000
1463ER_NON_GROUPING_FIELD_USED4200042000
1557ER_FOREIGN_DUPLICATE_KEY2300023000
1568ER_CANT_CHANGE_TX_ISOLATIONS100025001
1582ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT4200042000
1583ER_WRONG_PARAMETERS_TO_NATIVE_FCT4200042000
1584ER_WRONG_PARAMETERS_TO_STORED_FCT4200042000
1586ER_DUP_ENTRY_WITH_KEY_NAME2300023000
1613ER_XA_RBTIMEOUTS1000XA106
1614ER_XA_RBDEADLOCKS1000XA102
1630ER_FUNC_INEXISTENT_NAME_COLLISION4200042000
1641ER_DUP_SIGNAL_SET4200042000
1642ER_SIGNAL_WARN0100001000
1643ER_SIGNAL_NOT_FOUNDS100002000
1645ER_RESIGNAL_WITHOUT_ACTIVE_HANDLERS10000K000
1687ER_SPATIAL_MUST_HAVE_GEOM_COL4200042000
1690ER_DATA_OUT_OF_RANGE2200322003
1698ER_ACCESS_DENIED_NO_PASSWORD_ERROR2800028000
1701ER_TRUNCATE_ILLEGAL_FK4200042000
1758ER_DA_INVALID_CONDITION_NUMBER3500035000
1761ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO2300023000
1762ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO2300023000
1792ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTIONS100025006
1845ER_ALTER_OPERATION_NOT_SUPPORTED0A0000A000
1846ER_ALTER_OPERATION_NOT_SUPPORTED_REASON0A0000A000
1859ER_DUP_UNKNOWN_IN_INDEX2300023000
1873ER_ACCESS_DENIED_CHANGE_USER_ERROR2800028000
1887ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLERS10000Z002
1903ER_INVALID_ARGUMENT_FOR_LOGARITHMS10002201E

Chapter 6 JDBC Concepts

+ This section provides some general JDBC background. +

6.1 Connecting to MySQL Using the JDBC DriverManager + Interface

+ When you are using JDBC outside of an application server, the + DriverManager class manages the establishment + of connections. +

+ Specify to the DriverManager which JDBC + drivers to try to make Connections with. The easiest way to do + this is to use Class.forName() on the class + that implements the java.sql.Driver + interface. With MySQL Connector/J, the name of this class is + com.mysql.jdbc.Driver. With this method, you + could use an external configuration file to supply the driver + class name and driver parameters to use when connecting to a + database. +

+ The following section of Java code shows how you might register + MySQL Connector/J from the main() method of + your application. If testing this code, first read the + installation section at + Chapter 3, Connector/J Installation, to make sure you have + connector installed correctly and the + CLASSPATH set up. Also, ensure that MySQL is + configured to accept external TCP/IP connections. +

+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+// Notice, do not import com.mysql.jdbc.*
+// or you will have problems!
+
+public class LoadDriver {
+    public static void main(String[] args) {
+        try {
+            // The newInstance() call is a work around for some
+            // broken Java implementations
+
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
+        } catch (Exception ex) {
+            // handle the error
+        }
+    }
+}
+

+ After the driver has been registered with the + DriverManager, you can obtain a + Connection instance that is connected to a + particular database by calling + DriverManager.getConnection(): +

Example 6.1 Connector/J: Obtaining a connection from the + DriverManager

+ If you have not already done so, please review the portion of + Section 6.1, “Connecting to MySQL Using the JDBC DriverManager + Interface” + above before working with the example below. +

+ This example shows how you can obtain a + Connection instance from the + DriverManager. There are a few different + signatures for the getConnection() + method. Consult the API documentation that comes with your JDK + for more specific information on how to use them. +

+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+Connection conn = null;
+...
+try {
+    conn =
+       DriverManager.getConnection("jdbc:mysql://localhost/test?" +
+                                   "user=minty&password=greatsqldb");
+
+    // Do something with the Connection
+
+   ...
+} catch (SQLException ex) {
+    // handle any errors
+    System.out.println("SQLException: " + ex.getMessage());
+    System.out.println("SQLState: " + ex.getSQLState());
+    System.out.println("VendorError: " + ex.getErrorCode());
+}
+

+ Once a Connection is established, it + can be used to create Statement and + PreparedStatement objects, as well as + retrieve metadata about the database. This is explained in the + following sections. +


6.2 Using JDBC Statement Objects to Execute SQL

+ Statement objects allow you to execute + basic SQL queries and retrieve the results through the + ResultSet class, which is described later. +

+ To create a Statement instance, you call + the createStatement() method on the + Connection object you have retrieved using + one of the DriverManager.getConnection() or + DataSource.getConnection() methods described + earlier. +

+ Once you have a Statement instance, you + can execute a SELECT query by + calling the executeQuery(String) method + with the SQL you want to use. +

+ To update data in the database, use the + executeUpdate(String SQL) method. This + method returns the number of rows matched by the update + statement, not the number of rows that were modified. +

+ If you do not know ahead of time whether the SQL statement will + be a SELECT or an + UPDATE/INSERT, + then you can use the execute(String SQL) + method. This method will return true if the SQL query was a + SELECT, or false if it was an + UPDATE, + INSERT, or + DELETE statement. If the + statement was a SELECT query, you + can retrieve the results by calling the + getResultSet() method. If the statement was + an UPDATE, + INSERT, or + DELETE statement, you can + retrieve the affected rows count by calling + getUpdateCount() on the + Statement instance. +

Example 6.2 Connector/J: Using java.sql.Statement to execute a + SELECT query

+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.ResultSet;
+
+// assume that conn is an already created JDBC connection (see previous examples)
+
+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+    stmt = conn.createStatement();
+    rs = stmt.executeQuery("SELECT foo FROM bar");
+
+    // or alternatively, if you don't know ahead of time that
+    // the query will be a SELECT...
+
+    if (stmt.execute("SELECT foo FROM bar")) {
+        rs = stmt.getResultSet();
+    }
+
+    // Now do something with the ResultSet ....
+}
+catch (SQLException ex){
+    // handle any errors
+    System.out.println("SQLException: " + ex.getMessage());
+    System.out.println("SQLState: " + ex.getSQLState());
+    System.out.println("VendorError: " + ex.getErrorCode());
+}
+finally {
+    // it is a good idea to release
+    // resources in a finally{} block
+    // in reverse-order of their creation
+    // if they are no-longer needed
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException sqlEx) { } // ignore
+
+        rs = null;
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException sqlEx) { } // ignore
+
+        stmt = null;
+    }
+}
+

6.3 Using JDBC CallableStatements to Execute Stored + Procedures

+ Starting with MySQL server version 5.0 when used with + Connector/J 3.1.1 or newer, the + java.sql.CallableStatement interface is + fully implemented with the exception of the + getParameterMetaData() method. +

+ For more information on MySQL stored procedures, please refer to + Using Stored Routines (Procedures and Functions). +

+ Connector/J exposes stored procedure functionality through + JDBC's CallableStatement interface. +

Note

+ Current versions of MySQL server do not return enough + information for the JDBC driver to provide result set metadata + for callable statements. This means that when using + CallableStatement, + ResultSetMetaData may return + NULL. +

+ The following example shows a stored procedure that returns the + value of inOutParam incremented by 1, and the + string passed in using inputParam as a + ResultSet: + +

Example 6.3 Connector/J: Calling Stored Procedures

+CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \
+                        INOUT inOutParam INT)
+BEGIN
+    DECLARE z INT;
+    SET z = inOutParam + 1;
+    SET inOutParam = z;
+
+    SELECT inputParam;
+
+    SELECT CONCAT('zyxw', inputParam);
+END
+


+

+ To use the demoSp procedure with Connector/J, + follow these steps: +

  1. + Prepare the callable statement by using + Connection.prepareCall(). +

    + Notice that you have to use JDBC escape syntax, and that the + parentheses surrounding the parameter placeholders are not + optional: +

    Example 6.4 Connector/J: Using Connection.prepareCall()

    +import java.sql.CallableStatement;
    +
    +...
    +
    +    //
    +    // Prepare a call to the stored procedure 'demoSp'
    +    // with two parameters
    +    //
    +    // Notice the use of JDBC-escape syntax ({call ...})
    +    //
    +
    +    CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
    +
    +
    +
    +    cStmt.setString(1, "abcdefg");
    +

    Note

    + Connection.prepareCall() is an + expensive method, due to the metadata retrieval that the + driver performs to support output parameters. For + performance reasons, minimize unnecessary calls to + Connection.prepareCall() by reusing + CallableStatement instances in your + code. +

  2. + Register the output parameters (if any exist) +

    + To retrieve the values of output parameters (parameters + specified as OUT or + INOUT when you created the stored + procedure), JDBC requires that they be specified before + statement execution using the various + registerOutputParameter() methods in + the CallableStatement interface: + +

    Example 6.5 Connector/J: Registering output parameters

    +import java.sql.Types;
    +...
    +//
    +// Connector/J supports both named and indexed
    +// output parameters. You can register output
    +// parameters using either method, as well
    +// as retrieve output parameters using either
    +// method, regardless of what method was
    +// used to register them.
    +//
    +// The following examples show how to use
    +// the various methods of registering
    +// output parameters (you should of course
    +// use only one registration per parameter).
    +//
    +
    +//
    +// Registers the second parameter as output, and
    +// uses the type 'INTEGER' for values returned from
    +// getObject()
    +//
    +
    +cStmt.registerOutParameter(2, Types.INTEGER);
    +
    +//
    +// Registers the named parameter 'inOutParam', and
    +// uses the type 'INTEGER' for values returned from
    +// getObject()
    +//
    +
    +cStmt.registerOutParameter("inOutParam", Types.INTEGER);
    +...
    +


    +

  3. + Set the input parameters (if any exist) +

    + Input and in/out parameters are set as for + PreparedStatement objects. However, + CallableStatement also supports + setting parameters by name: + +

    Example 6.6 Connector/J: Setting CallableStatement input + parameters

    +...
    +
    +    //
    +    // Set a parameter by index
    +    //
    +
    +    cStmt.setString(1, "abcdefg");
    +
    +    //
    +    // Alternatively, set a parameter using
    +    // the parameter name
    +    //
    +
    +    cStmt.setString("inputParameter", "abcdefg");
    +
    +    //
    +    // Set the 'in/out' parameter using an index
    +    //
    +
    +    cStmt.setInt(2, 1);
    +
    +    //
    +    // Alternatively, set the 'in/out' parameter
    +    // by name
    +    //
    +
    +    cStmt.setInt("inOutParam", 1);
    +
    +...
    +


    +

  4. + Execute the CallableStatement, and + retrieve any result sets or output parameters. +

    + Although CallableStatement supports + calling any of the Statement execute + methods (executeUpdate(), + executeQuery() or + execute()), the most flexible method to + call is execute(), as you do not need + to know ahead of time if the stored procedure returns result + sets: + +

    Example 6.7 Connector/J: Retrieving results and output parameter values

    +...
    +
    +    boolean hadResults = cStmt.execute();
    +
    +    //
    +    // Process all returned result sets
    +    //
    +
    +    while (hadResults) {
    +        ResultSet rs = cStmt.getResultSet();
    +
    +        // process result set
    +        ...
    +
    +        hadResults = cStmt.getMoreResults();
    +    }
    +
    +    //
    +    // Retrieve output parameters
    +    //
    +    // Connector/J supports both index-based and
    +    // name-based retrieval
    +    //
    +
    +    int outputValue = cStmt.getInt(2); // index-based
    +
    +    outputValue = cStmt.getInt("inOutParam"); // name-based
    +
    +...
    +


    +

6.4 Retrieving AUTO_INCREMENT Column Values through JDBC

+ Before version 3.0 of the JDBC API, there was no standard way of + retrieving key values from databases that supported auto + increment or identity columns. With older JDBC drivers for + MySQL, you could always use a MySQL-specific method on the + Statement interface, or issue the query + SELECT LAST_INSERT_ID() after issuing an + INSERT to a table that had an + AUTO_INCREMENT key. Using the MySQL-specific + method call isn't portable, and issuing a + SELECT to get the + AUTO_INCREMENT key's value requires another + round-trip to the database, which isn't as efficient as + possible. The following code snippets demonstrate the three + different ways to retrieve AUTO_INCREMENT + values. First, we demonstrate the use of the new JDBC 3.0 method + getGeneratedKeys() which is now the + preferred method to use if you need to retrieve + AUTO_INCREMENT keys and have access to JDBC + 3.0. The second example shows how you can retrieve the same + value using a standard SELECT + LAST_INSERT_ID() query. The final example shows how + updatable result sets can retrieve the + AUTO_INCREMENT value when using the + insertRow() method. +

Example 6.8 Connector/J: Retrieving AUTO_INCREMENT column values + using Statement.getGeneratedKeys()

+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets assuming you have a
+    // Connection 'conn' to a MySQL database already
+    // available
+
+    stmt = conn.createStatement();
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')",
+            Statement.RETURN_GENERATED_KEYS);
+
+    //
+    // Example of using Statement.getGeneratedKeys()
+    // to retrieve the value of an auto-increment
+    // value
+    //
+
+    int autoIncKeyFromApi = -1;
+
+    rs = stmt.getGeneratedKeys();
+
+    if (rs.next()) {
+        autoIncKeyFromApi = rs.getInt(1);
+    } else {
+
+        // throw an exception from here
+    }
+
+    System.out.println("Key returned from getGeneratedKeys():"
+        + autoIncKeyFromApi);
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+

Example 6.9 Connector/J: Retrieving AUTO_INCREMENT column values + using SELECT LAST_INSERT_ID()

+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets.
+
+    stmt = conn.createStatement();
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')");
+
+    //
+    // Use the MySQL LAST_INSERT_ID()
+    // function to do the same thing as getGeneratedKeys()
+    //
+
+    int autoIncKeyFromFunc = -1;
+    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+    if (rs.next()) {
+        autoIncKeyFromFunc = rs.getInt(1);
+    } else {
+        // throw an exception from here
+    }
+
+    System.out.println("Key returned from " +
+                       "'SELECT LAST_INSERT_ID()': " +
+                       autoIncKeyFromFunc);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+

Example 6.10 Connector/J: Retrieving AUTO_INCREMENT column values + in Updatable ResultSets

+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets as well as an 'updatable'
+    // one, assuming you have a Connection 'conn' to
+    // a MySQL database already available
+    //
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Example of retrieving an AUTO INCREMENT key
+    // from an updatable result set
+    //
+
+    rs = stmt.executeQuery("SELECT priKey, dataField "
+       + "FROM autoIncTutorial");
+
+    rs.moveToInsertRow();
+
+    rs.updateString("dataField", "AUTO INCREMENT here?");
+    rs.insertRow();
+
+    //
+    // the driver adds rows at the end
+    //
+
+    rs.last();
+
+    //
+    // We should now be on the row we just inserted
+    //
+
+    int autoIncKeyFromRS = rs.getInt("priKey");
+
+    System.out.println("Key returned for inserted row: "
+        + autoIncKeyFromRS);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+

+ Running the preceding example code should produce the following + output: +

+Key returned from getGeneratedKeys(): 1
+Key returned from SELECT LAST_INSERT_ID(): 1
+Key returned for inserted row: 1
+

+ At times, it can be tricky to use the SELECT + LAST_INSERT_ID() query, as that function's value is + scoped to a connection. So, if some other query happens on the + same connection, the value is overwritten. On the other hand, + the getGeneratedKeys() method is scoped by + the Statement instance, so it can be used + even if other queries happen on the same connection, but not on + the same Statement instance. +

Chapter 7 Connection Pooling with Connector/J

+ Connection pooling is a technique of creating and managing a pool + of connections that are ready for use by any + thread that needs them. + Connection pooling can greatly increase the performance of your + Java application, while reducing overall resource usage. +

How Connection Pooling Works

+ Most applications only need a thread to have access to a JDBC + connection when they are actively processing a + transaction, which often + takes only milliseconds to complete. When not processing a + transaction, the connection sits idle. Connection pooling enables + the idle connection to be used by some other thread to do useful + work. +

+ In practice, when a thread needs to do work against a MySQL or + other database with JDBC, it requests a connection from the pool. + When the thread is finished using the connection, it returns it to + the pool, so that it can be used by any other threads. +

+ When the connection is loaned out from the pool, it is used + exclusively by the thread that requested it. From a programming + point of view, it is the same as if your thread called + DriverManager.getConnection() every time it + needed a JDBC connection. With connection pooling, your thread may + end up using either a new connection or an already-existing + connection. +

Benefits of Connection Pooling

+ The main benefits to connection pooling are: +

  • + Reduced connection creation time. +

    + Although this is not usually an issue with the quick + connection setup that MySQL offers compared to other + databases, creating new JDBC connections still incurs + networking and JDBC driver overhead that will be avoided if + connections are recycled. +

  • + Simplified programming model. +

    + When using connection pooling, each individual thread can act + as though it has created its own JDBC connection, allowing you + to use straightforward JDBC programming techniques. +

  • + Controlled resource usage. +

    + If you create a new connection every time a thread needs one + rather than using connection pooling, your application's + resource usage can be wasteful, and it could lead to + unpredictable behaviors for your application when it is under + a heavy load. +

Using Connection Pooling with Connector/J

+ The concept of connection pooling in JDBC has been standardized + through the JDBC 2.0 Optional interfaces, and all major + application servers have implementations of these APIs that work + with MySQL Connector/J. +

+ Generally, you configure a connection pool in your application + server configuration files, and access it through the Java Naming + and Directory Interface (JNDI). The following code shows how you + might use a connection pool from an application deployed in a J2EE + application server: + +

Example 7.1 Connector/J: Using a connection pool with a J2EE application server

+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+
+
+public class MyServletJspOrEjb {
+
+    public void doSomething() throws Exception {
+        /*
+         * Create a JNDI Initial context to be able to
+         *  lookup  the DataSource
+         *
+         * In production-level code, this should be cached as
+         * an instance or static variable, as it can
+         * be quite expensive to create a JNDI context.
+         *
+         * Note: This code only works when you are using servlets
+         * or EJBs in a J2EE application server. If you are
+         * using connection pooling in standalone Java code, you
+         * will have to create/configure datasources using whatever
+         * mechanisms your particular connection pooling library
+         * provides.
+         */
+
+        InitialContext ctx = new InitialContext();
+
+         /*
+          * Lookup the DataSource, which will be backed by a pool
+          * that the application server provides. DataSource instances
+          * are also a good candidate for caching as an instance
+          * variable, as JNDI lookups can be expensive as well.
+          */
+
+        DataSource ds =
+          (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
+
+        /*
+         * The following code is what would actually be in your
+         * Servlet, JSP or EJB 'service' method...where you need
+         * to work with a JDBC connection.
+         */
+
+        Connection conn = null;
+        Statement stmt = null;
+
+        try {
+            conn = ds.getConnection();
+
+            /*
+             * Now, use normal JDBC programming to work with
+             * MySQL, making sure to close each resource when you're
+             * finished with it, which permits the connection pool
+             * resources to be recovered as quickly as possible
+             */
+
+            stmt = conn.createStatement();
+            stmt.execute("SOME SQL QUERY");
+
+            stmt.close();
+            stmt = null;
+
+            conn.close();
+            conn = null;
+        } finally {
+            /*
+             * close any jdbc instances here that weren't
+             * explicitly closed during normal code path, so
+             * that we don't 'leak' resources...
+             */
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (sqlexception sqlex) {
+                    // ignore, as we can't do anything about it here
+                }
+
+                stmt = null;
+            }
+
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (sqlexception sqlex) {
+                    // ignore, as we can't do anything about it here
+                }
+
+                conn = null;
+            }
+        }
+    }
+}
+


+ + As shown in the example above, after obtaining the JNDI + InitialContext, and looking up the + DataSource, the rest of the code follows + familiar JDBC conventions. +

+ When using connection pooling, always make sure that connections, + and anything created by them (such as statements or result sets) + are closed. This rule applies no matter what happens in your code + (exceptions, flow-of-control, and so forth). When these objects + are closed, they can be re-used; otherwise, they will be stranded, + which means that the MySQL server resources they represent (such + as buffers, locks, or sockets) are tied up for some time, or in + the worst case can be tied up forever. +

Sizing the Connection Pool

+ Each connection to MySQL has overhead (memory, CPU, context + switches, and so forth) on both the client and server side. Every + connection limits how many resources there are available to your + application as well as the MySQL server. Many of these resources + will be used whether or not the connection is actually doing any + useful work! Connection pools can be tuned to maximize + performance, while keeping resource utilization below the point + where your application will start to fail rather than just run + slower. +

+ The optimal size for the connection pool depends on anticipated + load and average database transaction time. In practice, the + optimal connection pool size can be smaller than you might expect. + If you take Oracle's Java Petstore blueprint application for + example, a connection pool of 15-20 connections can serve a + relatively moderate load (600 concurrent users) using MySQL and + Tomcat with acceptable response times. +

+ To correctly size a connection pool for your application, create + load test scripts with tools such as Apache JMeter or The Grinder, + and load test your application. +

+ An easy way to determine a starting point is to configure your + connection pool's maximum number of connections to be unbounded, + run a load test, and measure the largest amount of concurrently + used connections. You can then work backward from there to + determine what values of minimum and maximum pooled connections + give the best performance for your particular application. +

Validating Connections

+ MySQL Connector/J can validate the connection by executing a + lightweight ping against a server. In the case of load-balanced + connections, this is performed against all active pooled internal + connections that are retained. This is beneficial to Java + applications using connection pools, as the pool can use this + feature to validate connections. Depending on your connection pool + and configuration, this validation can be carried out at different + times: +

  1. + Before the pool returns a connection to the application. +

  2. + When the application returns a connection to the pool. +

  3. + During periodic checks of idle connections. +

+ To use this feature, specify a validation query in your connection + pool that starts with /* ping */. Note that the + syntax must be exactly as specified. This will cause the driver + send a ping to the server and return a dummy lightweight result + set. When using a ReplicationConnection or + LoadBalancedConnection, the ping will be sent + across all active connections. +

+ It is critical that the syntax be specified correctly. The syntax + needs to be exact for reasons of efficiency, as this test is done + for every statement that is executed: +

+
+protected static final String PING_MARKER = "/* ping */";
+...
+if (sql.charAt(0) == '/') {
+if (sql.startsWith(PING_MARKER)) {
+doPingInstead();
+...
+
+

+ None of the following snippets will work, because the ping syntax + is sensitive to whitespace, capitalization, and placement: +

+sql = "/* PING */ SELECT 1";
+sql = "SELECT 1 /* ping*/";
+sql = "/*ping*/ SELECT 1";
+sql = " /* ping */ SELECT 1";
+sql = "/*to ping or not to ping*/ SELECT 1";
+

+ All of the previous statements will issue a normal + SELECT statement and will + not be transformed into the + lightweight ping. Further, for load-balanced connections, the + statement will be executed against one connection in the internal + pool, rather than validating each underlying physical connection. + This results in the non-active physical connections assuming a + stale state, and they may die. If Connector/J then re-balances, it + might select a dead connection, resulting in an exception being + passed to the application. To help prevent this, you can use + loadBalanceValidateConnectionOnSwapServer to + validate the connection before use. +

+ If your Connector/J deployment uses a connection pool that allows + you to specify a validation query, take advantage of it, but + ensure that the query starts exactly with + /* ping */. This is particularly important if + you are using the load-balancing or replication-aware features of + Connector/J, as it will help keep alive connections which + otherwise will go stale and die, causing problems later. +

Chapter 8 Multi-Host Connections

+ The following sections discuss a number of topics that involve + multi-host connections, namely, server load-balancing, failover, + and replication. +

+ Developers should know the following things about multi-host + connections that are managed through Connector/J: + +

  • + Each multi-host connection is a wrapper of the underlying + physical connections. +

  • + Each of the underlying physical connections has its own + session. Sessions cannot be tracked, shared, or copied, + given the MySQL architecture. +

  • + Every switch between physical connections means a switch + between sessions. +

  • + Within a transaction boundary, there are no switches between + physical connections. Beyond a transaction boundary, there + is no guarantee that a switch does not occur. +

    Note

    + If an application reuses session-scope data (for example, + variables, SSPs) beyond a transaction boundary, failures + are possible, as a switch between the physical connections + (which is also a switch between sessions) might occur. + Therefore, the application should re-prepare the session + data and also restart the last transaction in case of an + exception, or it should re-prepare session data for each + new transaction if it does not want to deal with exception + handling. +

+

8.1 Configuring Server Failover

+ MySQL Connector/J supports server failover. A failover happens + when connection-related errors occur for an underlying, active + connection. The connection errors are, by default, propagated to + the client, which has to handle them by, for example, recreating + the working objects (Statement, + ResultSet, etc.) and restarting the + processes. Sometimes, the driver might eventually fall back to + the original host automatically before the client application + continues to run, in which case the host switch is transparent + and the client application will not even notice it. +

+

+ A connection using failover support works just like a standard + connection: the client does not experience any disruptions in + the failover process. This means the client can rely on the same + connection instance even if two successive statements might be + executed on two different physical hosts. However, this does not + mean the client does not have to deal with the exception that + triggered the server switch. +

+ The failover is configured at the initial setup stage of the + server connection by the connection URL (see explanations for + its format + here): + +

+jdbc:mysql://[primary host][:port],[secondary host 1][:port][,[secondary host 2][:port]]...[/[database]]»
+[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
+

+

+ The host list in the connection URL comprises of two types of + hosts, the primary and the secondary. When starting a new + connection, the driver always tries to connect to the primary + host first and, if required, fails over to the secondary hosts + on the list sequentially when communication problems are + experienced. Even if the initial connection to the primary host + fails and the driver gets connected to a secondary host, the + primary host never loses its special status: for example, it can + be configured with an access mode distinct from those of the + secondary hosts, and it can be put on a higher priority when a + host is to be picked during a failover process. +

+ The failover support is configured by the following connection + properties (their functions are explained in the paragraphs + below): + +

  • + failOverReadOnly +

  • + secondsBeforeRetryMaster +

  • + queriesBeforeRetryMaster +

  • + retriesAllDown +

  • + autoReconnect +

  • + autoReconnectForPools +

+

Configuring Connection Access Mode

+ As with any standard connection, the initial connection to the + primary host is in read/write mode. However, if the driver fails + to establish the initial connection to the primary host and it + automatically switches to the next host on the list, the access + mode now depends on the value of the property + failOverReadOnly, which is + “true” by default. The same happens if the driver + is initially connected to the primary host and, because of some + connection failure, it fails over to a secondary host. Every + time the connection falls back to the primary host, its access + mode will be read/write, irrespective of whether or not the + primary host has been connected to before. The connection access + mode can be changed any time at runtime by calling the + method Connection.setReadOnly(boolean), which + partially overrides the property + failOverReadOnly. When + failOverReadOnly=false and the access mode is + explicitly set to either true or false, it becomes the mode for + every connection after a host switch, no matter what host type + are we connected to; but, if + failOverReadOnly=true, changing the access + mode to read/write is only possible if the driver is connecting + to the primary host; however, even if the access mode cannot be + changed for the current connection, the driver remembers the + client's last intention and, when falling back to the primary + host, that is the mode that will be used. For an illustration, + see the following successions of events with a two-host + connection. + +

  • + Sequence A, with failOverReadOnly=true: + +

    1. + Connects to primary host in read/write mode +

    2. + Sets + Connection.setReadOnly(true); + primary host now in read-only mode +

    3. + Failover event; connects to secondary host in + read-only mode +

    4. + Sets + Connection.setReadOnly(false); + secondary host remains in read-only mode +

    5. + Falls back to primary host; connection now in + read/write mode +

    +

  • + Sequence B, with failOverReadOnly=false +

    1. + Connects to primary host in read/write mode +

    2. + Sets Connection.setReadOnly(true); + primary host now in read-only mode +

    3. + Failover event; connects to secondary host in + read-only mode +

    4. + Set Connection.setReadOnly(false); + connection to secondary host switches to read/write + mode +

    5. + Falls back to primary host; connection now in + read/write mode +

+

+ The difference between the two scenarios is in step 4: the + access mode for the secondary host in sequence A does not change + at that step, but the driver remembers and uses the set mode + when falling back to the primary host, which would be read-only + otherwise; but in sequence B, the access mode for the secondary + host changes immediately. +

Configuring Fallback to Primary Host

+ As already mentioned, the primary host is special in the + failover arrangement when it comes to the host's access mode. + Additionally, the driver tries to fall back to the primary host + as soon as possible by default, even if no communication + exception occurs. Two properties, + secondsBeforeRetryMaster and + queriesBeforeRetryMaster, determine when the + driver is ready to retry a reconnection to the primary host (the + Master in the property names stands for the + primary host of our connection URL, which is not necessarily a + master host in a replication setup; the naming was maintained + for back compatibility with Connector/J versions prior to + 5.1.35): + +

  • + secondsBeforeRetryMaster determines how + much time the driver waits before trying to fall back to + the primary host +

  • + queriesBeforeRetryMaster determines the + number of queries that are executed before the driver + tries to fall back to the primary host. Note that for the + driver, each call to a + Statement.execute*() method increments + the query execution counter; therefore, when calls are + made to Statement.executeBatch() or if + allowMultiQueries or + rewriteBatchStatements are enabled, the + driver may not have an accurate count of the actual number + of queries executed on the server. Also, the driver calls + the Statement.execute*() methods + internally in several occasions. All these mean you can + only use queriesBeforeRetryMaster only + as a coarse specification for when to fall back to the + primary host. +

+ + In general, an attempt to fallback to the primary host is made + when at least one of the conditions specified by the two + properties is met, and the attempt always takes place at + transaction boundaries. However, if auto-commit is turned off, + the check happens only when the method + Connection.commit() or + Connection.rollback() is called. The + automatic fallback to the primary host can be turned off by + setting simultaneously + secondsBeforeRetryMaster and + queriesBeforeRetryMaster to “0”. + Setting only one of the properties to “0” only + disables one part of the check. +

Configuring Reconnection Attempts

+ When establishing a new connection or when a failover event + occurs, the driver tries to connect successively to the next + candidate on the host list. When the end of the list has been + reached, it restarts all over again from the beginning of the + list; however, the primary host is skipped over, if (a) NOT all + the secondary hosts have already been tested at least once, AND + (b) the fallback conditions defined by + secondsBeforeRetryMaster and + queriesBeforeRetryMaster are not yet + fulfilled. Each run-through of the whole host list, (which is + not necessarily completed at the end of the host list) counts as + a single connection attempt. The driver tries as many connection + attempts as specified by the value of the property + retriesAllDown. +

Seamless Reconnection

+ Although not recommended, you can make the driver perform + failovers without invalidating the active + Statement or ResultSet + instances by setting either the parameter + autoReconnect or + autoReconnectForPools to + true. This allows the client to continue + using the same object instances after a failover event, without + taking any exceptional measures. This, however, may lead to + unexpected results: for example, if the driver is connected to + the primary host with read/write access mode and it fails-over + to a secondary host in real-only mode, further attempts to issue + data-changing queries will result in errors, and the client will + not be aware of that. This limitation is particularly relevant + when using data streaming: after the failover, the + ResultSet looks to be alright, but the + underlying connection may have changed already, and no backing + cursor is available anymore. +

8.2 Configuring Load Balancing with Connector/J

+ Connector/J has long provided an effective means to distribute + read/write load across multiple MySQL server instances for + Cluster or master-master replication deployments. Starting with + Connector/J 5.1.3, you can now dynamically configure + load-balanced connections, with no service outage. In-process + transactions are not lost, and no application exceptions are + generated if any application is trying to use that particular + server instance. +

+ The load balancing is configured at the initial setup stage of + the server connection by the following connection URL, which has + a similar format as + the general URL + for MySQL connection, but a specialized scheme: + +

+jdbc:mysql:loadbalance://[host1][:port],[host2][:port][,[host3][:port]]...[/[database]] »
+[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
+

+

+ There are two configuration properties associated with this + functionality: +

  • + loadBalanceConnectionGroup – This + provides the ability to group connections from different + sources. This allows you to manage these JDBC sources within + a single class loader in any combination you choose. If they + use the same configuration, and you want to manage them as a + logical single group, give them the same name. This is the + key property for management: if you do not define a name + (string) for loadBalanceConnectionGroup, + you cannot manage the connections. All load-balanced + connections sharing the same + loadBalanceConnectionGroup value, + regardless of how the application creates them, will be + managed together. +

  • + loadBalanceEnableJMX – The ability to + manage the connections is exposed when you define a + loadBalanceConnectionGroup; but if you + want to manage this externally, enable JMX by setting this + property to true. This enables a JMX + implementation, which exposes the management and monitoring + operations of a connection group. Further, start your + application with the + -Dcom.sun.management.jmxremote JVM flag. + You can then perform connect and perform operations using a + JMX client such as jconsole. +

+ Once a connection has been made using the correct connection + properties, a number of monitoring properties are available: +

  • + Current active host count. +

  • + Current active physical connection count. +

  • + Current active logical connection count. +

  • + Total logical connections created. +

  • + Total transaction count. +

+ The following management operations can also be performed: +

  • + Add host. +

  • + Remove host. +

+ The JMX interface, + com.mysql.jdbc.jmx.LoadBalanceConnectionGroupManagerMBean, + has the following methods: +

  • + int getActiveHostCount(String group); +

  • + int getTotalHostCount(String group); +

  • + long getTotalLogicalConnectionCount(String + group); +

  • + long getActiveLogicalConnectionCount(String + group); +

  • + long getActivePhysicalConnectionCount(String + group); +

  • + long getTotalPhysicalConnectionCount(String + group); +

  • + long getTotalTransactionCount(String + group); +

  • + void removeHost(String group, String host) throws + SQLException; +

  • + void stopNewConnectionsToHost(String group, String + host) throws SQLException; +

  • + void addHost(String group, String host, boolean + forExisting); +

  • + String getActiveHostsList(String group); +

  • + String getRegisteredConnectionGroups(); +

+ The getRegisteredConnectionGroups() method + returns the names of all connection groups defined in that class + loader. +

+ You can test this setup with the following code: +

+
+public class Test {
+
+    private static String URL = "jdbc:mysql:loadbalance://" +
+        "localhost:3306,localhost:3310/test?" +
+        "loadBalanceConnectionGroup=first&loadBalanceEnableJMX=true";
+
+    public static void main(String[] args) throws Exception {
+        new Thread(new Repeater()).start();
+        new Thread(new Repeater()).start();
+        new Thread(new Repeater()).start();
+    }
+
+    static Connection getNewConnection() throws SQLException, ClassNotFoundException {
+        Class.forName("com.mysql.jdbc.Driver");
+        return DriverManager.getConnection(URL, "root", "");
+    }
+
+    static void executeSimpleTransaction(Connection c, int conn, int trans){
+        try {
+            c.setAutoCommit(false);
+            Statement s = c.createStatement();
+            s.executeQuery("SELECT SLEEP(1) /* Connection: " + conn + ", transaction: " + trans + " */");
+            c.commit();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static class Repeater implements Runnable {
+        public void run() {
+            for(int i=0; i < 100; i++){
+                try {
+                    Connection c = getNewConnection();
+                    for(int j=0; j < 10; j++){
+                        executeSimpleTransaction(c, i, j);
+                        Thread.sleep(Math.round(100 * Math.random()));
+                    }
+                    c.close();
+                    Thread.sleep(100);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}
+
+

+ After compiling, the application can be started with the + -Dcom.sun.management.jmxremote flag, to + enable remote management. jconsole can then + be started. The Test main class will be + listed by jconsole. Select this and click + Connect. You can then navigate to the + com.mysql.jdbc.jmx.LoadBalanceConnectionGroupManager + bean. At this point, you can click on various operations and + examine the returned result. +

+ If you now had an additional instance of MySQL running on port + 3309, you could ensure that Connector/J starts using it by using + the addHost(), which is exposed in + jconsole. Note that these operations can be + performed dynamically without having to stop the application + running. +

+ For further information on the combination of load balancing and + failover, see + Section 8.4, “Advanced Load-balancing and Failover Configuration”. +

8.3 Configuring Master/Slave Replication with Connector/J

+ This section describe a number of features of Connector/J's + support for replication-aware deployments. + + +

+ The replication is configured at the initial setup stage of the + server connection by the connection URL, which has a similar + format as the + general URL for MySQL connection, but a specialized + scheme: + +

+jdbc:mysql:replication://[master host][:port],[slave host 1][:port][,[slave host 2][:port]]...[/[database]] »
+[?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
+

+

+ Users may specify the property + allowMasterDownConnections=true to allow + Connection objects to be created even though + no master hosts are reachable. Such + Connection objects report they are read-only, + and isMasterConnection() returns false for + them. The Connection tests for available + master hosts when + Connection.setReadOnly(false) is called, + throwing an SQLException if it cannot establish a connection to + a master, or switching to a master connection if the host is + available. +

+ For Connector/J 5.1.38 and later, users may specify the + property allowSlavesDownConnections=true to + allow Connection objects to be created even + though no slave hosts are reachable. A + Connection then, at runtime, tests for + available slave hosts when + Connection.setReadOnly(true) is called (see + explanation for the method below), throwing an SQLException if + it cannot establish a connection to a slave, unless the property + readFromMasterWhenNoSlaves is set + to be “true” (see below for a description of the + property). +

Scaling out Read Load by Distributing Read Traffic to Slaves

+ Connector/J 3.1.7 and higher includes a variant of the driver + that will automatically send queries to a read/write master, or + a failover or round-robin loadbalanced set of slaves based on + the state of Connection.getReadOnly(). +

+ An application signals that it wants a transaction to be + read-only by calling + Connection.setReadOnly(true). The + replication-aware connection will use one of the slave + connections, which are load-balanced per slave host using a + round-robin scheme. A given connection is sticky to a slave + until a transaction boundary command (a commit or rollback) is + issued, or until the slave is removed from service. For + Connector/J 5.1.38 and later, after calling + Connection.setReadOnly(true), if you want to + allow connection to a master when no slaves are available, set + the property + readFromMasterWhenNoSlaves to + “true.” Notice that the master host will be used in + read-only state in those cases, as if it is a slave host. Also + notice that setting + readFromMasterWhenNoSlaves=true + might result in an extra load for the master host in a + transparent manner. +

+ If you have a write transaction, or if you have a read that is + time-sensitive (remember, replication in MySQL is asynchronous), + set the connection to be not read-only, by calling + Connection.setReadOnly(false) and the driver + will ensure that further calls are sent to the master MySQL + server. The driver takes care of propagating the current state + of autocommit, isolation level, and catalog between all of the + connections that it uses to accomplish this load balancing + functionality. +

+ To enable this functionality, use the + com.mysql.jdbc.ReplicationDriver class when + configuring your application server's connection pool or when + creating an instance of a JDBC driver for your standalone + application. Because it accepts the same URL format as the + standard MySQL JDBC driver, ReplicationDriver + does not currently work with + java.sql.DriverManager-based connection + creation unless it is the only MySQL JDBC driver registered with + the DriverManager . +

+ Here is a short example of how + ReplicationDriver might be used in a + standalone application: +

+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import com.mysql.jdbc.ReplicationDriver;
+
+public class ReplicationDriverDemo {
+
+  public static void main(String[] args) throws Exception {
+    ReplicationDriver driver = new ReplicationDriver();
+
+    Properties props = new Properties();
+
+    // We want this for failover on the slaves
+    props.put("autoReconnect", "true");
+
+    // We want to load balance between the slaves
+    props.put("roundRobinLoadBalance", "true");
+
+    props.put("user", "foo");
+    props.put("password", "bar");
+
+    //
+    // Looks like a normal MySQL JDBC url, with a
+    // comma-separated list of hosts, the first
+    // being the 'master', the rest being any number
+    // of slaves that the driver will load balance against
+    //
+
+    Connection conn =
+        driver.connect("jdbc:mysql:replication://master,slave1,slave2,slave3/test",
+            props);
+
+    //
+    // Perform read/write work on the master
+    // by setting the read-only flag to "false"
+    //
+
+    conn.setReadOnly(false);
+    conn.setAutoCommit(false);
+    conn.createStatement().executeUpdate("UPDATE some_table ....");
+    conn.commit();
+
+    //
+    // Now, do a query from a slave, the driver automatically picks one
+    // from the list
+    //
+
+    conn.setReadOnly(true);
+
+    ResultSet rs =
+      conn.createStatement().executeQuery("SELECT a,b FROM alt_table");
+
+     .......
+  }
+}
+

+ Consider using the Load Balancing JDBC Pool + (lbpool) tool, which provides a wrapper + around the standard JDBC driver and enables you to use DB + connection pools that includes checks for system failures and + uneven load distribution. For more information, see + Load + Balancing JDBC Driver for MySQL (mysql-lbpool). +

Support for Multiple-Master Replication Topographies

+ Since Connector/J 5.1.27, multi-master replication topographies + are supported. +

+ The connection URL for replication discussed earlier (i.e., in + the format of + jdbc:mysql:replication://master,slave1,slave2,slave3/test) + assumes that the first (and only the first) host is the master. + Supporting deployments with an arbitrary number of masters and + slaves requires a different URL syntax for specifying the hosts + and the properties for specific hosts, which is just an + expansion of the URL syntax discussed in + IPv6 Connections with the + property type=[master|slave]; for example: + +

jdbc:mysql://address=(type=master)(host=master1host),address=(type=master)(host=master2host),address=(type=slave)(host=slave1host)/database

+

+ Connector/J uses a load-balanced connection internally for + management of the master connections, which means that + ReplicationConnection, when configured to use + multiple masters, exposes the same options to balance load + across master hosts as described in + Section 8.2, “Configuring Load Balancing with Connector/J”. +

Live Reconfiguration of Replication Topography

+ Since Connector/J 5.1.28, live management of replication host + (single or multi-master) topographies is also supported. This + enables users to promote slaves for Java applications without + requiring an application restart. +

+ The replication hosts are most effectively managed in the + context of a replication connection group. A + ReplicationConnectionGroup class represents a logical grouping + of connections which can be managed together. There may be one + or more such replication connection groups in a given Java class + loader (there can be an application with two different JDBC + resources needing to be managed independently). This key class + exposes host management methods for replication connections, and + ReplicationConnection objects register + themselves with the appropriate + ReplicationConnectionGroup if a value for the + new replicationConnectionGroup property is + specified. The ReplicationConnectionGroup + object tracks these connections until they are closed, and it is + used to manipulate the hosts associated with these connections. +

+ Some important methods related to host management include: + +

  • + getMasterHosts(): Returns a collection + of strings representing the hosts configured as masters +

  • + getSlaveHosts(): Returns a collection + of strings representing the hosts configured as slaves +

  • + addSlaveHost(String host): Adds new + host to pool of possible slave hosts for selection at + start of new read-only workload +

  • + promoteSlaveToMaster(String host): + Removes the host from the pool of potential slaves for + future read-only processes (existing read-only process is + allowed to continue to completion) and adds the host to + the pool of potential master hosts +

  • + removeSlaveHost(String host, boolean + closeGently): Removes the host (host name match + must be exact) from the list of configured slaves; if + closeGently is false, existing + connections which have this host as currently active will + be closed hardly (application should expect exceptions) +

  • + removeMasterHost(String host, boolean + closeGently): Same as + removeSlaveHost(), but removes the host + from the list of configured masters +

+

+ Some useful management metrics include: + +

  • + getConnectionCountWithHostAsSlave(String + host): Returns the number of + ReplicationConnection objects that have the given host + configured as a possible slave +

  • + getConnectionCountWithHostAsMaster(String + host): Returns the number of + ReplicationConnection objects that have the given host + configured as a possible master +

  • + getNumberOfSlavesAdded(): Returns the + number of times a slave host has been dynamically added to + the group pool +

  • + getNumberOfSlavesRemoved(): Returns the + number of times a slave host has been dynamically removed + from the group pool +

  • + getNumberOfSlavePromotions(): Returns + the number of times a slave host has been promoted to a + master +

  • + getTotalConnectionCount(): Returns the + number of ReplicationConnection objects which have been + registered with this group +

  • + getActiveConnectionCount(): Returns the + number of ReplicationConnection objects currently being + managed by this group +

+

ReplicationConnectionGroupManager

+ com.mysql.jdbc.ReplicationConnectionGroupManager + provides access to the replication connection groups, together + with some utility methods. + +

  • + getConnectionGroup(String groupName): + Returns the ReplicationConnectionGroup + object matching the groupName provided +

+

+ The other methods in + ReplicationConnectionGroupManager mirror + those of ReplicationConnectionGroup, except + that the first argument is a String group name. These methods + will operate on all matching ReplicationConnectionGroups, which + are helpful for removing a server from service and have it + decommissioned across all possible + ReplicationConnectionGroups. +

+ These methods might be useful for in-JVM management of + replication hosts if an application triggers topography changes. + For managing host configurations from outside the JVM, JMX can + be used. +

Using JMX for Managing Replication Hosts

+ When Connector/J is started with + replicationEnableJMX=true and a value set for + the property replicationConnectionGroup, a + JMX MBean will be registered, allowing manipulation of + replication hosts by a JMX client. The MBean interface is + defined in + com.mysql.jdbc.jmx.ReplicationGroupManagerMBean, + and leverages the + ReplicationConnectionGroupManager static + methods: +

+ public abstract void addSlaveHost(String groupFilter, String host) throws SQLException;
+ public abstract void removeSlaveHost(String groupFilter, String host) throws SQLException;
+ public abstract void promoteSlaveToMaster(String groupFilter, String host) throws SQLException;
+ public abstract void removeMasterHost(String groupFilter, String host) throws SQLException;
+ public abstract String getMasterHostsList(String group);
+ public abstract String getSlaveHostsList(String group);
+ public abstract String getRegisteredConnectionGroups();
+ public abstract int getActiveMasterHostCount(String group);
+ public abstract int getActiveSlaveHostCount(String group);
+ public abstract int getSlavePromotionCount(String group);
+ public abstract long getTotalLogicalConnectionCount(String group);
+ public abstract long getActiveLogicalConnectionCount(String group);
+

8.4 Advanced Load-balancing and Failover Configuration

+ Connector/J provides a useful load-balancing implementation for + MySQL Cluster or multi-master deployments, as explained in + Section 8.2, “Configuring Load Balancing with Connector/J” + and + Support for Multiple-Master Replication Topographies. + As of Connector/J 5.1.12, this same implementation is used for + balancing load between read-only slaves with + ReplicationDriver. +

+ When trying to balance workload between multiple servers, the + driver has to determine when it is safe to swap servers, doing + so in the middle of a transaction, for example, could cause + problems. It is important not to lose state information. For + this reason, Connector/J will only try to pick a new server when + one of the following happens: +

  1. + At transaction boundaries (transactions are explicitly + committed or rolled back). +

  2. + A communication exception (SQL State starting with "08") is + encountered. +

  3. + When a SQLException matches conditions + defined by user, using the extension points defined by the + loadBalanceSQLStateFailover, + loadBalanceSQLExceptionSubclassFailover + or loadBalanceExceptionChecker + properties. +

+ The third condition revolves around three new properties + introduced with Connector/J 5.1.13. It allows you to control + which SQLExceptions trigger failover. +

  • + loadBalanceExceptionChecker - The + loadBalanceExceptionChecker property is + really the key. This takes a fully-qualified class name + which implements the new + com.mysql.jdbc.LoadBalanceExceptionChecker + interface. This interface is very simple, and you only need + to implement the following method: +

    +public boolean shouldExceptionTriggerFailover(SQLException ex)
    +

    + A SQLException is passed in, and a + boolean returned. A value of true + triggers a failover, false does not. +

    + You can use this to implement your own custom logic. An + example where this might be useful is when dealing with + transient errors when using MySQL Cluster, where certain + buffers may become overloaded. The following code snippet + illustrates this: +

    +
    +public class NdbLoadBalanceExceptionChecker
    + extends StandardLoadBalanceExceptionChecker {
    +
    + public boolean shouldExceptionTriggerFailover(SQLException ex) {
    +  return super.shouldExceptionTriggerFailover(ex)
    +    ||  checkNdbException(ex);
    + }
    +
    + private boolean checkNdbException(SQLException ex){
    + // Have to parse the message since most NDB errors
    + // are mapped to the same DEMC.
    +  return (ex.getMessage().startsWith("Lock wait timeout exceeded") ||
    +  (ex.getMessage().startsWith("Got temporary error")
    +  && ex.getMessage().endsWith("from NDB")));
    + }
    +}
    +
    +

    + The code above extends + com.mysql.jdbc.StandardLoadBalanceExceptionChecker, + which is the default implementation. There are a few + convenient shortcuts built into this, for those who want to + have some level of control using properties, without writing + Java code. This default implementation uses the two + remaining properties: + loadBalanceSQLStateFailover and + loadBalanceSQLExceptionSubclassFailover. +

  • + loadBalanceSQLStateFailover - allows you + to define a comma-delimited list of + SQLState code prefixes, against which a + SQLException is compared. If the prefix + matches, failover is triggered. So, for example, the + following would trigger a failover if a given + SQLException starts with "00", or is + "12345": +

    +loadBalanceSQLStateFailover=00,12345
    +
  • + loadBalanceSQLExceptionSubclassFailover - + can be used in conjunction with + loadBalanceSQLStateFailover or on its + own. If you want certain subclasses of + SQLException to trigger failover, simply + provide a comma-delimited list of fully-qualified class or + interface names to check against. For example, if you want + all SQLTransientConnectionExceptions to + trigger failover, you would specify: +

    +loadBalanceSQLExceptionSubclassFailover=java.sql.SQLTransientConnectionException
    +

+ While the three failover conditions enumerated earlier suit most + situations, if autocommit is enabled, + Connector/J never re-balances, and continues using the same + physical connection. This can be problematic, particularly when + load-balancing is being used to distribute read-only load across + multiple slaves. However, Connector/J can be configured to + re-balance after a certain number of statements are executed, + when autocommit is enabled. This + functionality is dependent upon the following properties: +

  • + loadBalanceAutoCommitStatementThreshold + – defines the number of matching statements which will + trigger the driver to potentially swap physical server + connections. The default value, 0, retains the behavior that + connections with autocommit enabled are + never balanced. +

  • + loadBalanceAutoCommitStatementRegex – + the regular expression against which statements must match. + The default value, blank, matches all statements. So, for + example, using the following properties will cause + Connector/J to re-balance after every third statement that + contains the string “test”: +

    +loadBalanceAutoCommitStatementThreshold=3
    +loadBalanceAutoCommitStatementRegex=.*test.*
    +

    + loadBalanceAutoCommitStatementRegex can + prove useful in a number of situations. Your application may + use temporary tables, server-side session state variables, + or connection state, where letting the driver arbitrarily + swap physical connections before processing is complete + could cause data loss or other problems. This allows you to + identify a trigger statement that is only executed when it + is safe to swap physical connections. +

Chapter 9 Using the Connector/J Interceptor Classes

+ An interceptor is a software design pattern that provides a + transparent way to extend or modify some aspect of a program, + similar to a user exit. No recompiling is required. With + Connector/J, the interceptors are enabled and disabled by updating + the connection string to refer to different sets of interceptor + classes that you instantiate. +

+ The connection properties that control the interceptors are + explained in + Section 5.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J”: +

  • + connectionLifecycleInterceptors, where you + specify the fully qualified names of classes that implement + the + com.mysql.jdbc.ConnectionLifecycleInterceptor + interface. In these kinds of interceptor classes, you might + log events such as rollbacks, measure the time between + transaction start and end, or count events such as calls to + setAutoCommit(). +

  • + exceptionInterceptors, where you specify + the fully qualified names of classes that implement the + com.mysql.jdbc.ExceptionInterceptor + interface. In these kinds of interceptor classes, you might + add extra diagnostic information to exceptions that can have + multiple causes or indicate a problem with server settings. + Because exceptionInterceptors classes are + only called when handling a SQLException + thrown from Connector/J code, they can be used even in + production deployments without substantial performance + overhead. +

  • + statementInterceptors, where you specify + the fully qualified names of classes that implement the + com.mysql.jdbc.StatementInterceptorV2 + interface. In these kinds of interceptor classes, you might + change or augment the processing done by certain kinds of + statements, such as automatically checking for queried data in + a memcached server, rewriting slow queries, + logging information about statement execution, or route + requests to remote servers. +

Chapter 10 Using Connector/J with Tomcat

+ The following instructions are based on the instructions for + Tomcat-5.x, available at + http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html + which is current at the time this document was written. +

+ First, install the .jar file that comes with + Connector/J in $CATALINA_HOME/common/lib so + that it is available to all applications installed in the + container. +

+ Next, configure the JNDI DataSource by adding a declaration + resource to $CATALINA_HOME/conf/server.xml in + the context that defines your web application: +

+  <Context ....>
+
+  ...
+
+  <Resource name="jdbc/MySQLDB"
+               auth="Container"
+               type="javax.sql.DataSource"/>
+
+  <ResourceParams name="jdbc/MySQLDB">
+    <parameter>
+      <name>factory</name>
+      <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+    </parameter>
+
+    <parameter>
+      <name>maxActive</name>
+      <value>10</value>
+    </parameter>
+
+    <parameter>
+      <name>maxIdle</name>
+      <value>5</value>
+    </parameter>
+
+    <parameter>
+      <name>validationQuery</name>
+      <value>SELECT 1</value>
+    </parameter>
+
+    <parameter>
+      <name>testOnBorrow</name>
+      <value>true</value>
+    </parameter>
+
+    <parameter>
+      <name>testWhileIdle</name>
+      <value>true</value>
+    </parameter>
+
+    <parameter>
+      <name>timeBetweenEvictionRunsMillis</name>
+      <value>10000</value>
+    </parameter>
+
+    <parameter>
+      <name>minEvictableIdleTimeMillis</name>
+      <value>60000</value>
+    </parameter>
+
+    <parameter>
+     <name>username</name>
+     <value>someuser</value>
+    </parameter>
+
+    <parameter>
+     <name>password</name>
+     <value>somepass</value>
+    </parameter>
+
+    <parameter>
+       <name>driverClassName</name>
+       <value>com.mysql.jdbc.Driver</value>
+    </parameter>
+
+    <parameter>
+      <name>url</name>
+      <value>jdbc:mysql://localhost:3306/test</value>
+    </parameter>
+
+  </ResourceParams>
+</Context>
+

+ Note that Connector/J 5.1.3 introduced a facility whereby, rather + than use a validationQuery value of + SELECT 1, it is possible to use + validationQuery with a value set to /* + ping */. This sends a ping to the server which then + returns a fake result set. This is a lighter weight solution. It + also has the advantage that if using + ReplicationConnection or + LoadBalancedConnection type connections, the + ping will be sent across all active connections. The following XML + snippet illustrates how to select this option: +

+
+<parameter>
+ <name>validationQuery</name>
+ <value>/* ping */</value>
+</parameter>
+
+

+ Note that /* ping */ has to be specified + exactly. +

+ In general, follow the installation instructions that come with + your version of Tomcat, as the way you configure datasources in + Tomcat changes from time to time, and if you use the wrong syntax + in your XML file, you will most likely end up with an exception + similar to the following: +

+Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQL
+state: null 
+

+ Note that the auto-loading of drivers having the + META-INF/service/java.sql.Driver class in + JDBC 4.0 and above causes an improper undeployment of the + Connector/J driver in Tomcat on Windows. Namely, the Connector/J + jar remains locked. This is an initialization problem that is not + related to the driver. The possible workarounds, if viable, are as + follows: use "antiResourceLocking=true" as a + Tomcat Context attribute, or remove the + META-INF/ directory. +

Chapter 11 Using Connector/J with JBoss

+ These instructions cover JBoss-4.x. To make the JDBC driver + classes available to the application server, copy the + .jar file that comes with Connector/J to the + lib directory for your server configuration + (which is usually called default). Then, in + the same configuration directory, in the subdirectory named + deploy, create a datasource configuration file that ends with + -ds.xml, which tells JBoss to deploy this file + as a JDBC Datasource. The file should have the following contents: +

+<datasources>
+    <local-tx-datasource>
+
+        <jndi-name>MySQLDB</jndi-name>
+        <connection-url>jdbc:mysql://localhost:3306/dbname</connection-url>
+        <driver-class>com.mysql.jdbc.Driver</driver-class>
+        <user-name>user</user-name>
+        <password>pass</password>
+
+        <min-pool-size>5</min-pool-size>
+
+        <max-pool-size>20</max-pool-size>
+
+        <idle-timeout-minutes>5</idle-timeout-minutes>
+
+        <exception-sorter-class-name>
+  com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
+        </exception-sorter-class-name>
+        <valid-connection-checker-class-name>
+  com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
+        </valid-connection-checker-class-name>
+
+    </local-tx-datasource>
+</datasources> 
+

Chapter 12 Using Connector/J with Spring

+ The Spring Framework is a Java-based application framework + designed for assisting in application design by providing a way to + configure components. The technique used by Spring is a well known + design pattern called Dependency Injection (see + Inversion + of Control Containers and the Dependency Injection + pattern). This article will focus on Java-oriented access + to MySQL databases with Spring 2.0. For those wondering, there is + a .NET port of Spring appropriately named Spring.NET. +

+ Spring is not only a system for configuring components, but also + includes support for aspect oriented programming (AOP). This is + one of the main benefits and the foundation for Spring's resource + and transaction management. Spring also provides utilities for + integrating resource management with JDBC and Hibernate. +

+ For the examples in this section the MySQL world sample database + will be used. The first task is to set up a MySQL data source + through Spring. Components within Spring use the + “bean” terminology. For example, to configure a + connection to a MySQL server supporting the world sample database, + you might use: +

+
+<util:map id="dbProps">
+    <entry key="db.driver" value="com.mysql.jdbc.Driver"/>
+    <entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/>
+    <entry key="db.username" value="myuser"/>
+    <entry key="db.password" value="mypass"/>
+</util:map>
+
+

+ In the above example, we are assigning values to properties that + will be used in the configuration. For the datasource + configuration: +

+
+<bean id="dataSource"
+       class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+</bean>
+
+

+ The placeholders are used to provide values for properties of this + bean. This means that you can specify all the properties of the + configuration in one place instead of entering the values for each + property on each bean. We do, however, need one more bean to pull + this all together. The last bean is responsible for actually + replacing the placeholders with the property values. +

+
+<bean
+ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+    <property name="properties" ref="dbProps"/>
+</bean>
+
+

+ Now that we have our MySQL data source configured and ready to go, + we write some Java code to access it. The example below will + retrieve three random cities and their corresponding country using + the data source we configured with Spring. +

+// Create a new application context. this processes the Spring config
+ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex1appContext.xml");
+// Retrieve the data source from the application context
+    DataSource ds = (DataSource) ctx.getBean("dataSource");
+// Open a database connection using Spring's DataSourceUtils
+Connection c = DataSourceUtils.getConnection(ds);
+try {
+    // retrieve a list of three random cities
+    PreparedStatement ps = c.prepareStatement(
+        "select City.Name as 'City', Country.Name as 'Country' " +
+        "from City inner join Country on City.CountryCode = Country.Code " +
+        "order by rand() limit 3");
+    ResultSet rs = ps.executeQuery();
+    while(rs.next()) {
+        String city = rs.getString("City");
+        String country = rs.getString("Country");
+        System.out.printf("The city %s is in %s%n", city, country);
+    }
+} catch (SQLException ex) {
+    // something has failed and we print a stack trace to analyse the error
+    ex.printStackTrace();
+    // ignore failure closing connection
+    try { c.close(); } catch (SQLException e) { }
+} finally {
+    // properly release our connection
+    DataSourceUtils.releaseConnection(c, ds);
+}
+

+ This is very similar to normal JDBC access to MySQL with the main + difference being that we are using DataSourceUtils instead of the + DriverManager to create the connection. +

+ While it may seem like a small difference, the implications are + somewhat far reaching. Spring manages this resource in a way + similar to a container managed data source in a J2EE application + server. When a connection is opened, it can be subsequently + accessed in other parts of the code if it is synchronized with a + transaction. This makes it possible to treat different parts of + your application as transactional instead of passing around a + database connection. +

12.1 Using JdbcTemplate

+ Spring makes extensive use of the Template method design pattern + (see + Template + Method Pattern). Our immediate focus will be on the + JdbcTemplate and related classes, + specifically NamedParameterJdbcTemplate. The + template classes handle obtaining and releasing a connection for + data access when one is needed. +

+ The next example shows how to use + NamedParameterJdbcTemplate inside of a DAO + (Data Access Object) class to retrieve a random city given a + country code. +

+public class Ex2JdbcDao {
+     /**
+     * Data source reference which will be provided by Spring.
+     */
+     private DataSource dataSource;
+
+     /**
+     * Our query to find a random city given a country code. Notice
+     * the ":country" parameter toward the end. This is called a
+     * named parameter.
+     */
+     private String queryString = "select Name from City " +
+        "where CountryCode = :country order by rand() limit 1";
+
+     /**
+     * Retrieve a random city using Spring JDBC access classes.
+     */
+     public String getRandomCityByCountryCode(String cntryCode) {
+         // A template that permits using queries with named parameters
+         NamedParameterJdbcTemplate template =
+         new NamedParameterJdbcTemplate(dataSource);
+         // A java.util.Map is used to provide values for the parameters
+         Map params = new HashMap();
+         params.put("country", cntryCode);
+         // We query for an Object and specify what class we are expecting
+         return (String)template.queryForObject(queryString, params, String.class);
+     }
+
+    /**
+    * A JavaBean setter-style method to allow Spring to inject the data source.
+    * @param dataSource
+    */
+    public void setDataSource(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+}
+

+ The focus in the above code is on the + getRandomCityByCountryCode() method. We + pass a country code and use the + NamedParameterJdbcTemplate to query for a + city. The country code is placed in a Map with the key + "country", which is the parameter is named in the SQL query. +

+ To access this code, you need to configure it with Spring by + providing a reference to the data source. +

+
+<bean id="dao" class="code.Ex2JdbcDao">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+

+ At this point, we can just grab a reference to the DAO from + Spring and call + getRandomCityByCountryCode(). +

+    // Create the application context
+    ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex2appContext.xml");
+    // Obtain a reference to our DAO
+    Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao");
+
+    String countryCode = "USA";
+
+    // Find a few random cities in the US
+    for(int i = 0; i < 4; ++i)
+        System.out.printf("A random city in %s is %s%n", countryCode,
+            dao.getRandomCityByCountryCode(countryCode));
+

+ This example shows how to use Spring's JDBC classes to + completely abstract away the use of traditional JDBC classes + including Connection and + PreparedStatement. +

12.2 Transactional JDBC Access

+ You might be wondering how we can add transactions into our code + if we do not deal directly with the JDBC classes. Spring + provides a transaction management package that not only replaces + JDBC transaction management, but also enables declarative + transaction management (configuration instead of code). +

+ To use transactional database access, we will need to change the + storage engine of the tables in the world database. The + downloaded script explicitly creates MyISAM tables which do not + support transactional semantics. The InnoDB storage engine does + support transactions and this is what we will be using. We can + change the storage engine with the following statements. +

+ALTER TABLE City ENGINE=InnoDB;
+ALTER TABLE Country ENGINE=InnoDB;
+ALTER TABLE CountryLanguage ENGINE=InnoDB;
+

+ A good programming practice emphasized by Spring is separating + interfaces and implementations. What this means is that we can + create a Java interface and only use the operations on this + interface without any internal knowledge of what the actual + implementation is. We will let Spring manage the implementation + and with this it will manage the transactions for our + implementation. +

+ First you create a simple interface: +

+public interface Ex3Dao {
+    Integer createCity(String name, String countryCode,
+    String district, Integer population);
+}
+

+ This interface contains one method that will create a new city + record in the database and return the id of the new record. Next + you need to create an implementation of this interface. +

+public class Ex3DaoImpl implements Ex3Dao {
+    protected DataSource dataSource;
+    protected SqlUpdate updateQuery;
+    protected SqlFunction idQuery;
+
+    public Integer createCity(String name, String countryCode,
+        String district, Integer population) {
+            updateQuery.update(new Object[] { name, countryCode,
+                   district, population });
+            return getLastId();
+        }
+
+    protected Integer getLastId() {
+        return idQuery.run();
+    }
+}
+

+ You can see that we only operate on abstract query objects here + and do not deal directly with the JDBC API. Also, this is the + complete implementation. All of our transaction management will + be dealt with in the configuration. To get the configuration + started, we need to create the DAO. +

+
+<bean id="dao" class="code.Ex3DaoImpl">
+    <property name="dataSource" ref="dataSource"/>
+    <property name="updateQuery">...</property>
+    <property name="idQuery">...</property>
+</bean>
+
+

+ Now you need to set up the transaction configuration. The first + thing you must do is create transaction manager to manage the + data source and a specification of what transaction properties + are required for the dao methods. +

+
+<bean id="transactionManager"
+  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+<tx:advice id="txAdvice" transaction-manager="transactionManager">
+    <tx:attributes>
+        <tx:method name="*"/>
+    </tx:attributes>
+</tx:advice>
+
+

+ The preceding code creates a transaction manager that handles + transactions for the data source provided to it. The + txAdvice uses this transaction manager and + the attributes specify to create a transaction for all methods. + Finally you need to apply this advice with an AOP pointcut. +

+
+<aop:config>
+    <aop:pointcut id="daoMethods"
+        expression="execution(* code.Ex3Dao.*(..))"/>
+     <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/>
+</aop:config>
+
+

+ This basically says that all methods called on the + Ex3Dao interface will be wrapped in a + transaction. To make use of this, you only have to retrieve the + dao from the application context and call a + method on the dao instance. +

+Ex3Dao dao = (Ex3Dao) ctx.getBean("dao");
+Integer id = dao.createCity(name,  countryCode, district, pop);
+

+ We can verify from this that there is no transaction management + happening in our Java code and it is all configured with Spring. + This is a very powerful notion and regarded as one of the most + beneficial features of Spring. +

12.3 Connection Pooling with Spring

+ In many situations, such as web applications, there will be a + large number of small database transactions. When this is the + case, it usually makes sense to create a pool of database + connections available for web requests as needed. Although MySQL + does not spawn an extra process when a connection is made, there + is still a small amount of overhead to create and set up the + connection. Pooling of connections also alleviates problems such + as collecting large amounts of sockets in the + TIME_WAIT state. +

+ Setting up pooling of MySQL connections with Spring is as simple + as changing the data source configuration in the application + context. There are a number of configurations that we can use. + The first example is based on the + Jakarta + Commons DBCP library. The example below replaces the + source configuration that was based on + DriverManagerDataSource with DBCP's + BasicDataSource. +

+
+<bean id="dataSource" destroy-method="close"
+  class="org.apache.commons.dbcp.BasicDataSource">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+    <property name="initialSize" value="3"/>
+</bean>
+
+

+ The configuration of the two solutions is very similar. The + difference is that DBCP will pool connections to the database + instead of creating a new connection every time one is + requested. We have also set a parameter here called + initialSize. This tells DBCP that we want + three connections in the pool when it is created. +

+ Another way to configure connection pooling is to configure a + data source in our J2EE application server. Using JBoss as an + example, you can set up the MySQL connection pool by creating a + file called mysql-local-ds.xml and placing + it in the server/default/deploy directory in JBoss. Once we have + this setup, we can use JNDI to look it up. With Spring, this + lookup is very simple. The data source configuration looks like + this. +

+
+<jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/>
+
+

Chapter 13 Using Connector/J with GlassFish

+ This section explains how to use MySQL Connector/J with GlassFish ™ + Server Open Source Edition 3.0.1. GlassFish can be downloaded from + the + GlassFish + website. +

+ Once GlassFish is installed, make sure it can access MySQL Connector/J. To do + this, copy the MySQL Connector/J jar file to the + domain-dir/lib + directory. For example, copy + mysql-connector-java-5.1.30-bin.jar to + C:\glassfish-install-path\domains\domain-name\lib. + Restart the GlassFish Application Server. For more information, + see “Integrating the JDBC Driver” in + GlassFish Server Open Source Edition Administration + Guide, available at + GlassFish + Server Documentation. +

+ You are now ready to create JDBC Connection Pools and JDBC + Resources. +

+ Creating a Connection Pool +

  1. + In the GlassFish Administration Console, using the navigation + tree navigate to Resources, + JDBC, Connection + Pools. +

  2. + In the JDBC Connection Pools frame click + New. You will enter a two step wizard. +

  3. + In the Name field under General + Settings enter the name for the connection pool, + for example enter MySQLConnPool. +

  4. + In the Resource Type field, select + javax.sql.DataSource from the drop-down + listbox. +

  5. + In the Database Vendor field, select + MySQL from the drop-down listbox. Click + Next to go to the next page of the + wizard. +

  6. + You can accept the default settings for General Settings, Pool + Settings and Transactions for this example. Scroll down to + Additional Properties. +

  7. + In Additional Properties you will need to ensure the following + properties are set: +

    • + ServerName - The server + to connect to. For local testing this will be + localhost. +

    • + User - The user name with + which to connect to MySQL. +

    • + Password - The + corresponding password for the user. +

    • + DatabaseName - The + database to connect to, for example the sample MySQL + database World. +

  8. + Click Finish to exit the wizard. You + will be taken to the JDBC Connection + Pools page where all current connection pools, + including the one you just created, will be displayed. +

  9. + In the JDBC Connection Pools frame click + on the connection pool you just created. Here, you can review + and edit information about the connection pool. Because + Connector/J does not support optimized validation queries, go + to the Advanced tab, and under Connection + Validation, configure the following settings: + +

    • + Connection Validation - + select Required. +

    • + Validation Method - + select table from the drop-down + menu. +

    • + Table Name - enter + DUAL. +

    +

  10. + To test your connection pool click the + Ping button at the top of the frame. A + message will be displayed confirming correct operation or + otherwise. If an error message is received recheck the + previous steps, and ensure that MySQL Connector/J has been correctly copied + into the previously specified location. +

+ Now that you have created a connection pool you will also need to + create a JDBC Resource (data source) for use by your application. +

+ Creating a JDBC Resource +

+ Your Java application will usually reference a data source object + to establish a connection with the database. This needs to be + created first using the following procedure. +

  • + Using the navigation tree in the GlassFish Administration + Console, navigate to Resources, + JDBC, JDBC + Resources. A list of resources will be displayed in + the JDBC Resources frame. +

  • + Click New. The New JDBC + Resource frame will be displayed. +

  • + In the JNDI Name field, enter the JNDI + name that will be used to access this resource, for example + enter jdbc/MySQLDataSource. +

  • + In the Pool Name field, select a + connection pool you want this resource to use from the + drop-down listbox. +

  • + Optionally, you can enter a description into the + Description field. +

  • + Additional properties can be added if required. +

  • + Click OK to create the new JDBC + resource. The JDBC Resources frame will + list all available JDBC Resources. +

13.1 A Simple JSP Application with GlassFish, Connector/J and MySQL

+ This section shows how to deploy a simple JSP application on + GlassFish, that connects to a MySQL database. +

+ This example assumes you have already set up a suitable + Connection Pool and JDBC Resource, as explained in the preceding + sections. It is also assumed you have a sample database + installed, such as world. +

+ The main application code, index.jsp is + presented here: +

+
+<%@ page import="java.sql.*, javax.sql.*, java.io.*, javax.naming.*" %>
+<html>
+<head><title>Hello world from JSP</title></head>
+<body>
+<%
+  InitialContext ctx;
+  DataSource ds;
+  Connection conn;
+  Statement stmt;
+  ResultSet rs;
+
+  try {
+    ctx = new InitialContext();
+    ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource");
+    //ds = (DataSource) ctx.lookup("jdbc/MySQLDataSource");
+    conn = ds.getConnection();
+    stmt = conn.createStatement();
+    rs = stmt.executeQuery("SELECT * FROM Country");
+
+    while(rs.next()) {
+%>
+    <h3>Name: <%= rs.getString("Name") %></h3>
+    <h3>Population: <%= rs.getString("Population") %></h3>
+<%    
+    }
+  }
+  catch (SQLException se) {
+%>
+    <%= se.getMessage() %>
+<%      
+  }
+  catch (NamingException ne) {
+%>  
+    <%= ne.getMessage() %>
+<%
+  }
+%>
+</body>
+</html>
+
+

+ In addition two XML files are required: + web.xml, and + sun-web.xml. There may be other files + present, such as classes and images. These files are organized + into the directory structure as follows: +

+index.jsp
+WEB-INF
+   |
+   - web.xml
+   - sun-web.xml
+

+ The code for web.xml is: +

+
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+  <display-name>HelloWebApp</display-name>  
+  <distributable/>
+  <resource-ref>
+    <res-ref-name>jdbc/MySQLDataSource</res-ref-name>
+    <res-type>javax.sql.DataSource</res-type>
+    <res-auth>Container</res-auth>
+    <res-sharing-scope>Shareable</res-sharing-scope>                
+  </resource-ref>
+</web-app>
+
+

+ The code for sun-web.xml is: +

+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
+<sun-web-app>
+  <context-root>HelloWebApp</context-root>
+  <resource-ref>
+    <res-ref-name>jdbc/MySQLDataSource</res-ref-name>
+    <jndi-name>jdbc/MySQLDataSource</jndi-name>  
+  </resource-ref> 
+</sun-web-app>
+
+

+ These XML files illustrate a very important aspect of running + JDBC applications on GlassFish. On GlassFish it is important to + map the string specified for a JDBC resource to its JNDI name, + as set up in the GlassFish administration console. In this + example, the JNDI name for the JDBC resource, as specified in + the GlassFish Administration console when creating the JDBC + Resource, was jdbc/MySQLDataSource. This must + be mapped to the name given in the application. In this example + the name specified in the application, + jdbc/MySQLDataSource, and the JNDI name, + happen to be the same, but this does not necessarily have to be + the case. Note that the XML element <res-ref-name> is used + to specify the name as used in the application source code, and + this is mapped to the JNDI name specified using the + <jndi-name> element, in the file + sun-web.xml. The resource also has to be + created in the web.xml file, although the + mapping of the resource to a JNDI name takes place in the + sun-web.xml file. +

+ If you do not have this mapping set up correctly in the XML + files you will not be able to lookup the data source using a + JNDI lookup string such as: +

+ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource");
+

+ You will still be able to access the data source directly using: +

+ds = (DataSource) ctx.lookup("jdbc/MySQLDataSource");
+

+ With the source files in place, in the correct directory + structure, you are ready to deploy the application: +

  1. + In the navigation tree, navigate to + Applications - the + Applications frame will be displayed. + Click Deploy. +

  2. + You can now deploy an application packaged into a single WAR + file from a remote client, or you can choose a packaged file + or directory that is locally accessible to the server. If + you are simply testing an application locally you can simply + point GlassFish at the directory that contains your + application, without needing to package the application into + a WAR file. +

  3. + Now select the application type from the + Type drop-down listbox, which in this + example is Web application. +

  4. + Click OK. +

+ Now, when you navigate to the Applications + frame, you will have the option to Launch, + Redeploy, or Restart + your application. You can test your application by clicking + Launch. The application will connection to + the MySQL database and display the Name and Population of + countries in the Country table. +

13.2 A Simple Servlet with GlassFish, Connector/J and MySQL

+ This section describes a simple servlet that can be used in the + GlassFish environment to access a MySQL database. As with the + previous section, this example assumes the sample database + world is installed. +

+ The project is set up with the following directory structure: +

+index.html
+WEB-INF
+   |
+   - web.xml
+   - sun-web.xml
+   - classes
+        |
+        - HelloWebServlet.java
+        - HelloWebServlet.class
+

+ The code for the servlet, located in + HelloWebServlet.java, is as follows: +

+
+import javax.servlet.http.*;
+import javax.servlet.*;
+import java.io.*;
+import java.sql.*;
+import javax.sql.*;
+import javax.naming.*;
+
+public class HelloWebServlet extends HttpServlet {
+
+  InitialContext ctx = null;
+  DataSource ds = null;
+  Connection conn = null;
+  PreparedStatement ps = null;
+  ResultSet rs = null;
+
+  String sql = "SELECT Name, Population FROM Country WHERE Name=?";
+
+  public void init () throws ServletException {
+    try {
+      ctx = new InitialContext();
+      ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MySQLDataSource");
+      conn = ds.getConnection();
+      ps = conn.prepareStatement(sql);
+    }
+    catch (SQLException se) {
+      System.out.println("SQLException: "+se.getMessage());
+    }
+    catch (NamingException ne) {
+      System.out.println("NamingException: "+ne.getMessage());  
+    }  
+  }
+
+  public void destroy () {
+    try {
+      if (rs != null)
+        rs.close();
+      if (ps != null)
+        ps.close();
+      if (conn != null)
+        conn.close();
+      if (ctx != null)
+        ctx.close(); 
+    }     
+    catch (SQLException se) {
+      System.out.println("SQLException: "+se.getMessage());
+    }
+    catch (NamingException ne) {
+      System.out.println("NamingException: "+ne.getMessage());  
+    }  
+  }
+
+  public void doPost(HttpServletRequest req, HttpServletResponse resp){
+    try {
+      String country_name = req.getParameter("country_name");    
+      resp.setContentType("text/html");
+      PrintWriter writer = resp.getWriter();
+      writer.println("<html><body>");
+      writer.println("<p>Country: "+country_name+"</p>");
+      ps.setString(1, country_name);
+      rs = ps.executeQuery();
+      if (!rs.next()){
+        writer.println("<p>Country does not exist!</p>");
+      }
+      else {
+        rs.beforeFirst();
+        while(rs.next()) {
+          writer.println("<p>Name: "+rs.getString("Name")+"</p>");
+          writer.println("<p>Population: "+rs.getString("Population")+"</p>");
+        }
+      }
+      writer.println("</body></html>");
+      writer.close(); 
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }  
+  }
+
+  public void doGet(HttpServletRequest req, HttpServletResponse resp){
+    try {    
+      resp.setContentType("text/html");
+      PrintWriter writer = resp.getWriter();
+      writer.println("<html><body>");
+      writer.println("<p>Hello from servlet doGet()</p>");
+      writer.println("</body></html>");
+      writer.close(); 
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }  
+  }
+}
+
+

+ In the preceding code a basic doGet() method + is implemented, but is not used in the example. The code to + establish the connection with the database is as shown in the + previous example, + Section 13.1, “A Simple JSP Application with GlassFish, Connector/J and MySQL”, + and is most conveniently located in the servlet + init() method. The corresponding freeing of + resources is located in the destroy method. The main + functionality of the servlet is located in the + doPost() method. If the user enters into the + input form a country name that can be located in the database, + the population of the country is returned. The code is invoked + using a POST action associated with the input form. The form is + defined in the file index.html: +

+
+<html>
+  <head><title>HelloWebServlet</title></head>
+  
+  <body>
+    <h1>HelloWebServlet</h1>
+    
+    <p>Please enter country name:</p>
+    
+    <form action="HelloWebServlet" method="POST">
+      <input type="text" name="country_name" length="50" />
+      <input type="submit" value="Submit" />
+    </form>
+    
+  </body>
+</html>
+
+

+ The XML files web.xml and + sun-web.xml are as for the example in the + preceding section, + Section 13.1, “A Simple JSP Application with GlassFish, Connector/J and MySQL”, + no additional changes are required. +

+ When compiling the Java source code, you will need to specify + the path to the file javaee.jar. On + Windows, this can be done as follows: +

+shell> javac -classpath c:\glassfishv3\glassfish\lib\javaee.jar HelloWebServlet.java 
+

+ Once the code is correctly located within its directory + structure, and compiled, the application can be deployed in + GlassFish. This is done in exactly the same way as described in + the preceding section, + Section 13.1, “A Simple JSP Application with GlassFish, Connector/J and MySQL”. +

+ Once deployed the application can be launched from within the + GlassFish Administration Console. Enter a country name such as + “England”, and the application will return + “Country does not exist!”. Enter + “France”, and the application will return a + population of 59225700. +

Chapter 14 Using Connector/J with MySQL Fabric

+ MySQL Fabric is a system for managing a farm of MySQL servers (and + other components). Fabric provides an extensible and easy to use + system for managing a MySQL deployment for sharding and + high-availability. +

+ For more information on MySQL Fabric, see + MySQL Fabric. For instructions on how to use + Connector/J with MySQL Fabric, see + Using Connector/J with MySQL Fabric. +

Chapter 15 Troubleshooting Connector/J Applications

+ This section explains the symptoms and resolutions for the most + commonly encountered issues with applications using MySQL + Connector/J. +

Questions

  • 15.1: + When I try to connect to the database with MySQL + Connector/J, I get the following exception: +

    +SQLException: Server configuration denies access to data source
    +SQLState: 08001
    +VendorError: 0
    +

    + What is going on? I can connect just fine with the MySQL + command-line client. +

  • 15.2: + My application throws an SQLException 'No Suitable Driver'. + Why is this happening? +

  • 15.3: + I'm trying to use MySQL Connector/J in an applet or + application and I get an exception similar to: +

    +SQLException: Cannot connect to MySQL server on host:3306.
    +Is there a MySQL server running on the machine/port you
    +are trying to connect to?
    +
    +(java.security.AccessControlException)
    +SQLState: 08S01
    +VendorError: 0 
    +
  • 15.4: + I have a servlet/application that works fine for a day, and + then stops working overnight +

  • 15.5: + I'm trying to use JDBC 2.0 updatable result sets, and I get + an exception saying my result set is not updatable. +

  • 15.6: + I cannot connect to the MySQL server using Connector/J, and + I'm sure the connection parameters are correct. +

  • 15.7: + I am trying to connect to my MySQL server within my + application, but I get the following error and stack trace: +

    +java.net.SocketException
    +MESSAGE: Software caused connection abort: recv failed
    +
    +STACKTRACE:
    +
    +java.net.SocketException: Software caused connection abort: recv failed
    +at java.net.SocketInputStream.socketRead0(Native Method)
    +at java.net.SocketInputStream.read(Unknown Source)
    +at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
    +at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
    +at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
    +at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
    +at com.mysql.jdbc.Connection.<init>(Connection.java:452)
    +at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)
    +
  • 15.8: + My application is deployed through JBoss and I am using + transactions to handle the statements on the MySQL database. + Under heavy loads, I am getting an error and stack trace, + but these only occur after a fixed period of heavy activity. +

  • 15.9: + When using gcj, a + java.io.CharConversionException exception + is raised when working with certain character sequences. +

  • 15.10: + Updating a table that contains a + primary key that is + either FLOAT or compound + primary key that uses FLOAT + fails to update the table and raises an exception. +

  • 15.11: + You get an + ER_NET_PACKET_TOO_LARGE + exception, even though the binary blob size you want to + insert using JDBC is safely below the + max_allowed_packet size. +

  • 15.12: + What should you do if you receive error messages similar to + the following: “Communications link failure – Last + packet sent to the server was X ms ago”? +

  • 15.13: + Why does Connector/J not reconnect to MySQL and re-issue the + statement after a communication failure, instead of throwing + an Exception, even though I use the + autoReconnect connection string option? +

  • 15.14: + How can I use 3-byte UTF8 with Connector/J? +

  • 15.15: + How can I use 4-byte UTF8, utf8mb4 with + Connector/J? +

  • 15.16: + Using useServerPrepStmts=false and + certain character encodings can lead to corruption when + inserting BLOBs. How can this be avoided? +

Questions and Answers

15.1: + When I try to connect to the database with MySQL + Connector/J, I get the following exception: +

+SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0
+

+ What is going on? I can connect just fine with the MySQL + command-line client. +

+ MySQL Connector/J must use TCP/IP sockets to connect to + MySQL, as Java does not support Unix Domain Sockets. + Therefore, when MySQL Connector/J connects to MySQL, the + security manager in MySQL server will use its grant tables + to determine whether the connection is permitted. +

+ You must add the necessary security credentials to the MySQL + server for this to happen, using the + GRANT statement to your MySQL + Server. See GRANT Syntax, for more information. +

Note

+ Testing your connectivity with the + mysql command-line client will not work + unless you add the "host" flag, and use something other + than localhost for the host. The + mysql command-line client will use Unix + domain sockets if you use the special host name + localhost. If you are testing + connectivity to localhost, use + 127.0.0.1 as the host name instead. +

Warning

+ Changing privileges and permissions improperly in MySQL + can potentially cause your server installation to not have + optimal security properties. +

15.2: + My application throws an SQLException 'No Suitable Driver'. + Why is this happening? +

+ There are three possible causes for this error: +

  • + The Connector/J driver is not in your + CLASSPATH, see + Chapter 3, Connector/J Installation. +

  • + The format of your connection URL is incorrect, or you + are referencing the wrong JDBC driver. +

  • + When using DriverManager, the + jdbc.drivers system property has not + been populated with the location of the Connector/J + driver. +

15.3: + I'm trying to use MySQL Connector/J in an applet or + application and I get an exception similar to: +

+SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0 
+

+ Either you're running an Applet, your MySQL server has been + installed with the "skip-networking" option set, or your + MySQL server has a firewall sitting in front of it. +

+ Applets can only make network connections back to the + machine that runs the web server that served the .class + files for the applet. This means that MySQL must run on the + same machine (or you must have some sort of port + re-direction) for this to work. This also means that you + will not be able to test applets from your local file + system, you must always deploy them to a web server. +

+ MySQL Connector/J can only communicate with MySQL using + TCP/IP, as Java does not support Unix domain sockets. TCP/IP + communication with MySQL might be affected if MySQL was + started with the "skip-networking" flag, or if it is + firewalled. +

+ If MySQL has been started with the "skip-networking" option + set (the Debian Linux package of MySQL server does this for + example), you need to comment it out in the file + /etc/mysql/my.cnf or + /etc/my.cnf. Of course your + my.cnf file might also exist in the + data directory of your MySQL server, or + anywhere else (depending on how MySQL was compiled for your + system). Binaries created by us always look in + /etc/my.cnf and + datadir/my.cnf. + If your MySQL server has been firewalled, you will need to + have the firewall configured to allow TCP/IP connections + from the host where your Java code is running to the MySQL + server on the port that MySQL is listening to (by default, + 3306). +

15.4: + I have a servlet/application that works fine for a day, and + then stops working overnight +

+ MySQL closes connections after 8 hours of inactivity. You + either need to use a connection pool that handles stale + connections or use the autoReconnect + parameter (see + Section 5.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J”). +

+ Also, catch SQLExceptions in your + application and deal with them, rather than propagating them + all the way until your application exits. This is just good + programming practice. MySQL Connector/J will set the + SQLState (see + java.sql.SQLException.getSQLState() in + your API docs) to 08S01 when it + encounters network-connectivity issues during the processing + of a query. Attempt to reconnect to MySQL at this point. +

+ The following (simplistic) example shows what code that can + handle these exceptions might look like: +

+

Example 15.1 Connector/J: Example of transaction with retry logic

+public void doBusinessOp() throws SQLException {
+    Connection conn = null;
+    Statement stmt = null;
+    ResultSet rs = null;
+
+    //
+    // How many times do you want to retry the transaction
+    // (or at least _getting_ a connection)?
+    //
+    int retryCount = 5;
+
+    boolean transactionCompleted = false;
+
+    do {
+        try {
+            conn = getConnection(); // assume getting this from a
+                                    // javax.sql.DataSource, or the
+                                    // java.sql.DriverManager
+
+            conn.setAutoCommit(false);
+
+            //
+            // Okay, at this point, the 'retry-ability' of the
+            // transaction really depends on your application logic,
+            // whether or not you're using autocommit (in this case
+            // not), and whether you're using transactional storage
+            // engines
+            //
+            // For this example, we'll assume that it's _not_ safe
+            // to retry the entire transaction, so we set retry
+            // count to 0 at this point
+            //
+            // If you were using exclusively transaction-safe tables,
+            // or your application could recover from a connection going
+            // bad in the middle of an operation, then you would not
+            // touch 'retryCount' here, and just let the loop repeat
+            // until retryCount == 0.
+            //
+            retryCount = 0;
+
+            stmt = conn.createStatement();
+
+            String query = "SELECT foo FROM bar ORDER BY baz";
+
+            rs = stmt.executeQuery(query);
+
+            while (rs.next()) {
+            }
+
+            rs.close();
+            rs = null;
+
+            stmt.close();
+            stmt = null;
+
+            conn.commit();
+            conn.close();
+            conn = null;
+
+            transactionCompleted = true;
+        } catch (SQLException sqlEx) {
+
+            //
+            // The two SQL states that are 'retry-able' are 08S01
+            // for a communications error, and 40001 for deadlock.
+            //
+            // Only retry if the error was due to a stale connection,
+            // communications problem or deadlock
+            //
+
+            String sqlState = sqlEx.getSQLState();
+
+            if ("08S01".equals(sqlState) || "40001".equals(sqlState)) {
+                retryCount -= 1;
+            } else {
+                retryCount = 0;
+            }
+        } finally {
+            if (rs != null) {
+                try {
+                    rs.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this...
+                }
+            }
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this as well...
+                }
+            }
+
+            if (conn != null) {
+                try {
+                    //
+                    // If we got here, and conn is not null, the
+                    // transaction should be rolled back, as not
+                    // all work has been done
+
+                    try {
+                        conn.rollback();
+                    } finally {
+                        conn.close();
+                    }
+                } catch (SQLException sqlEx) {
+                    //
+                    // If we got an exception here, something
+                    // pretty serious is going on, so we better
+                    // pass it up the stack, rather than just
+                    // logging it...
+
+                    throw sqlEx;
+                }
+            }
+        }
+    } while (!transactionCompleted && (retryCount > 0));
+}
+


+

Note

+ Use of the autoReconnect option is not + recommended because there is no safe method of + reconnecting to the MySQL server without risking some + corruption of the connection state or database state + information. Instead, use a connection pool, which will + enable your application to connect to the MySQL server + using an available connection from the pool. The + autoReconnect facility is deprecated, and + may be removed in a future release. +

15.5: + I'm trying to use JDBC 2.0 updatable result sets, and I get + an exception saying my result set is not updatable. +

+ Because MySQL does not have row identifiers, MySQL + Connector/J can only update result sets that have come from + queries on tables that have at least one + primary key, the + query must select every primary key column, and the query + can only span one table (that is, no joins). This is + outlined in the JDBC specification. +

+ Note that this issue only occurs when using updatable result + sets, and is caused because Connector/J is unable to + guarantee that it can identify the correct rows within the + result set to be updated without having a unique reference + to each row. There is no requirement to have a unique field + on a table if you are using + UPDATE or + DELETE statements on a table + where you can individually specify the criteria to be + matched using a WHERE clause. +

15.6: + I cannot connect to the MySQL server using Connector/J, and + I'm sure the connection parameters are correct. +

+ Make sure that the + skip-networking option has + not been enabled on your server. Connector/J must be able to + communicate with your server over TCP/IP; named sockets are + not supported. Also ensure that you are not filtering + connections through a firewall or other network security + system. For more information, see + Can't connect to [local] MySQL server. +

15.7: + I am trying to connect to my MySQL server within my + application, but I get the following error and stack trace: +

+java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
+
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv failed
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.<init>(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)
+

+ The error probably indicates that you are using a older + version of the Connector/J JDBC driver (2.0.14 or 3.0.x) and + you are trying to connect to a MySQL server with version + 4.1x or newer. The older drivers are not compatible with 4.1 + or newer of MySQL as they do not support the newer + authentication mechanisms. +

+ It is likely that the older version of the Connector/J + driver exists within your application directory or your + CLASSPATH includes the older Connector/J + package. +

15.8: + My application is deployed through JBoss and I am using + transactions to handle the statements on the MySQL database. + Under heavy loads, I am getting an error and stack trace, + but these only occur after a fixed period of heavy activity. +

+ This is a JBoss, not Connector/J, issue and is connected to + the use of transactions. Under heavy loads the time taken + for transactions to complete can increase, and the error is + caused because you have exceeded the predefined timeout. +

+ You can increase the timeout value by setting the + TransactionTimeout attribute to the + TransactionManagerService within the + /conf/jboss-service.xml file + (pre-4.0.3) or /deploy/jta-service.xml + for JBoss 4.0.3 or later. See + TransactionTimeout + within the JBoss wiki for more information. +

15.9: + When using gcj, a + java.io.CharConversionException exception + is raised when working with certain character sequences. +

+ This is a known issue with gcj which + raises an exception when it reaches an unknown character or + one it cannot convert. Add + useJvmCharsetConverters=true to your + connection string to force character conversion outside of + the gcj libraries, or try a different + JDK. +

15.10: + Updating a table that contains a + primary key that is + either FLOAT or compound + primary key that uses FLOAT + fails to update the table and raises an exception. +

+ Connector/J adds conditions to the WHERE + clause during an UPDATE to + check the old values of the primary key. If there is no + match, then Connector/J considers this a failure condition + and raises an exception. +

+ The problem is that rounding differences between supplied + values and the values stored in the database may mean that + the values never match, and hence the update fails. The + issue will affect all queries, not just those from + Connector/J. +

+ To prevent this issue, use a primary key that does not use + FLOAT. If you have to use a + floating point column in your primary key, use + DOUBLE or + DECIMAL types in place of + FLOAT. +

15.11: + You get an + ER_NET_PACKET_TOO_LARGE + exception, even though the binary blob size you want to + insert using JDBC is safely below the + max_allowed_packet size. +

+ This is because the hexEscapeBlock() + method in + com.mysql.jdbc.PreparedStatement.streamToBytes() + may almost double the size of your data. +

15.12: + What should you do if you receive error messages similar to + the following: “Communications link failure – Last + packet sent to the server was X ms ago”? +

+ Generally speaking, this error suggests that the network + connection has been closed. There can be several root + causes: +

  • + Firewalls or routers may clamp down on idle connections + (the MySQL client/server protocol does not ping). +

  • + The MySQL Server may be closing idle connections that + exceed the wait_timeout or + interactive_timeout threshold. +

+ To help troubleshoot these issues, the following tips can be + used. If a recent (5.1.13+) version of Connector/J is used, + you will see an improved level of information compared to + earlier versions. Older versions simply display the last + time a packet was sent to the server, which is frequently 0 + ms ago. This is of limited use, as it may be that a packet + was just sent, while a packet from the server has not been + received for several hours. Knowing the period of time since + Connector/J last received a packet from the server is useful + information, so if this is not displayed in your exception + message, it is recommended that you update Connector/J. +

+ Further, if the time a packet was last sent/received exceeds + the wait_timeout or + interactive_timeout threshold, this is + noted in the exception message. +

+ Although network connections can be volatile, the following + can be helpful in avoiding problems: +

  • + Ensure connections are valid when used from the + connection pool. Use a query that starts with + /* ping */ to execute a lightweight + ping instead of full query. Note, the syntax of the ping + needs to be exactly as specified here. +

  • + Minimize the duration a connection object is left idle + while other application logic is executed. +

  • + Explicitly validate the connection before using it if + the connection has been left idle for an extended period + of time. +

  • + Ensure that wait_timeout and + interactive_timeout are set + sufficiently high. +

  • + Ensure that tcpKeepalive is enabled. +

  • + Ensure that any configurable firewall or router timeout + settings allow for the maximum expected connection idle + time. +

Note

+ Do not expect to be able to reuse a connection without + problems, if it has being lying idle for a period. If a + connection is to be reused after being idle for any length + of time, ensure that you explicitly test it before reusing + it. +

15.13: + Why does Connector/J not reconnect to MySQL and re-issue the + statement after a communication failure, instead of throwing + an Exception, even though I use the + autoReconnect connection string option? +

+ There are several reasons for this. The first is + transactional integrity. The MySQL Reference Manual states + that “there is no safe method of reconnecting to the + MySQL server without risking some corruption of the + connection state or database state information”. + Consider the following series of statements for example: +

+
+conn.createStatement().execute(
+  "UPDATE checking_account SET balance = balance - 1000.00 WHERE customer='Smith'");
+conn.createStatement().execute(
+  "UPDATE savings_account SET balance = balance + 1000.00 WHERE customer='Smith'");
+conn.commit();
+
+

+ Consider the case where the connection to the server fails + after the UPDATE to + checking_account. If no exception is + thrown, and the application never learns about the problem, + it will continue executing. However, the server did not + commit the first transaction in this case, so that will get + rolled back. But execution continues with the next + transaction, and increases the + savings_account balance by 1000. The + application did not receive an exception, so it continued + regardless, eventually committing the second transaction, as + the commit only applies to the changes made in the new + connection. Rather than a transfer taking place, a deposit + was made in this example. +

+ Note that running with autocommit enabled + does not solve this problem. When Connector/J encounters a + communication problem, there is no means to determine + whether the server processed the currently executing + statement or not. The following theoretical states are + equally possible: +

  • + The server never received the statement, and therefore + no related processing occurred on the server. +

  • + The server received the statement, executed it in full, + but the response was not received by the client. +

+ If you are running with autocommit + enabled, it is not possible to guarantee the state of data + on the server when a communication exception is encountered. + The statement may have reached the server, or it may not. + All you know is that communication failed at some point, + before the client received confirmation (or data) from the + server. This does not only affect + autocommit statements though. If the + communication problem occurred during + Connection.commit(), the question arises + of whether the transaction was committed on the server + before the communication failed, or whether the server + received the commit request at all. +

+ The second reason for the generation of exceptions is that + transaction-scoped contextual data may be vulnerable, for + example: +

  • + Temporary tables. +

  • + User-defined variables. +

  • + Server-side prepared statements. +

+ These items are lost when a connection fails, and if the + connection silently reconnects without generating an + exception, this could be detrimental to the correct + execution of your application. +

+ In summary, communication errors generate conditions that + may well be unsafe for Connector/J to simply ignore by + silently reconnecting. It is necessary for the application + to be notified. It is then for the application developer to + decide how to proceed in the event of connection errors and + failures. +

15.14: + How can I use 3-byte UTF8 with Connector/J? +

+ To use 3-byte UTF8 with Connector/J set + characterEncoding=utf8 and set + useUnicode=true in the connection string. +

15.15: + How can I use 4-byte UTF8, utf8mb4 with + Connector/J? +

+ To use 4-byte UTF8 with Connector/J configure the MySQL + server with character_set_server=utf8mb4. + Connector/J will then use that setting as long as + characterEncoding has not been set in the + connection string. This is equivalent to autodetection of + the character set. +

15.16: + Using useServerPrepStmts=false and + certain character encodings can lead to corruption when + inserting BLOBs. How can this be avoided? +

+ When using certain character encodings, such as SJIS, CP932, + and BIG5, it is possible that BLOB data contains characters + that can be interpreted as control characters, for example, + backslash, '\'. This can lead to corrupted data when + inserting BLOBs into the database. There are two things that + need to be done to avoid this: +

  1. + Set the connection string option + useServerPrepStmts to + true. +

  2. + Set SQL_MODE to + NO_BACKSLASH_ESCAPES. +

Chapter 16 Known Issues and Limitations

+ The following are some known issues and limitations for MySQL + Connector/J: +

  • + When Connector/J retrieves timestamps for a daylight saving + time (DST) switch day using the + getTimeStamp() method on the result set, + some of the returned values might be wrong. The errors can be + avoided by using the following connection options when + connecting to a database: + +

    +        useTimezone=true
    +        useLegacyDatetimeCode=false
    +        serverTimezone=UTC
    +      

    +

Chapter 17 Connector/J Support

17.1 Connector/J Community Support

+ Oracle provides assistance to the user community by means of its + mailing lists. For Connector/J related issues, you can get help + from experienced users by using the MySQL and Java mailing list. + Archives and subscription information is available online at + http://lists.mysql.com/java. +

+ For information about subscribing to MySQL mailing lists or to + browse list archives, visit + http://lists.mysql.com/. See + MySQL Mailing Lists. +

+ Community support from experienced users is also available + through the JDBC + Forum. You may also find help from other users in the + other MySQL Forums, located at + http://forums.mysql.com. See + MySQL Community Support at the MySQL Forums. +

17.2 How to Report Connector/J Bugs or Problems

+ The normal place to report bugs is + http://bugs.mysql.com/, which is the address for + our bugs database. This database is public, and can be browsed + and searched by anyone. If you log in to the system, you will + also be able to enter new reports. +

+ If you find a sensitive security bug in MySQL Server, please let + us know immediately by sending an email message to + . Exception: Support + customers should report all problems, including security bugs, + to Oracle Support at http://support.oracle.com/. +

+ Writing a good bug report takes patience, but doing it right the + first time saves time both for us and for yourself. A good bug + report, containing a full test case for the bug, makes it very + likely that we will fix the bug in the next release. +

+ This section will help you write your report correctly so that + you do not waste your time doing things that may not help us + much or at all. +

+ If you have a repeatable bug report, please report it to the + bugs database at http://bugs.mysql.com/. Any bug + that we are able to repeat has a high chance of being fixed in + the next MySQL release. +

+ To report other problems, you can use one of the MySQL mailing + lists. +

+ Remember that it is possible for us to respond to a message + containing too much information, but not to one containing too + little. People often omit facts because they think they know the + cause of a problem and assume that some details do not matter. +

+ A good principle is this: If you are in doubt about stating + something, state it. It is faster and less troublesome to write + a couple more lines in your report than to wait longer for the + answer if we must ask you to provide information that was + missing from the initial report. +

+ The most common errors made in bug reports are (a) not including + the version number of Connector/J or MySQL used, and (b) not + fully describing the platform on which Connector/J is installed + (including the JVM version, and the platform type and version + number that MySQL itself is installed on). +

+ This is highly relevant information, and in 99 cases out of 100, + the bug report is useless without it. Very often we get + questions like, “Why doesn't this work for me?” + Then we find that the feature requested wasn't implemented in + that MySQL version, or that a bug described in a report has + already been fixed in newer MySQL versions. +

+ Sometimes the error is platform-dependent; in such cases, it is + next to impossible for us to fix anything without knowing the + operating system and the version number of the platform. +

+ If at all possible, create a repeatable, standalone testcase + that doesn't involve any third-party classes. +

+ To streamline this process, we ship a base class for testcases + with Connector/J, named + 'com.mysql.jdbc.util.BaseBugReport'. To + create a testcase for Connector/J using this class, create your + own class that inherits from + com.mysql.jdbc.util.BaseBugReport and + override the methods setUp(), + tearDown() and + runTest(). +

+ In the setUp() method, create code that + creates your tables, and populates them with any data needed to + demonstrate the bug. +

+ In the runTest() method, create code that + demonstrates the bug using the tables and data you created in + the setUp method. +

+ In the tearDown() method, drop any tables + you created in the setUp() method. +

+ In any of the above three methods, use one of the variants of + the getConnection() method to create a JDBC + connection to MySQL: +

  • + getConnection() - Provides a connection + to the JDBC URL specified in getUrl(). + If a connection already exists, that connection is returned, + otherwise a new connection is created. +

  • + getNewConnection() - Use this if you + need to get a new connection for your bug report (that is, + there is more than one connection involved). +

  • + getConnection(String url) - Returns a + connection using the given URL. +

  • + getConnection(String url, Properties + props) - Returns a connection using the given URL + and properties. +

+ If you need to use a JDBC URL that is different from + 'jdbc:mysql:///test', override the method + getUrl() as well. +

+ Use the assertTrue(boolean expression) and + assertTrue(String failureMessage, boolean + expression) methods to create conditions that must be + met in your testcase demonstrating the behavior you are + expecting (vs. the behavior you are observing, which is why you + are most likely filing a bug report). +

+ Finally, create a main() method that + creates a new instance of your testcase, and calls the + run method: +

+public static void main(String[] args) throws Exception {
+      new MyBugReport().run();
+ }
+

+ Once you have finished your testcase, and have verified that it + demonstrates the bug you are reporting, upload it with your bug + report to http://bugs.mysql.com/. +

diff --git a/mysql-connector-java-5.1.40/docs/connector-j.pdf b/mysql-connector-java-5.1.40/docs/connector-j.pdf new file mode 100644 index 0000000..6d7d20d Binary files /dev/null and b/mysql-connector-java-5.1.40/docs/connector-j.pdf differ diff --git a/mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar b/mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar new file mode 100644 index 0000000..60bef5c Binary files /dev/null and b/mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar differ diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricCommunicationException.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricCommunicationException.java new file mode 100644 index 0000000..064a212 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricCommunicationException.java @@ -0,0 +1,44 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * Indicates an exceptional condition while communicating with a Fabric server. + */ +public class FabricCommunicationException extends Exception { + + private static final long serialVersionUID = 1L; + + public FabricCommunicationException(Throwable cause) { + super(cause); + } + + public FabricCommunicationException(String message) { + super(message); + } + + public FabricCommunicationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricConnection.java new file mode 100644 index 0000000..e032f7e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricConnection.java @@ -0,0 +1,129 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import com.mysql.fabric.proto.xmlrpc.XmlRpcClient; + +public class FabricConnection { + private XmlRpcClient client; + + // internal caches + private Map shardMappingsByTableName = new HashMap(); + private Map serverGroupsByName = new HashMap(); + private long shardMappingsExpiration; + private int shardMappingsTtl; + private long serverGroupsExpiration; + private int serverGroupsTtl; + + public FabricConnection(String url, String username, String password) throws FabricCommunicationException { + this.client = new XmlRpcClient(url, username, password); + refreshState(); + } + + /** + * @param urls + * @param username + * @param password + * @throws FabricCommunicationException + */ + public FabricConnection(Set urls, String username, String password) throws FabricCommunicationException { + throw new UnsupportedOperationException("Multiple connections not supported."); + } + + public String getInstanceUuid() { + return null; + } + + public int getVersion() { + return 0; + } + + /** + * @return version of state data + */ + public int refreshState() throws FabricCommunicationException { + FabricStateResponse> serverGroups = this.client.getServerGroups(); + FabricStateResponse> shardMappings = this.client.getShardMappings(); + + this.serverGroupsExpiration = serverGroups.getExpireTimeMillis(); + this.serverGroupsTtl = serverGroups.getTtl(); + for (ServerGroup g : serverGroups.getData()) { + this.serverGroupsByName.put(g.getName(), g); + } + + this.shardMappingsExpiration = shardMappings.getExpireTimeMillis(); + this.shardMappingsTtl = shardMappings.getTtl(); + for (ShardMapping m : shardMappings.getData()) { + // a shard mapping may be associated with more than one table + for (ShardTable t : m.getShardTables()) { + this.shardMappingsByTableName.put(t.getDatabase() + "." + t.getTable(), m); + } + } + + return 0; + } + + public int refreshStatePassive() { + try { + return refreshState(); + } catch (FabricCommunicationException e) { + // Fabric node is down but we can operate on previous setup. Just reset the TTL timers. + this.serverGroupsExpiration = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(this.serverGroupsTtl); + this.shardMappingsExpiration = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(this.shardMappingsTtl); + } + + return 0; + } + + public ServerGroup getServerGroup(String serverGroupName) { + if (isStateExpired()) { + refreshStatePassive(); + } + return this.serverGroupsByName.get(serverGroupName); + } + + public ShardMapping getShardMapping(String database, String table) { + if (isStateExpired()) { + refreshStatePassive(); + } + return this.shardMappingsByTableName.get(database + "." + table); + } + + public boolean isStateExpired() { + return System.currentTimeMillis() > this.shardMappingsExpiration || System.currentTimeMillis() > this.serverGroupsExpiration; + } + + public Set getFabricHosts() { + return null; + } + + public XmlRpcClient getClient() { + return this.client; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricStateResponse.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricStateResponse.java new file mode 100644 index 0000000..86cf8a7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/FabricStateResponse.java @@ -0,0 +1,59 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.concurrent.TimeUnit; + +public class FabricStateResponse { + private T data; + private int secsTtl; + private long expireTimeMillis; + + public FabricStateResponse(T data, int secsTtl) { + this.data = data; + this.secsTtl = secsTtl; + this.expireTimeMillis = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(secsTtl); + } + + public FabricStateResponse(T data, int secsTtl, long presetExpireTimeMillis) { + this.data = data; + this.secsTtl = secsTtl; + this.expireTimeMillis = presetExpireTimeMillis; + } + + public T getData() { + return this.data; + } + + public int getTtl() { + return this.secsTtl; + } + + /** + * The expiration time of this data. Should be compared to {@link System.currentTimeMillis()}. + */ + public long getExpireTimeMillis() { + return this.expireTimeMillis; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/HashShardMapping.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/HashShardMapping.java new file mode 100644 index 0000000..6a6632c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/HashShardMapping.java @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; + +/** + * A shard mapping with ranges defined by hashing key values. Lookups work essentially + * the same as in {@link RangeShardMapping} but strings are compared as opposed to ints. + */ +public class HashShardMapping extends ShardMapping { + private static class ReverseShardIndexSorter implements Comparator { + public int compare(ShardIndex i1, ShardIndex i2) { + return i2.getBound().compareTo(i1.getBound()); + } + + // singleton instance + public static final ReverseShardIndexSorter instance = new ReverseShardIndexSorter(); + } + + private static final MessageDigest md5Hasher; + + static { + try { + md5Hasher = MessageDigest.getInstance("MD5"); + } catch (java.security.NoSuchAlgorithmException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + public HashShardMapping(int mappingId, ShardingType shardingType, String globalGroupName, Set shardTables, Set shardIndices) { + super(mappingId, shardingType, globalGroupName, shardTables, new TreeSet(ReverseShardIndexSorter.instance)); + this.shardIndices.addAll(shardIndices); + } + + @Override + protected ShardIndex getShardIndexForKey(String stringKey) { + String hashedKey = new BigInteger(/* unsigned/positive */1, md5Hasher.digest(stringKey.getBytes())).toString(16).toUpperCase(); + + // pad out to 32 digits + for (int i = 0; i < (32 - hashedKey.length()); ++i) { + hashedKey = "0" + hashedKey; + } + + for (ShardIndex i : this.shardIndices) { + if (i.getBound().compareTo(hashedKey) <= 0) { + return i; + } + } + + // default to the first (highest) bound, + // implementing wrapping + return this.shardIndices.iterator().next(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/RangeShardMapping.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/RangeShardMapping.java new file mode 100644 index 0000000..afaffa1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/RangeShardMapping.java @@ -0,0 +1,73 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.Comparator; +import java.util.Set; +import java.util.TreeSet; + +/** + * A shard mapping that partitions data by ranges. + */ +public class RangeShardMapping extends ShardMapping { + /** + * A sorter that sorts shard indices from highest to lowest based on the integer + * value of their bounds. For a range shard mapping, the bound is a lowest bound. + */ + private static class RangeShardIndexSorter implements Comparator { + public int compare(ShardIndex i1, ShardIndex i2) { + Integer bound1, bound2; + bound1 = Integer.parseInt(i1.getBound()); + bound2 = Integer.parseInt(i2.getBound()); + return bound2.compareTo(bound1); // this reverses it + } + + // singleton instance + public static final RangeShardIndexSorter instance = new RangeShardIndexSorter(); + } + + public RangeShardMapping(int mappingId, ShardingType shardingType, String globalGroupName, Set shardTables, Set shardIndices) { + // sort shard indices eagerly so {@link getShardIndexForKey} has them in the necessary order + super(mappingId, shardingType, globalGroupName, shardTables, new TreeSet(RangeShardIndexSorter.instance)); + this.shardIndices.addAll(shardIndices); + } + + /** + * Search through the shard indicies to find the shard holding this key. Range-based sharding + * defines a lower bound for each partition with the upper bound being one less than the lower bound + * of the next highest shard. There is no upper bound for the shard with the highest lower bound. + */ + @Override + protected ShardIndex getShardIndexForKey(String stringKey) { + Integer key = -1; + key = Integer.parseInt(stringKey); + for (ShardIndex i : this.shardIndices) { + Integer lowerBound = Integer.valueOf(i.getBound()); + if (key >= lowerBound) { + return i; + } + } + return null; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/Response.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/Response.java new file mode 100644 index 0000000..6bb2a58 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/Response.java @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.List; +import java.util.Map; + +import com.mysql.fabric.proto.xmlrpc.ResultSetParser; + +/** + * Response from Fabric request. + */ +public class Response { + private int protocolVersion; + private String fabricUuid; + private int ttl; + private String errorMessage; + private List> resultSet; + + @SuppressWarnings("unchecked") + public Response(List responseData) throws FabricCommunicationException { + // parser of protocol version 1 as defined by WL#7760 + this.protocolVersion = (Integer) responseData.get(0); + if (this.protocolVersion != 1) { + throw new FabricCommunicationException("Unknown protocol version: " + this.protocolVersion); + } + this.fabricUuid = (String) responseData.get(1); + this.ttl = (Integer) responseData.get(2); + this.errorMessage = (String) responseData.get(3); + if ("".equals(this.errorMessage)) { + this.errorMessage = null; + } + List> resultSets = (List>) responseData.get(4); + if (resultSets.size() > 0) { + Map resultData = resultSets.get(0); + this.resultSet = new ResultSetParser().parse((Map) resultData.get("info"), (List>) resultData.get("rows")); + } + } + + public int getProtocolVersion() { + return this.protocolVersion; + } + + public String getFabricUuid() { + return this.fabricUuid; + } + + public int getTtl() { + return this.ttl; + } + + public String getErrorMessage() { + return this.errorMessage; + } + + public List> getResultSet() { + return this.resultSet; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/Server.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/Server.java new file mode 100644 index 0000000..55bc28d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/Server.java @@ -0,0 +1,116 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * Database server, as represented by Fabric. + */ +public class Server implements Comparable { + private String groupName; + private String uuid; + private String hostname; + private int port; + private ServerMode mode; + private ServerRole role; + private double weight; + + public Server(String groupName, String uuid, String hostname, int port, ServerMode mode, ServerRole role, double weight) { + this.groupName = groupName; // may be null + this.uuid = uuid; + this.hostname = hostname; + this.port = port; + this.mode = mode; + this.role = role; + this.weight = weight; + assert (uuid != null && !"".equals(uuid)); + assert (hostname != null && !"".equals(hostname)); + assert (port > 0); + assert (mode != null); + assert (role != null); + assert (weight > 0.0); + } + + public String getGroupName() { + return this.groupName; + } + + public String getUuid() { + return this.uuid; + } + + public String getHostname() { + return this.hostname; + } + + public int getPort() { + return this.port; + } + + public ServerMode getMode() { + return this.mode; + } + + public ServerRole getRole() { + return this.role; + } + + public double getWeight() { + return this.weight; + } + + public String getHostPortString() { + return this.hostname + ":" + this.port; + } + + public boolean isMaster() { + return this.role == ServerRole.PRIMARY; + } + + public boolean isSlave() { + return this.role == ServerRole.SECONDARY || this.role == ServerRole.SPARE; + } + + @Override + public String toString() { + return String.format("Server[%s, %s:%d, %s, %s, weight=%s]", this.uuid, this.hostname, this.port, this.mode, this.role, this.weight); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Server)) { + return false; + } + Server s = (Server) o; + return s.getUuid().equals(getUuid()); + } + + @Override + public int hashCode() { + return getUuid().hashCode(); + } + + public int compareTo(Server other) { + return getUuid().compareTo(other.getUuid()); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerGroup.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerGroup.java new file mode 100644 index 0000000..1668232 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerGroup.java @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.Set; + +/** + * Server Group - a set of servers responsible for the same set of data + */ +public class ServerGroup { + private String name; + private Set servers; + + public ServerGroup(String name, Set servers) { + this.name = name; + this.servers = servers; + } + + public String getName() { + return this.name; + } + + public Set getServers() { + return this.servers; + } + + /** + * Find the master server for this group. + * + * @return the master server, or null if there's no master for the current group state + */ + public Server getMaster() { + for (Server s : this.servers) { + if (s.getRole() == ServerRole.PRIMARY) { + return s; + } + } + return null; + } + + /** + * Lookup a server in this group for the matching host:port string. + * + * @return the server, if found. null otherwise + */ + public Server getServer(String hostPortString) { + for (Server s : this.servers) { + if (s.getHostPortString().equals(hostPortString)) { + return s; + } + } + return null; + } + + @Override + public String toString() { + return String.format("Group[name=%s, servers=%s]", this.name, this.servers); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerMode.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerMode.java new file mode 100644 index 0000000..4e330e5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerMode.java @@ -0,0 +1,36 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * Server modes. + * From: lib/mysql/fabric/server.py #SERVER MODE CONSTANTS and connector-semantics.pdf + */ +public enum ServerMode { + OFFLINE, READ_ONLY, WRITE_ONLY, READ_WRITE; + + public static ServerMode getFromConstant(Integer constant) { + return values()[constant]; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerRole.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerRole.java new file mode 100644 index 0000000..c3c94d5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ServerRole.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * Server roles. + */ +public enum ServerRole { + FAULTY, SPARE, SECONDARY, PRIMARY, CONFIGURING; + + public static ServerRole getFromConstant(Integer constant) { + return values()[constant]; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardIndex.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardIndex.java new file mode 100644 index 0000000..a3843fa --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardIndex.java @@ -0,0 +1,62 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * A shard index represents the physical location of a segment of data. The data segment + * is identified by it's key's relation to the `bound' value. + */ +public class ShardIndex { + private String bound; + private Integer shardId; + private String groupName; + + public ShardIndex(String bound, Integer shardId, String groupName) { + this.bound = bound; + this.shardId = shardId; + this.groupName = groupName; + } + + /** + * The bound that the key will be compared to. This is treated different based on the + * ShardingType. + */ + public String getBound() { + return this.bound; + } + + /** + * A unique identified for this shard. + */ + public Integer getShardId() { + return this.shardId; + } + + /** + * The name of the group in the data for this shard resides. + */ + public String getGroupName() { + return this.groupName; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMapping.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMapping.java new file mode 100644 index 0000000..bbe6a49 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMapping.java @@ -0,0 +1,93 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.Collections; +import java.util.Set; + +/** + * A shard mapping representing this set of sharded data. + */ +public abstract class ShardMapping { + private int mappingId; + private ShardingType shardingType; + private String globalGroupName; + protected Set shardTables; + protected Set shardIndices; + + public ShardMapping(int mappingId, ShardingType shardingType, String globalGroupName, Set shardTables, Set shardIndices) { + this.mappingId = mappingId; + this.shardingType = shardingType; + this.globalGroupName = globalGroupName; + this.shardTables = shardTables; + this.shardIndices = shardIndices; + } + + /** + * Lookup the server group that stores the given key. + */ + public String getGroupNameForKey(String key) { + return getShardIndexForKey(key).getGroupName(); + } + + /** + * Decide which shard index stores the given key. + */ + protected abstract ShardIndex getShardIndexForKey(String key); + + /** + * The ID of this mapping. + */ + public int getMappingId() { + return this.mappingId; + } + + /** + * The {@link ShardingType} of this mapping. + */ + public ShardingType getShardingType() { + return this.shardingType; + } + + /** + * The name of the global group for this shard map. + */ + public String getGlobalGroupName() { + return this.globalGroupName; + } + + /** + * Return the set of tables sharded in this mapping. + */ + public Set getShardTables() { + return Collections.unmodifiableSet(this.shardTables); + } + + /** + * Return the set of shards in this mapping. + */ + public Set getShardIndices() { + return Collections.unmodifiableSet(this.shardIndices); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMappingFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMappingFactory.java new file mode 100644 index 0000000..b63f994 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardMappingFactory.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +import java.util.Set; + +/** + * Factory for creating {@link ShardMapping} objects. This is generally based on the {@link ShardingType} but can be modified as necessary. + */ +public class ShardMappingFactory { + public ShardMapping createShardMapping(int mappingId, ShardingType shardingType, String globalGroupName, Set shardTables, + Set shardIndices) { + ShardMapping sm = null; + switch (shardingType) { + case RANGE: + sm = new RangeShardMapping(mappingId, shardingType, globalGroupName, shardTables, shardIndices); + break; + case HASH: + sm = new HashShardMapping(mappingId, shardingType, globalGroupName, shardTables, shardIndices); + break; + default: + throw new IllegalArgumentException("Invalid ShardingType"); + } + return sm; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardTable.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardTable.java new file mode 100644 index 0000000..a5d010b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardTable.java @@ -0,0 +1,60 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * A shard "table" is a description of how a specific table is distributed across shards. + */ +public class ShardTable { + private String database; + private String table; + private String column; + + public ShardTable(String database, String table, String column) { + this.database = database; + this.table = table; + this.column = column; + } + + /** + * The database in which the sharded data is contained in. + */ + public String getDatabase() { + return this.database; + } + + /** + * The table in which the sharded data is contained in. + */ + public String getTable() { + return this.table; + } + + /** + * The column whose value is used to differentiate between different physical shards. + */ + public String getColumn() { + return this.column; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardingType.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardingType.java new file mode 100644 index 0000000..18a7c29 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/ShardingType.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric; + +/** + * Sharding type. The algorithm by which rows are distributed across servers. + * + * @see HashShardMapping + * @see RangeShardMapping + * @see ListShardMapping + */ +public enum ShardingType { + LIST, RANGE, HASH; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/hibernate/FabricMultiTenantConnectionProvider.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/hibernate/FabricMultiTenantConnectionProvider.java new file mode 100644 index 0000000..bbd67b7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/hibernate/FabricMultiTenantConnectionProvider.java @@ -0,0 +1,142 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.hibernate; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.FabricConnection; +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; +import com.mysql.fabric.ServerMode; +import com.mysql.fabric.ShardMapping; + +/** + * Multi-tenancy connection provider for Hibernate 4. + * + * http://docs.jboss.org/hibernate/orm/4.1/javadocs/org/hibernate/service/jdbc/connections/spi/MultiTenantConnectionProvider.html + */ +public class FabricMultiTenantConnectionProvider implements MultiTenantConnectionProvider { + + private static final long serialVersionUID = 1L; + + private FabricConnection fabricConnection; + private String database; + private String table; // the sharded table + private String user; + private String password; + private ShardMapping shardMapping; + private ServerGroup globalGroup; + + public FabricMultiTenantConnectionProvider(String fabricUrl, String database, String table, String user, String password, String fabricUser, + String fabricPassword) { + try { + this.fabricConnection = new FabricConnection(fabricUrl, fabricUser, fabricPassword); + this.database = database; + this.table = table; + this.user = user; + this.password = password; + this.shardMapping = this.fabricConnection.getShardMapping(this.database, this.table); + this.globalGroup = this.fabricConnection.getServerGroup(this.shardMapping.getGlobalGroupName()); + } catch (FabricCommunicationException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Find a server with mode READ_WRITE in the given server group and create a JDBC connection to it. + * + * @returns a {@link Connection} to an arbitrary MySQL server + * @throws SQLException + * if connection fails or a READ_WRITE server is not contained in the group + */ + private Connection getReadWriteConnectionFromServerGroup(ServerGroup serverGroup) throws SQLException { + for (Server s : serverGroup.getServers()) { + if (ServerMode.READ_WRITE.equals(s.getMode())) { + String jdbcUrl = String.format("jdbc:mysql://%s:%s/%s", s.getHostname(), s.getPort(), this.database); + return DriverManager.getConnection(jdbcUrl, this.user, this.password); + } + } + throw new SQLException("Unable to find r/w server for chosen shard mapping in group " + serverGroup.getName()); + } + + /** + * Get a connection that be used to access data or metadata not specific to any shard/tenant. + * The returned connection is a READ_WRITE connection to the global group of the shard mapping + * for the database and table association with this connection provider. + */ + public Connection getAnyConnection() throws SQLException { + return getReadWriteConnectionFromServerGroup(this.globalGroup); + } + + /** + * Get a connection to access data association with the provided `tenantIdentifier' (or shard + * key in Fabric-speak). The returned connection is a READ_WRITE connection. + */ + public Connection getConnection(String tenantIdentifier) throws SQLException { + String serverGroupName = this.shardMapping.getGroupNameForKey(tenantIdentifier); + ServerGroup serverGroup = this.fabricConnection.getServerGroup(serverGroupName); + return getReadWriteConnectionFromServerGroup(serverGroup); + } + + /** + * Release a non-shard-specific connection. + */ + public void releaseAnyConnection(Connection connection) throws SQLException { + try { + connection.close(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * Release a connection specific to `tenantIdentifier'. + */ + public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { + releaseAnyConnection(connection); + } + + /** + * We don't track connections. + * + * @returns false + */ + public boolean supportsAggressiveRelease() { + return false; + } + + @SuppressWarnings("rawtypes") + public boolean isUnwrappableAs(Class unwrapType) { + return false; + } + + public T unwrap(Class unwrapType) { + return null; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/ErrorReportingExceptionInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/ErrorReportingExceptionInterceptor.java new file mode 100644 index 0000000..4c0a702 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/ErrorReportingExceptionInterceptor.java @@ -0,0 +1,71 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.SQLError; + +/** + * Relay the exception to {@link FabricMySQLConnectionProxy} for error reporting. This class exists solely because extensions cannot be provided with instances + * but instead require the connection to instantiate the provided class. + */ +public class ErrorReportingExceptionInterceptor implements ExceptionInterceptor { + private String hostname; + private String port; + private String fabricHaGroup; + + public SQLException interceptException(SQLException sqlEx, Connection conn) { + MySQLConnection mysqlConn = (MySQLConnection) conn; + + // don't intercept exceptions during initialization, before the proxy has a chance to setProxy() on the physical connection + if (ConnectionImpl.class.isAssignableFrom(mysqlConn.getMultiHostSafeProxy().getClass())) { + return null; + } + + FabricMySQLConnectionProxy fabricProxy = (FabricMySQLConnectionProxy) mysqlConn.getMultiHostSafeProxy(); + try { + return fabricProxy.interceptException(sqlEx, conn, this.fabricHaGroup, this.hostname, this.port); + } catch (FabricCommunicationException ex) { + return SQLError.createSQLException("Failed to report error to Fabric.", SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, ex, null); + } + } + + public void init(Connection conn, Properties props) throws SQLException { + this.hostname = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + this.port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + String connectionAttributes = props.getProperty("connectionAttributes"); + this.fabricHaGroup = connectionAttributes.replaceAll("^.*\\bfabricHaGroup:(.+)\\b.*$", "$1"); + } + + public void destroy() { + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnection.java new file mode 100644 index 0000000..cb443ac --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnection.java @@ -0,0 +1,99 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.SQLException; +import java.util.Set; + +import com.mysql.fabric.ServerGroup; + +public interface FabricMySQLConnection extends com.mysql.jdbc.MySQLConnection { + /** + * Clear all the state that is used to determine which server to + * send queries to. + */ + void clearServerSelectionCriteria() throws SQLException; + + /** + * Set the shard key for the data being accessed. + */ + void setShardKey(String shardKey) throws SQLException; + + /** + * Get the shard key for the data being accessed. + */ + String getShardKey(); + + /** + * Set the table being accessed. Can be a table name or a + * "database.table" pair. The table must be known by Fabric + * as a sharded table. + */ + void setShardTable(String shardTable) throws SQLException; + + /** + * Get the table being accessed. + */ + String getShardTable(); + + /** + * Set the server group name to connect to. Direct server group selection + * is mutually exclusive of sharded data access. + */ + void setServerGroupName(String serverGroupName) throws SQLException; + + /** + * Get the server group name when using direct server group selection. + */ + String getServerGroupName(); + + /** + * Get the current server group. + * + * @returns The currently chosen group if sufficient server group selection + * criteria has been provided. Otherwise null. + */ + ServerGroup getCurrentServerGroup(); + + /** + * Clear the list of tables for the last query. This also clears the + * shard mapping/table and must be given again for the next query via {@link setShardTable} or {@addQueryTable}. + */ + void clearQueryTables() throws SQLException; + + /** + * Add a table to the set of tables used for the next query on this connection. + * This is used for: + *
    + *
  • Choosing a shard given the tables used
  • + *
  • Preventing cross-shard queries
  • + *
+ */ + void addQueryTable(String tableName) throws SQLException; + + /** + * The set of tables to be used in the next query on this connection. + */ + Set getQueryTables(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProperties.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProperties.java new file mode 100644 index 0000000..39fa5a1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProperties.java @@ -0,0 +1,59 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import com.mysql.jdbc.ConnectionProperties; + +/** + * Additional properties for {@link FabricMySQLConnection}s. + */ +public interface FabricMySQLConnectionProperties extends ConnectionProperties { + void setFabricShardKey(String value); + + String getFabricShardKey(); + + void setFabricShardTable(String value); + + String getFabricShardTable(); + + void setFabricServerGroup(String value); + + String getFabricServerGroup(); + + void setFabricProtocol(String value); + + String getFabricProtocol(); + + void setFabricUsername(String value); + + String getFabricUsername(); + + void setFabricPassword(String value); + + String getFabricPassword(); + + void setFabricReportErrors(boolean value); + + boolean getFabricReportErrors(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java new file mode 100644 index 0000000..f613df0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLConnectionProxy.java @@ -0,0 +1,3053 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Savepoint; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TimeZone; +import java.util.Timer; +import java.util.concurrent.Executor; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.FabricConnection; +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; +import com.mysql.fabric.ShardMapping; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.CachedResultSetMetaData; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.ConnectionPropertiesImpl; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.Extension; +import com.mysql.jdbc.Field; +import com.mysql.jdbc.LoadBalancedConnectionProxy; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlIO; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ReplicationConnection; +import com.mysql.jdbc.ReplicationConnectionGroup; +import com.mysql.jdbc.ReplicationConnectionGroupManager; +import com.mysql.jdbc.ReplicationConnectionProxy; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.ServerPreparedStatement; +import com.mysql.jdbc.SingleByteCharsetConverter; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.StatementInterceptorV2; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException; +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.log.LogFactory; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +/** + * A proxy to a set of MySQL servers managed by MySQL Fabric. + * + * Limitations: + *
    + *
  • One shard key can be specified
  • + *
+ */ +public class FabricMySQLConnectionProxy extends ConnectionPropertiesImpl implements FabricMySQLConnection, FabricMySQLConnectionProperties { + + private static final long serialVersionUID = 5845485979107347258L; + + private Log log; + + protected FabricConnection fabricConnection; + + protected boolean closed = false; + + protected boolean transactionInProgress = false; + + // Set of connections created for this proxy (initialized lazily) + protected Map serverConnections = new HashMap(); + + // Connection currently in use for this proxy + protected ReplicationConnection currentConnection; + + // Server selection criteria + // one for group selection (i.e. sharding), + // one for server selection (i.e. RO, global, load balancing, etc) + protected String shardKey; + protected String shardTable; + protected String serverGroupName; + + protected Set queryTables = new HashSet(); + + protected ServerGroup serverGroup; + + protected String host; + protected String port; + protected String username; + protected String password; + protected String database; + + protected ShardMapping shardMapping; + + protected boolean readOnly = false; + protected boolean autoCommit = true; + protected int transactionIsolation = Connection.TRANSACTION_REPEATABLE_READ; + + private String fabricShardKey; + private String fabricShardTable; + private String fabricServerGroup; + private String fabricProtocol; + private String fabricUsername; + private String fabricPassword; + private boolean reportErrors = false; + + // Synchronized Set that holds temporary "locks" on ReplicationConnectionGroups being synced. + // These locks are used to prevent simultaneous syncing of the state of the current group's servers. + private static final Set replConnGroupLocks = Collections.synchronizedSet(new HashSet()); + + private static final Class JDBC4_NON_TRANSIENT_CONN_EXCEPTION; + static { + Class clazz = null; + try { + if (Util.isJdbc4()) { + clazz = Class.forName("com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException"); + } + } catch (ClassNotFoundException e) { + // no-op + } + JDBC4_NON_TRANSIENT_CONN_EXCEPTION = clazz; + } + + public FabricMySQLConnectionProxy(Properties props) throws SQLException { + // first, handle and remove Fabric-specific properties. once fabricShardKey et al are ConnectionProperty instances this will be unnecessary + this.fabricShardKey = props.getProperty(FabricMySQLDriver.FABRIC_SHARD_KEY_PROPERTY_KEY); + this.fabricShardTable = props.getProperty(FabricMySQLDriver.FABRIC_SHARD_TABLE_PROPERTY_KEY); + this.fabricServerGroup = props.getProperty(FabricMySQLDriver.FABRIC_SERVER_GROUP_PROPERTY_KEY); + this.fabricProtocol = props.getProperty(FabricMySQLDriver.FABRIC_PROTOCOL_PROPERTY_KEY); + this.fabricUsername = props.getProperty(FabricMySQLDriver.FABRIC_USERNAME_PROPERTY_KEY); + this.fabricPassword = props.getProperty(FabricMySQLDriver.FABRIC_PASSWORD_PROPERTY_KEY); + this.reportErrors = Boolean.valueOf(props.getProperty(FabricMySQLDriver.FABRIC_REPORT_ERRORS_PROPERTY_KEY)); + props.remove(FabricMySQLDriver.FABRIC_SHARD_KEY_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_SHARD_TABLE_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_SERVER_GROUP_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_PROTOCOL_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_USERNAME_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_PASSWORD_PROPERTY_KEY); + props.remove(FabricMySQLDriver.FABRIC_REPORT_ERRORS_PROPERTY_KEY); + + this.host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + this.port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + this.username = props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + this.password = props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + this.database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (this.username == null) { + this.username = ""; + } + if (this.password == null) { + this.password = ""; + } + + // add our interceptor to pass exceptions back to the `interceptException' method + String exceptionInterceptors = props.getProperty("exceptionInterceptors"); + if (exceptionInterceptors == null || "null".equals("exceptionInterceptors")) { + exceptionInterceptors = ""; + } else { + exceptionInterceptors += ","; + } + exceptionInterceptors += "com.mysql.fabric.jdbc.ErrorReportingExceptionInterceptor"; + props.setProperty("exceptionInterceptors", exceptionInterceptors); + + initializeProperties(props); + + // validation check of properties + if (this.fabricServerGroup != null && this.fabricShardTable != null) { + throw SQLError.createSQLException("Server group and shard table are mutually exclusive. Only one may be provided.", + SQLError.SQL_STATE_CONNECTION_REJECTED, null, getExceptionInterceptor(), this); + } + + try { + String url = this.fabricProtocol + "://" + this.host + ":" + this.port; + this.fabricConnection = new FabricConnection(url, this.fabricUsername, this.fabricPassword); + } catch (FabricCommunicationException ex) { + throw SQLError.createSQLException("Unable to establish connection to the Fabric server", SQLError.SQL_STATE_CONNECTION_REJECTED, ex, + getExceptionInterceptor(), this); + } + + // initialize log before any further calls that might actually use it + this.log = LogFactory.getLogger(getLogger(), "FabricMySQLConnectionProxy", null); + + setShardTable(this.fabricShardTable); + setShardKey(this.fabricShardKey); + + setServerGroupName(this.fabricServerGroup); + } + + /** + * Deal with an exception thrown on an underlying connection. We only consider connection exceptions (SQL State 08xxx). We internally handle a possible + * failover situation. + * + * @param sqlEx + * @param conn + * @param group + * @param hostname + * @param portNumber + * @throws FabricCommunicationException + */ + synchronized SQLException interceptException(SQLException sqlEx, Connection conn, String groupName, String hostname, String portNumber) + throws FabricCommunicationException { + // we are only concerned with connection failures to MySQL servers, skip anything else including connection failures to Fabric + if ((sqlEx.getSQLState() == null || !sqlEx.getSQLState().startsWith("08")) + && !MySQLNonTransientConnectionException.class.isAssignableFrom(sqlEx.getClass()) + && (JDBC4_NON_TRANSIENT_CONN_EXCEPTION == null || !JDBC4_NON_TRANSIENT_CONN_EXCEPTION.isAssignableFrom(sqlEx.getClass())) + || sqlEx.getCause() != null && FabricCommunicationException.class.isAssignableFrom(sqlEx.getCause().getClass())) { + return null; + } + + // find the Server corresponding to this connection + Server currentServer = this.serverGroup.getServer(hostname + ":" + portNumber); + + // we have already failed over or dealt with this connection, let the exception propagate + if (currentServer == null) { + return null; + } + + // report error (if necessary) + if (this.reportErrors) { + this.fabricConnection.getClient().reportServerError(currentServer, sqlEx.toString(), true); + } + + // no need for concurrent threads to duplicate this work + if (replConnGroupLocks.add(this.serverGroup.getName())) { + try { + // refresh group status. (after reporting the error. error reporting may trigger a failover) + try { + this.fabricConnection.refreshStatePassive(); + setCurrentServerGroup(this.serverGroup.getName()); + } catch (SQLException ex) { + return SQLError.createSQLException("Unable to refresh Fabric state. Failover impossible", SQLError.SQL_STATE_CONNECTION_FAILURE, ex, null); + } + + // propagate to repl conn group + try { + syncGroupServersToReplicationConnectionGroup(ReplicationConnectionGroupManager.getConnectionGroup(groupName)); + } catch (SQLException ex) { + return ex; + } + } finally { + replConnGroupLocks.remove(this.serverGroup.getName()); + } + } else { + return SQLError.createSQLException("Fabric state syncing already in progress in another thread.", SQLError.SQL_STATE_CONNECTION_FAILURE, sqlEx, + null); + } + return null; + } + + /** + * Refresh the client Fabric state cache if the TTL has expired. + */ + private void refreshStateIfNecessary() throws SQLException { + if (this.fabricConnection.isStateExpired()) { + this.fabricConnection.refreshStatePassive(); + if (this.serverGroup != null) { + setCurrentServerGroup(this.serverGroup.getName()); + } + } + } + + ///////////////////////////////////////// + // Server selection criteria and logic // + ///////////////////////////////////////// + public void setShardKey(String shardKey) throws SQLException { + ensureNoTransactionInProgress(); + + this.currentConnection = null; + + if (shardKey != null) { + if (this.serverGroupName != null) { + throw SQLError.createSQLException("Shard key cannot be provided when server group is chosen directly.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + null, getExceptionInterceptor(), this); + } else if (this.shardTable == null) { + throw SQLError.createSQLException("Shard key cannot be provided without a shard table.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null, + getExceptionInterceptor(), this); + } + + // sharded group selection + setCurrentServerGroup(this.shardMapping.getGroupNameForKey(shardKey)); + } else if (this.shardTable != null) { + setCurrentServerGroup(this.shardMapping.getGlobalGroupName()); + } + this.shardKey = shardKey; + } + + public String getShardKey() { + return this.shardKey; + } + + public void setShardTable(String shardTable) throws SQLException { + ensureNoTransactionInProgress(); + + this.currentConnection = null; + + if (this.serverGroupName != null) { + throw SQLError.createSQLException("Server group and shard table are mutually exclusive. Only one may be provided.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null, getExceptionInterceptor(), this); + } + + this.shardKey = null; + this.serverGroup = null; + this.shardTable = shardTable; + if (shardTable == null) { + this.shardMapping = null; + } else { + // lookup shard mapping + String table = shardTable; + String db = this.database; + if (shardTable.contains(".")) { + String pair[] = shardTable.split("\\."); + table = pair[0]; + db = pair[1]; + } + this.shardMapping = this.fabricConnection.getShardMapping(db, table); + if (this.shardMapping == null) { + throw SQLError.createSQLException("Shard mapping not found for table `" + shardTable + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null, + getExceptionInterceptor(), this); + } + // default to global group + setCurrentServerGroup(this.shardMapping.getGlobalGroupName()); + } + } + + public String getShardTable() { + return this.shardTable; + } + + public void setServerGroupName(String serverGroupName) throws SQLException { + ensureNoTransactionInProgress(); + + this.currentConnection = null; + + // direct group selection + if (serverGroupName != null) { + setCurrentServerGroup(serverGroupName); + } + + this.serverGroupName = serverGroupName; + } + + public String getServerGroupName() { + return this.serverGroupName; + } + + public void clearServerSelectionCriteria() throws SQLException { + ensureNoTransactionInProgress(); + this.shardTable = null; + this.shardKey = null; + this.serverGroupName = null; + this.serverGroup = null; + this.queryTables.clear(); + this.currentConnection = null; + } + + public ServerGroup getCurrentServerGroup() { + return this.serverGroup; + } + + public void clearQueryTables() throws SQLException { + ensureNoTransactionInProgress(); + + this.currentConnection = null; + + this.queryTables.clear(); + setShardTable(null); + } + + /** + * Add a table to the set of tables used for the next query on this connection. + * This is used for: + *
    + *
  • Choosing a shard given the tables used
  • + *
  • Preventing cross-shard queries
  • + *
+ */ + public void addQueryTable(String tableName) throws SQLException { + ensureNoTransactionInProgress(); + + this.currentConnection = null; + + // choose shard mapping if necessary + if (this.shardMapping == null) { + if (this.fabricConnection.getShardMapping(this.database, tableName) != null) { + setShardTable(tableName); + } + } else { // make sure we aren't in conflict with the chosen shard mapping + ShardMapping mappingForTableName = this.fabricConnection.getShardMapping(this.database, tableName); + if (mappingForTableName != null && !mappingForTableName.equals(this.shardMapping)) { + throw SQLError.createSQLException("Cross-shard query not allowed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null, getExceptionInterceptor(), this); + } + } + this.queryTables.add(tableName); + } + + /** + * The set of tables to be used in the next query on this connection. + */ + public Set getQueryTables() { + return this.queryTables; + } + + /** + * Change the server group to the given named group. + */ + protected void setCurrentServerGroup(String serverGroupName) throws SQLException { + this.serverGroup = this.fabricConnection.getServerGroup(serverGroupName); + + if (this.serverGroup == null) { + throw SQLError.createSQLException("Cannot find server group: `" + serverGroupName + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null, + getExceptionInterceptor(), this); + } + + // check for any changes that need to be propagated to the entire group + ReplicationConnectionGroup replConnGroup = ReplicationConnectionGroupManager.getConnectionGroup(serverGroupName); + if (replConnGroup != null) { + if (replConnGroupLocks.add(this.serverGroup.getName())) { + try { + syncGroupServersToReplicationConnectionGroup(replConnGroup); + } finally { + replConnGroupLocks.remove(this.serverGroup.getName()); + } + } + } + } + + ////////////////////////////////////////////////////// + // Methods dealing with state internal to the proxy // + ////////////////////////////////////////////////////// + /** + * Get the active connection as an object implementing the + * internal MySQLConnection interface. This should not be used + * unless a MySQLConnection is required. + * + * {@link getActiveConnection()} is provided for the general case. + * The returned object is not a {@link ReplicationConnection}, but + * instead the {@link LoadBalancedConnectionProxy} for either the + * master or slaves. + */ + protected MySQLConnection getActiveMySQLConnection() throws SQLException { + ReplicationConnection c = (ReplicationConnection) getActiveConnection(); + MySQLConnection mc = (MySQLConnection) c.getCurrentConnection(); + return mc; + } + + protected MySQLConnection getActiveMySQLConnectionPassive() { + try { + return getActiveMySQLConnection(); + } catch (SQLException ex) { + throw new IllegalStateException("Unable to determine active connection", ex); + } + } + + protected Connection getActiveConnectionPassive() { + try { + return getActiveConnection(); + } catch (SQLException ex) { + throw new IllegalStateException("Unable to determine active connection", ex); + } + } + + /** + * Sync the state of the current group's servers to that of the given replication connection group. This is necessary as: + *
    + *
  • New connections have updated state from the Fabric server
  • + *
  • Failover scenarios may update state and it should be propagated across the active connections
  • + *
+ */ + private void syncGroupServersToReplicationConnectionGroup(ReplicationConnectionGroup replConnGroup) throws SQLException { + String currentMasterString = null; + if (replConnGroup.getMasterHosts().size() == 1) { + currentMasterString = replConnGroup.getMasterHosts().iterator().next(); + } + // check if master has changed + if (currentMasterString != null + && (this.serverGroup.getMaster() == null || !currentMasterString.equals(this.serverGroup.getMaster().getHostPortString()))) { + // old master is gone (there may be a new one) (closeGently=false) + try { + replConnGroup.removeMasterHost(currentMasterString, false); + } catch (SQLException ex) { + // effectively ignored + getLog().logWarn("Unable to remove master: " + currentMasterString, ex); + } + } + + // add new master (if exists and the old master was absent has been removed) + Server newMaster = this.serverGroup.getMaster(); + if (newMaster != null && replConnGroup.getMasterHosts().size() == 0) { + getLog().logInfo("Changing master for group '" + replConnGroup.getGroupName() + "' to: " + newMaster); + try { + if (!replConnGroup.getSlaveHosts().contains(newMaster.getHostPortString())) { + replConnGroup.addSlaveHost(newMaster.getHostPortString()); + } + replConnGroup.promoteSlaveToMaster(newMaster.getHostPortString()); + } catch (SQLException ex) { + throw SQLError.createSQLException("Unable to promote new master '" + newMaster.toString() + "'", ex.getSQLState(), ex, null); + } + } + + // synchronize HA group state with replication connection group in two steps: + // 1. add any new slaves to the connection group + for (Server s : this.serverGroup.getServers()) { + if (s.isSlave()) { + // this is a no-op if the slave is already present + try { + replConnGroup.addSlaveHost(s.getHostPortString()); + } catch (SQLException ex) { + // effectively ignored + getLog().logWarn("Unable to add slave: " + s.toString(), ex); + } + } + } + // 2. remove any old slaves from the connection group + for (String hostPortString : replConnGroup.getSlaveHosts()) { + Server fabServer = this.serverGroup.getServer(hostPortString); + if (fabServer == null || !(fabServer.isSlave())) { + try { + replConnGroup.removeSlaveHost(hostPortString, true); + } catch (SQLException ex) { + // effectively ignored + getLog().logWarn("Unable to remove slave: " + hostPortString, ex); + } + } + } + } + + protected Connection getActiveConnection() throws SQLException { + if (!this.transactionInProgress) { + refreshStateIfNecessary(); + } + + if (this.currentConnection != null) { + return this.currentConnection; + } + + if (getCurrentServerGroup() == null) { + throw SQLError.createSQLException("No server group selected.", SQLError.SQL_STATE_CONNECTION_REJECTED, null, getExceptionInterceptor(), this); + } + + // try to find an existing replication connection to the current group + this.currentConnection = this.serverConnections.get(this.serverGroup); + if (this.currentConnection != null) { + return this.currentConnection; + } + + // otherwise, build a replication connection to the current group + List masterHost = new ArrayList(); + List slaveHosts = new ArrayList(); + for (Server s : this.serverGroup.getServers()) { + if (s.isMaster()) { + masterHost.add(s.getHostPortString()); + } else if (s.isSlave()) { + slaveHosts.add(s.getHostPortString()); + } + } + Properties info = exposeAsProperties(null); + ReplicationConnectionGroup replConnGroup = ReplicationConnectionGroupManager.getConnectionGroup(this.serverGroup.getName()); + if (replConnGroup != null) { + if (replConnGroupLocks.add(this.serverGroup.getName())) { + try { + syncGroupServersToReplicationConnectionGroup(replConnGroup); + } finally { + replConnGroupLocks.remove(this.serverGroup.getName()); + } + } + } + info.put("replicationConnectionGroup", this.serverGroup.getName()); + info.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, this.username); + info.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, this.password); + info.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, getCatalog()); + info.setProperty("connectionAttributes", "fabricHaGroup:" + this.serverGroup.getName()); + info.setProperty("retriesAllDown", "1"); + info.setProperty("allowMasterDownConnections", "true"); + info.setProperty("allowSlaveDownConnections", "true"); + info.setProperty("readFromMasterWhenNoSlaves", "true"); + this.currentConnection = ReplicationConnectionProxy.createProxyInstance(masterHost, info, slaveHosts, info); + this.serverConnections.put(this.serverGroup, this.currentConnection); + + this.currentConnection.setProxy(this); + this.currentConnection.setAutoCommit(this.autoCommit); + this.currentConnection.setReadOnly(this.readOnly); + this.currentConnection.setTransactionIsolation(this.transactionIsolation); + return this.currentConnection; + } + + private void ensureOpen() throws SQLException { + if (this.closed) { + throw SQLError.createSQLException("No operations allowed after connection closed.", SQLError.SQL_STATE_CONNECTION_NOT_OPEN, + getExceptionInterceptor()); + } + } + + private void ensureNoTransactionInProgress() throws SQLException { + ensureOpen(); + if (this.transactionInProgress && !this.autoCommit) { + throw SQLError.createSQLException("Not allow while a transaction is active.", "25000", getExceptionInterceptor()); + } + } + + /** + * Close this connection proxy which entails closing all + * open connections to MySQL servers. + */ + public void close() throws SQLException { + this.closed = true; + for (Connection c : this.serverConnections.values()) { + try { + c.close(); + } catch (SQLException ex) { + } + } + } + + public boolean isClosed() { + return this.closed; + } + + /** + * @param timeout + * @throws SQLException + */ + public boolean isValid(int timeout) throws SQLException { + return !this.closed; + } + + public void setReadOnly(boolean readOnly) throws SQLException { + this.readOnly = readOnly; + for (ReplicationConnection conn : this.serverConnections.values()) { + conn.setReadOnly(readOnly); + } + } + + public boolean isReadOnly() throws SQLException { + return this.readOnly; + } + + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + return this.readOnly; + } + + public void setCatalog(String catalog) throws SQLException { + this.database = catalog; + for (Connection c : this.serverConnections.values()) { + c.setCatalog(catalog); + } + } + + public String getCatalog() { + return this.database; + } + + public void rollback() throws SQLException { + getActiveConnection().rollback(); + transactionCompleted(); + } + + public void rollback(Savepoint savepoint) throws SQLException { + getActiveConnection().rollback(); + transactionCompleted(); + } + + public void commit() throws SQLException { + getActiveConnection().commit(); + transactionCompleted(); + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + this.autoCommit = autoCommit; + for (Connection c : this.serverConnections.values()) { + c.setAutoCommit(this.autoCommit); + } + } + + public void transactionBegun() throws SQLException { + if (!this.autoCommit) { + this.transactionInProgress = true; + } + } + + public void transactionCompleted() throws SQLException { + this.transactionInProgress = false; + refreshStateIfNecessary(); + } + + public boolean getAutoCommit() { + return this.autoCommit; + } + + /** + * @deprecated replaced by getMultiHostSafeProxy() + */ + @Deprecated + public MySQLConnection getLoadBalanceSafeProxy() { + return getMultiHostSafeProxy(); + } + + public MySQLConnection getMultiHostSafeProxy() { + return getActiveMySQLConnectionPassive(); + } + + //////////////////////////////////////////////////////// + // Methods applying changes to all active connections // + //////////////////////////////////////////////////////// + public void setTransactionIsolation(int level) throws SQLException { + this.transactionIsolation = level; + for (Connection c : this.serverConnections.values()) { + c.setTransactionIsolation(level); + } + } + + public void setTypeMap(Map> map) throws SQLException { + for (Connection c : this.serverConnections.values()) { + c.setTypeMap(map); + } + } + + public void setHoldability(int holdability) throws SQLException { + for (Connection c : this.serverConnections.values()) { + c.setHoldability(holdability); + } + } + + public void setProxy(MySQLConnection proxy) { + } + + ////////////////////////////////////////////////////////// + // Methods delegating directly to the active connection // + ////////////////////////////////////////////////////////// + public Savepoint setSavepoint() throws SQLException { + return getActiveConnection().setSavepoint(); + } + + public Savepoint setSavepoint(String name) throws SQLException { + this.transactionInProgress = true; + return getActiveConnection().setSavepoint(name); + } + + public void releaseSavepoint(Savepoint savepoint) { + } + + public CallableStatement prepareCall(String sql) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareCall(sql); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareCall(sql, resultSetType, resultSetConcurrency); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql); + } + + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql, autoGeneratedKeys); + } + + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql, columnIndexes); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + transactionBegun(); + return getActiveConnection().prepareStatement(sql, columnNames); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql, autoGenKeyIndex); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql, autoGenKeyIndexes); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + transactionBegun(); + return getActiveConnection().clientPrepareStatement(sql, autoGenKeyColNames); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql, autoGenKeyIndex); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql, autoGenKeyIndexes); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + transactionBegun(); + return getActiveConnection().serverPrepareStatement(sql, autoGenKeyColNames); + } + + public Statement createStatement() throws SQLException { + transactionBegun(); + return getActiveConnection().createStatement(); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + transactionBegun(); + return getActiveConnection().createStatement(resultSetType, resultSetConcurrency); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + transactionBegun(); + return getActiveConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata) throws SQLException { + return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata); + } + + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException { + return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata, isBatch); + } + + public String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket, int endOfQueryPacketPosition) throws SQLException { + return getActiveMySQLConnection().extractSqlFromPacket(possibleSqlQuery, queryPacket, endOfQueryPacketPosition); + } + + public StringBuilder generateConnectionCommentBlock(StringBuilder buf) { + return getActiveMySQLConnectionPassive().generateConnectionCommentBlock(buf); + } + + public MysqlIO getIO() throws SQLException { + return getActiveMySQLConnection().getIO(); + } + + public Calendar getCalendarInstanceForSessionOrNew() { + return getActiveMySQLConnectionPassive().getCalendarInstanceForSessionOrNew(); + } + + /** + * @deprecated replaced by getServerCharset() + */ + @Deprecated + public String getServerCharacterEncoding() { + return getServerCharset(); + } + + public String getServerCharset() { + return getActiveMySQLConnectionPassive().getServerCharset(); + } + + public TimeZone getServerTimezoneTZ() { + return getActiveMySQLConnectionPassive().getServerTimezoneTZ(); + } + + /** + * Only valid until the end of the transaction. These could optionally be implemented + * to only return true if all current connections return true. + */ + public boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException { + return getActiveConnection().versionMeetsMinimum(major, minor, subminor); + } + + /** + * Only valid until the end of the transaction. + */ + public boolean supportsIsolationLevel() { + return getActiveConnectionPassive().supportsIsolationLevel(); + } + + /** + * Only valid until the end of the transaction. + */ + public boolean supportsQuotedIdentifiers() { + return getActiveConnectionPassive().supportsQuotedIdentifiers(); + } + + public DatabaseMetaData getMetaData() throws SQLException { + return getActiveConnection().getMetaData(); + } + + public String getCharacterSetMetadata() { + return getActiveMySQLConnectionPassive().getCharacterSetMetadata(); + } + + public java.sql.Statement getMetadataSafeStatement() throws SQLException { + return getActiveMySQLConnection().getMetadataSafeStatement(); + } + + /** + * Methods doing essentially nothing + * + * @param iface + */ + public boolean isWrapperFor(Class iface) { + return false; + } + + /** + * @param iface + */ + public T unwrap(Class iface) { + return null; + } + + public void unSafeStatementInterceptors() throws SQLException { + } + + public boolean supportsTransactions() { + // Fabric requires MySQL 5.6 w/GTID + return true; + } + + public boolean isRunningOnJDK13() { + return false; + } + + public void createNewIO(boolean isForReconnect) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public void dumpTestcaseQuery(String query) { + // no-op + } + + public void abortInternal() throws SQLException { + // no-op + } + + public boolean isServerLocal() throws SQLException { + // Fabric doesn't support pipes + return false; + } + + public void shutdownServer() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + @Deprecated + public void clearHasTriedMaster() { + // no-op + } + + @Deprecated + public boolean hasTriedMaster() { + return false; + } + + // This proxy is not XA-aware + public boolean isInGlobalTx() { + return false; + } + + // This proxy is not XA-aware + public void setInGlobalTx(boolean flag) { + throw new RuntimeException("Global transactions not supported."); + } + + public void changeUser(String userName, String newPassword) throws SQLException { + throw SQLError.createSQLException("User change not allowed.", getExceptionInterceptor()); + } + + ///////////////////////////////////// + // FabricMySQLConnectionProperties // + ///////////////////////////////////// + public void setFabricShardKey(String value) { + this.fabricShardKey = value; + } + + public String getFabricShardKey() { + return this.fabricShardKey; + } + + public void setFabricShardTable(String value) { + this.fabricShardTable = value; + } + + public String getFabricShardTable() { + return this.fabricShardTable; + } + + public void setFabricServerGroup(String value) { + this.fabricServerGroup = value; + } + + public String getFabricServerGroup() { + return this.fabricServerGroup; + } + + public void setFabricProtocol(String value) { + this.fabricProtocol = value; + } + + public String getFabricProtocol() { + return this.fabricProtocol; + } + + public void setFabricUsername(String value) { + this.fabricUsername = value; + } + + public String getFabricUsername() { + return this.fabricUsername; + } + + public void setFabricPassword(String value) { + this.fabricPassword = value; + } + + public String getFabricPassword() { + return this.fabricPassword; + } + + public void setFabricReportErrors(boolean value) { + this.reportErrors = value; + } + + public boolean getFabricReportErrors() { + return this.reportErrors; + } + + /////////////////////////////////////////////////////// + // ConnectionProperties - applied to all connections // + /////////////////////////////////////////////////////// + @Override + public void setAllowLoadLocalInfile(boolean property) { + super.setAllowLoadLocalInfile(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAllowLoadLocalInfile(property); + } + } + + @Override + public void setAllowMultiQueries(boolean property) { + super.setAllowMultiQueries(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAllowMultiQueries(property); + } + } + + @Override + public void setAllowNanAndInf(boolean flag) { + super.setAllowNanAndInf(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAllowNanAndInf(flag); + } + } + + @Override + public void setAllowUrlInLocalInfile(boolean flag) { + super.setAllowUrlInLocalInfile(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAllowUrlInLocalInfile(flag); + } + } + + @Override + public void setAlwaysSendSetIsolation(boolean flag) { + super.setAlwaysSendSetIsolation(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAlwaysSendSetIsolation(flag); + } + } + + @Override + public void setAutoDeserialize(boolean flag) { + super.setAutoDeserialize(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoDeserialize(flag); + } + } + + @Override + public void setAutoGenerateTestcaseScript(boolean flag) { + super.setAutoGenerateTestcaseScript(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoGenerateTestcaseScript(flag); + } + } + + @Override + public void setAutoReconnect(boolean flag) { + super.setAutoReconnect(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoReconnect(flag); + } + } + + @Override + public void setAutoReconnectForConnectionPools(boolean property) { + super.setAutoReconnectForConnectionPools(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoReconnectForConnectionPools(property); + } + } + + @Override + public void setAutoReconnectForPools(boolean flag) { + super.setAutoReconnectForPools(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoReconnectForPools(flag); + } + } + + @Override + public void setBlobSendChunkSize(String value) throws SQLException { + super.setBlobSendChunkSize(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setBlobSendChunkSize(value); + } + } + + @Override + public void setCacheCallableStatements(boolean flag) { + super.setCacheCallableStatements(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCacheCallableStatements(flag); + } + } + + @Override + public void setCachePreparedStatements(boolean flag) { + super.setCachePreparedStatements(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCachePreparedStatements(flag); + } + } + + @Override + public void setCacheResultSetMetadata(boolean property) { + super.setCacheResultSetMetadata(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCacheResultSetMetadata(property); + } + } + + @Override + public void setCacheServerConfiguration(boolean flag) { + super.setCacheServerConfiguration(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCacheServerConfiguration(flag); + } + } + + @Override + public void setCallableStatementCacheSize(int size) throws SQLException { + super.setCallableStatementCacheSize(size); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCallableStatementCacheSize(size); + } + } + + @Override + public void setCapitalizeDBMDTypes(boolean property) { + super.setCapitalizeDBMDTypes(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCapitalizeDBMDTypes(property); + } + } + + @Override + public void setCapitalizeTypeNames(boolean flag) { + super.setCapitalizeTypeNames(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCapitalizeTypeNames(flag); + } + } + + @Override + public void setCharacterEncoding(String encoding) { + super.setCharacterEncoding(encoding); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCharacterEncoding(encoding); + } + } + + @Override + public void setCharacterSetResults(String characterSet) { + super.setCharacterSetResults(characterSet); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCharacterSetResults(characterSet); + } + } + + @Override + public void setClobberStreamingResults(boolean flag) { + super.setClobberStreamingResults(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClobberStreamingResults(flag); + } + } + + @Override + public void setClobCharacterEncoding(String encoding) { + super.setClobCharacterEncoding(encoding); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClobCharacterEncoding(encoding); + } + } + + @Override + public void setConnectionCollation(String collation) { + super.setConnectionCollation(collation); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setConnectionCollation(collation); + } + } + + @Override + public void setConnectTimeout(int timeoutMs) throws SQLException { + super.setConnectTimeout(timeoutMs); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setConnectTimeout(timeoutMs); + } + } + + @Override + public void setContinueBatchOnError(boolean property) { + super.setContinueBatchOnError(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setContinueBatchOnError(property); + } + } + + @Override + public void setCreateDatabaseIfNotExist(boolean flag) { + super.setCreateDatabaseIfNotExist(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCreateDatabaseIfNotExist(flag); + } + } + + @Override + public void setDefaultFetchSize(int n) throws SQLException { + super.setDefaultFetchSize(n); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDefaultFetchSize(n); + } + } + + @Override + public void setDetectServerPreparedStmts(boolean property) { + super.setDetectServerPreparedStmts(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDetectServerPreparedStmts(property); + } + } + + @Override + public void setDontTrackOpenResources(boolean flag) { + super.setDontTrackOpenResources(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDontTrackOpenResources(flag); + } + } + + @Override + public void setDumpQueriesOnException(boolean flag) { + super.setDumpQueriesOnException(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDumpQueriesOnException(flag); + } + } + + @Override + public void setDynamicCalendars(boolean flag) { + super.setDynamicCalendars(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDynamicCalendars(flag); + } + } + + @Override + public void setElideSetAutoCommits(boolean flag) { + super.setElideSetAutoCommits(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setElideSetAutoCommits(flag); + } + } + + @Override + public void setEmptyStringsConvertToZero(boolean flag) { + super.setEmptyStringsConvertToZero(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEmptyStringsConvertToZero(flag); + } + } + + @Override + public void setEmulateLocators(boolean property) { + super.setEmulateLocators(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEmulateLocators(property); + } + } + + @Override + public void setEmulateUnsupportedPstmts(boolean flag) { + super.setEmulateUnsupportedPstmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEmulateUnsupportedPstmts(flag); + } + } + + @Override + public void setEnablePacketDebug(boolean flag) { + super.setEnablePacketDebug(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEnablePacketDebug(flag); + } + } + + @Override + public void setEncoding(String property) { + super.setEncoding(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEncoding(property); + } + } + + @Override + public void setExplainSlowQueries(boolean flag) { + super.setExplainSlowQueries(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setExplainSlowQueries(flag); + } + } + + @Override + public void setFailOverReadOnly(boolean flag) { + super.setFailOverReadOnly(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setFailOverReadOnly(flag); + } + } + + @Override + public void setGatherPerformanceMetrics(boolean flag) { + super.setGatherPerformanceMetrics(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setGatherPerformanceMetrics(flag); + } + } + + @Override + public void setHoldResultsOpenOverStatementClose(boolean flag) { + super.setHoldResultsOpenOverStatementClose(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setHoldResultsOpenOverStatementClose(flag); + } + } + + @Override + public void setIgnoreNonTxTables(boolean property) { + super.setIgnoreNonTxTables(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setIgnoreNonTxTables(property); + } + } + + @Override + public void setInitialTimeout(int property) throws SQLException { + super.setInitialTimeout(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setInitialTimeout(property); + } + } + + @Override + public void setIsInteractiveClient(boolean property) { + super.setIsInteractiveClient(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setIsInteractiveClient(property); + } + } + + @Override + public void setJdbcCompliantTruncation(boolean flag) { + super.setJdbcCompliantTruncation(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setJdbcCompliantTruncation(flag); + } + } + + @Override + public void setLocatorFetchBufferSize(String value) throws SQLException { + super.setLocatorFetchBufferSize(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLocatorFetchBufferSize(value); + } + } + + @Override + public void setLogger(String property) { + super.setLogger(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLogger(property); + } + } + + @Override + public void setLoggerClassName(String className) { + super.setLoggerClassName(className); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoggerClassName(className); + } + } + + @Override + public void setLogSlowQueries(boolean flag) { + super.setLogSlowQueries(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLogSlowQueries(flag); + } + } + + @Override + public void setMaintainTimeStats(boolean flag) { + super.setMaintainTimeStats(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setMaintainTimeStats(flag); + } + } + + @Override + public void setMaxQuerySizeToLog(int sizeInBytes) throws SQLException { + super.setMaxQuerySizeToLog(sizeInBytes); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setMaxQuerySizeToLog(sizeInBytes); + } + } + + @Override + public void setMaxReconnects(int property) throws SQLException { + super.setMaxReconnects(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setMaxReconnects(property); + } + } + + @Override + public void setMaxRows(int property) throws SQLException { + super.setMaxRows(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setMaxRows(property); + } + } + + @Override + public void setMetadataCacheSize(int value) throws SQLException { + super.setMetadataCacheSize(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setMetadataCacheSize(value); + } + } + + @Override + public void setNoDatetimeStringSync(boolean flag) { + super.setNoDatetimeStringSync(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNoDatetimeStringSync(flag); + } + } + + @Override + public void setNullCatalogMeansCurrent(boolean value) { + super.setNullCatalogMeansCurrent(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNullCatalogMeansCurrent(value); + } + } + + @Override + public void setNullNamePatternMatchesAll(boolean value) { + super.setNullNamePatternMatchesAll(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNullNamePatternMatchesAll(value); + } + } + + @Override + public void setPacketDebugBufferSize(int size) throws SQLException { + super.setPacketDebugBufferSize(size); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPacketDebugBufferSize(size); + } + } + + @Override + public void setParanoid(boolean property) { + super.setParanoid(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setParanoid(property); + } + } + + @Override + public void setPedantic(boolean property) { + super.setPedantic(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPedantic(property); + } + } + + @Override + public void setPreparedStatementCacheSize(int cacheSize) throws SQLException { + super.setPreparedStatementCacheSize(cacheSize); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPreparedStatementCacheSize(cacheSize); + } + } + + @Override + public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) throws SQLException { + super.setPreparedStatementCacheSqlLimit(cacheSqlLimit); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPreparedStatementCacheSqlLimit(cacheSqlLimit); + } + } + + @Override + public void setProfileSql(boolean property) { + super.setProfileSql(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setProfileSql(property); + } + } + + @Override + public void setProfileSQL(boolean flag) { + super.setProfileSQL(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setProfileSQL(flag); + } + } + + @Override + public void setPropertiesTransform(String value) { + super.setPropertiesTransform(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPropertiesTransform(value); + } + } + + @Override + public void setQueriesBeforeRetryMaster(int property) throws SQLException { + super.setQueriesBeforeRetryMaster(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setQueriesBeforeRetryMaster(property); + } + } + + @Override + public void setReconnectAtTxEnd(boolean property) { + super.setReconnectAtTxEnd(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setReconnectAtTxEnd(property); + } + } + + @Override + public void setRelaxAutoCommit(boolean property) { + super.setRelaxAutoCommit(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRelaxAutoCommit(property); + } + } + + @Override + public void setReportMetricsIntervalMillis(int millis) throws SQLException { + super.setReportMetricsIntervalMillis(millis); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setReportMetricsIntervalMillis(millis); + } + } + + @Override + public void setRequireSSL(boolean property) { + super.setRequireSSL(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRequireSSL(property); + } + } + + @Override + public void setRetainStatementAfterResultSetClose(boolean flag) { + super.setRetainStatementAfterResultSetClose(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRetainStatementAfterResultSetClose(flag); + } + } + + @Override + public void setRollbackOnPooledClose(boolean flag) { + super.setRollbackOnPooledClose(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRollbackOnPooledClose(flag); + } + } + + @Override + public void setRoundRobinLoadBalance(boolean flag) { + super.setRoundRobinLoadBalance(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRoundRobinLoadBalance(flag); + } + } + + @Override + public void setRunningCTS13(boolean flag) { + super.setRunningCTS13(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRunningCTS13(flag); + } + } + + @Override + public void setSecondsBeforeRetryMaster(int property) throws SQLException { + super.setSecondsBeforeRetryMaster(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSecondsBeforeRetryMaster(property); + } + } + + @Override + public void setServerTimezone(String property) { + super.setServerTimezone(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setServerTimezone(property); + } + } + + @Override + public void setSessionVariables(String variables) { + super.setSessionVariables(variables); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSessionVariables(variables); + } + } + + @Override + public void setSlowQueryThresholdMillis(int millis) throws SQLException { + super.setSlowQueryThresholdMillis(millis); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSlowQueryThresholdMillis(millis); + } + } + + @Override + public void setSocketFactoryClassName(String property) { + super.setSocketFactoryClassName(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSocketFactoryClassName(property); + } + } + + @Override + public void setSocketTimeout(int property) throws SQLException { + super.setSocketTimeout(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSocketTimeout(property); + } + } + + @Override + public void setStrictFloatingPoint(boolean property) { + super.setStrictFloatingPoint(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setStrictFloatingPoint(property); + } + } + + @Override + public void setStrictUpdates(boolean property) { + super.setStrictUpdates(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setStrictUpdates(property); + } + } + + @Override + public void setTinyInt1isBit(boolean flag) { + super.setTinyInt1isBit(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTinyInt1isBit(flag); + } + } + + @Override + public void setTraceProtocol(boolean flag) { + super.setTraceProtocol(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTraceProtocol(flag); + } + } + + @Override + public void setTransformedBitIsBoolean(boolean flag) { + super.setTransformedBitIsBoolean(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTransformedBitIsBoolean(flag); + } + } + + @Override + public void setUseCompression(boolean property) { + super.setUseCompression(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseCompression(property); + } + } + + @Override + public void setUseFastIntParsing(boolean flag) { + super.setUseFastIntParsing(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseFastIntParsing(flag); + } + } + + @Override + public void setUseHostsInPrivileges(boolean property) { + super.setUseHostsInPrivileges(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseHostsInPrivileges(property); + } + } + + @Override + public void setUseInformationSchema(boolean flag) { + super.setUseInformationSchema(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseInformationSchema(flag); + } + } + + @Override + public void setUseLocalSessionState(boolean flag) { + super.setUseLocalSessionState(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseLocalSessionState(flag); + } + } + + @Override + public void setUseOldUTF8Behavior(boolean flag) { + super.setUseOldUTF8Behavior(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseOldUTF8Behavior(flag); + } + } + + @Override + public void setUseOnlyServerErrorMessages(boolean flag) { + super.setUseOnlyServerErrorMessages(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseOnlyServerErrorMessages(flag); + } + } + + @Override + public void setUseReadAheadInput(boolean flag) { + super.setUseReadAheadInput(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseReadAheadInput(flag); + } + } + + @Override + public void setUseServerPreparedStmts(boolean flag) { + super.setUseServerPreparedStmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseServerPreparedStmts(flag); + } + } + + @Override + public void setUseSqlStateCodes(boolean flag) { + super.setUseSqlStateCodes(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseSqlStateCodes(flag); + } + } + + @Override + public void setUseSSL(boolean property) { + super.setUseSSL(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseSSL(property); + } + } + + @Override + public void setUseStreamLengthsInPrepStmts(boolean property) { + super.setUseStreamLengthsInPrepStmts(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseStreamLengthsInPrepStmts(property); + } + } + + @Override + public void setUseTimezone(boolean property) { + super.setUseTimezone(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseTimezone(property); + } + } + + @Override + public void setUseUltraDevWorkAround(boolean property) { + super.setUseUltraDevWorkAround(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseUltraDevWorkAround(property); + } + } + + @Override + public void setUseUnbufferedInput(boolean flag) { + super.setUseUnbufferedInput(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseUnbufferedInput(flag); + } + } + + @Override + public void setUseUnicode(boolean flag) { + super.setUseUnicode(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseUnicode(flag); + } + } + + @Override + public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) { + super.setUseUsageAdvisor(useUsageAdvisorFlag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseUsageAdvisor(useUsageAdvisorFlag); + } + } + + @Override + public void setYearIsDateType(boolean flag) { + super.setYearIsDateType(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setYearIsDateType(flag); + } + } + + @Override + public void setZeroDateTimeBehavior(String behavior) { + super.setZeroDateTimeBehavior(behavior); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setZeroDateTimeBehavior(behavior); + } + } + + @Override + public void setUseCursorFetch(boolean flag) { + super.setUseCursorFetch(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseCursorFetch(flag); + } + } + + @Override + public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) { + super.setOverrideSupportsIntegrityEnhancementFacility(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setOverrideSupportsIntegrityEnhancementFacility(flag); + } + } + + @Override + public void setNoTimezoneConversionForTimeType(boolean flag) { + super.setNoTimezoneConversionForTimeType(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNoTimezoneConversionForTimeType(flag); + } + } + + @Override + public void setUseJDBCCompliantTimezoneShift(boolean flag) { + super.setUseJDBCCompliantTimezoneShift(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseJDBCCompliantTimezoneShift(flag); + } + } + + @Override + public void setAutoClosePStmtStreams(boolean flag) { + super.setAutoClosePStmtStreams(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoClosePStmtStreams(flag); + } + } + + @Override + public void setProcessEscapeCodesForPrepStmts(boolean flag) { + super.setProcessEscapeCodesForPrepStmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setProcessEscapeCodesForPrepStmts(flag); + } + } + + @Override + public void setUseGmtMillisForDatetimes(boolean flag) { + super.setUseGmtMillisForDatetimes(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseGmtMillisForDatetimes(flag); + } + } + + @Override + public void setDumpMetadataOnColumnNotFound(boolean flag) { + super.setDumpMetadataOnColumnNotFound(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDumpMetadataOnColumnNotFound(flag); + } + } + + @Override + public void setResourceId(String resourceId) { + super.setResourceId(resourceId); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setResourceId(resourceId); + } + } + + @Override + public void setRewriteBatchedStatements(boolean flag) { + super.setRewriteBatchedStatements(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRewriteBatchedStatements(flag); + } + } + + @Override + public void setJdbcCompliantTruncationForReads(boolean jdbcCompliantTruncationForReads) { + super.setJdbcCompliantTruncationForReads(jdbcCompliantTruncationForReads); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setJdbcCompliantTruncationForReads(jdbcCompliantTruncationForReads); + } + } + + @Override + public void setUseJvmCharsetConverters(boolean flag) { + super.setUseJvmCharsetConverters(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseJvmCharsetConverters(flag); + } + } + + @Override + public void setPinGlobalTxToPhysicalConnection(boolean flag) { + super.setPinGlobalTxToPhysicalConnection(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPinGlobalTxToPhysicalConnection(flag); + } + } + + @Override + public void setGatherPerfMetrics(boolean flag) { + super.setGatherPerfMetrics(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setGatherPerfMetrics(flag); + } + } + + @Override + public void setUltraDevHack(boolean flag) { + super.setUltraDevHack(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUltraDevHack(flag); + } + } + + @Override + public void setInteractiveClient(boolean property) { + super.setInteractiveClient(property); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setInteractiveClient(property); + } + } + + @Override + public void setSocketFactory(String name) { + super.setSocketFactory(name); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSocketFactory(name); + } + } + + @Override + public void setUseServerPrepStmts(boolean flag) { + super.setUseServerPrepStmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseServerPrepStmts(flag); + } + } + + @Override + public void setCacheCallableStmts(boolean flag) { + super.setCacheCallableStmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCacheCallableStmts(flag); + } + } + + @Override + public void setCachePrepStmts(boolean flag) { + super.setCachePrepStmts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCachePrepStmts(flag); + } + } + + @Override + public void setCallableStmtCacheSize(int cacheSize) throws SQLException { + super.setCallableStmtCacheSize(cacheSize); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCallableStmtCacheSize(cacheSize); + } + } + + @Override + public void setPrepStmtCacheSize(int cacheSize) throws SQLException { + super.setPrepStmtCacheSize(cacheSize); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPrepStmtCacheSize(cacheSize); + } + } + + @Override + public void setPrepStmtCacheSqlLimit(int sqlLimit) throws SQLException { + super.setPrepStmtCacheSqlLimit(sqlLimit); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPrepStmtCacheSqlLimit(sqlLimit); + } + } + + @Override + public void setNoAccessToProcedureBodies(boolean flag) { + super.setNoAccessToProcedureBodies(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNoAccessToProcedureBodies(flag); + } + } + + @Override + public void setUseOldAliasMetadataBehavior(boolean flag) { + super.setUseOldAliasMetadataBehavior(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseOldAliasMetadataBehavior(flag); + } + } + + @Override + public void setClientCertificateKeyStorePassword(String value) { + super.setClientCertificateKeyStorePassword(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClientCertificateKeyStorePassword(value); + } + } + + @Override + public void setClientCertificateKeyStoreType(String value) { + super.setClientCertificateKeyStoreType(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClientCertificateKeyStoreType(value); + } + } + + @Override + public void setClientCertificateKeyStoreUrl(String value) { + super.setClientCertificateKeyStoreUrl(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClientCertificateKeyStoreUrl(value); + } + } + + @Override + public void setTrustCertificateKeyStorePassword(String value) { + super.setTrustCertificateKeyStorePassword(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTrustCertificateKeyStorePassword(value); + } + } + + @Override + public void setTrustCertificateKeyStoreType(String value) { + super.setTrustCertificateKeyStoreType(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTrustCertificateKeyStoreType(value); + } + } + + @Override + public void setTrustCertificateKeyStoreUrl(String value) { + super.setTrustCertificateKeyStoreUrl(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTrustCertificateKeyStoreUrl(value); + } + } + + @Override + public void setUseSSPSCompatibleTimezoneShift(boolean flag) { + super.setUseSSPSCompatibleTimezoneShift(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseSSPSCompatibleTimezoneShift(flag); + } + } + + @Override + public void setTreatUtilDateAsTimestamp(boolean flag) { + super.setTreatUtilDateAsTimestamp(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTreatUtilDateAsTimestamp(flag); + } + } + + @Override + public void setUseFastDateParsing(boolean flag) { + super.setUseFastDateParsing(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseFastDateParsing(flag); + } + } + + @Override + public void setLocalSocketAddress(String address) { + super.setLocalSocketAddress(address); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLocalSocketAddress(address); + } + } + + @Override + public void setUseConfigs(String configs) { + super.setUseConfigs(configs); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseConfigs(configs); + } + } + + @Override + public void setGenerateSimpleParameterMetadata(boolean flag) { + super.setGenerateSimpleParameterMetadata(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setGenerateSimpleParameterMetadata(flag); + } + } + + @Override + public void setLogXaCommands(boolean flag) { + super.setLogXaCommands(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLogXaCommands(flag); + } + } + + @Override + public void setResultSetSizeThreshold(int threshold) throws SQLException { + super.setResultSetSizeThreshold(threshold); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setResultSetSizeThreshold(threshold); + } + } + + @Override + public void setNetTimeoutForStreamingResults(int value) throws SQLException { + super.setNetTimeoutForStreamingResults(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setNetTimeoutForStreamingResults(value); + } + } + + @Override + public void setEnableQueryTimeouts(boolean flag) { + super.setEnableQueryTimeouts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setEnableQueryTimeouts(flag); + } + } + + @Override + public void setPadCharsWithSpace(boolean flag) { + super.setPadCharsWithSpace(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPadCharsWithSpace(flag); + } + } + + @Override + public void setUseDynamicCharsetInfo(boolean flag) { + super.setUseDynamicCharsetInfo(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseDynamicCharsetInfo(flag); + } + } + + @Override + public void setClientInfoProvider(String classname) { + super.setClientInfoProvider(classname); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setClientInfoProvider(classname); + } + } + + @Override + public void setPopulateInsertRowWithDefaultValues(boolean flag) { + super.setPopulateInsertRowWithDefaultValues(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPopulateInsertRowWithDefaultValues(flag); + } + } + + @Override + public void setLoadBalanceStrategy(String strategy) { + super.setLoadBalanceStrategy(strategy); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceStrategy(strategy); + } + } + + @Override + public void setTcpNoDelay(boolean flag) { + super.setTcpNoDelay(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTcpNoDelay(flag); + } + } + + @Override + public void setTcpKeepAlive(boolean flag) { + super.setTcpKeepAlive(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTcpKeepAlive(flag); + } + } + + @Override + public void setTcpRcvBuf(int bufSize) throws SQLException { + super.setTcpRcvBuf(bufSize); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTcpRcvBuf(bufSize); + } + } + + @Override + public void setTcpSndBuf(int bufSize) throws SQLException { + super.setTcpSndBuf(bufSize); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTcpSndBuf(bufSize); + } + } + + @Override + public void setTcpTrafficClass(int classFlags) throws SQLException { + super.setTcpTrafficClass(classFlags); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setTcpTrafficClass(classFlags); + } + } + + @Override + public void setUseNanosForElapsedTime(boolean flag) { + super.setUseNanosForElapsedTime(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseNanosForElapsedTime(flag); + } + } + + @Override + public void setSlowQueryThresholdNanos(long nanos) throws SQLException { + super.setSlowQueryThresholdNanos(nanos); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSlowQueryThresholdNanos(nanos); + } + } + + @Override + public void setStatementInterceptors(String value) { + super.setStatementInterceptors(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setStatementInterceptors(value); + } + } + + @Override + public void setUseDirectRowUnpack(boolean flag) { + super.setUseDirectRowUnpack(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseDirectRowUnpack(flag); + } + } + + @Override + public void setLargeRowSizeThreshold(String value) throws SQLException { + super.setLargeRowSizeThreshold(value); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLargeRowSizeThreshold(value); + } + } + + @Override + public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) { + super.setUseBlobToStoreUTF8OutsideBMP(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseBlobToStoreUTF8OutsideBMP(flag); + } + } + + @Override + public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) { + super.setUtf8OutsideBmpExcludedColumnNamePattern(regexPattern); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUtf8OutsideBmpExcludedColumnNamePattern(regexPattern); + } + } + + @Override + public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) { + super.setUtf8OutsideBmpIncludedColumnNamePattern(regexPattern); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUtf8OutsideBmpIncludedColumnNamePattern(regexPattern); + } + } + + @Override + public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) { + super.setIncludeInnodbStatusInDeadlockExceptions(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setIncludeInnodbStatusInDeadlockExceptions(flag); + } + } + + @Override + public void setIncludeThreadDumpInDeadlockExceptions(boolean flag) { + super.setIncludeThreadDumpInDeadlockExceptions(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setIncludeThreadDumpInDeadlockExceptions(flag); + } + } + + @Override + public void setIncludeThreadNamesAsStatementComment(boolean flag) { + super.setIncludeThreadNamesAsStatementComment(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setIncludeThreadNamesAsStatementComment(flag); + } + } + + @Override + public void setBlobsAreStrings(boolean flag) { + super.setBlobsAreStrings(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setBlobsAreStrings(flag); + } + } + + @Override + public void setFunctionsNeverReturnBlobs(boolean flag) { + super.setFunctionsNeverReturnBlobs(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setFunctionsNeverReturnBlobs(flag); + } + } + + @Override + public void setAutoSlowLog(boolean flag) { + super.setAutoSlowLog(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAutoSlowLog(flag); + } + } + + @Override + public void setConnectionLifecycleInterceptors(String interceptors) { + super.setConnectionLifecycleInterceptors(interceptors); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setConnectionLifecycleInterceptors(interceptors); + } + } + + @Override + public void setProfilerEventHandler(String handler) { + super.setProfilerEventHandler(handler); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setProfilerEventHandler(handler); + } + } + + @Override + public void setVerifyServerCertificate(boolean flag) { + super.setVerifyServerCertificate(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setVerifyServerCertificate(flag); + } + } + + @Override + public void setUseLegacyDatetimeCode(boolean flag) { + super.setUseLegacyDatetimeCode(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseLegacyDatetimeCode(flag); + } + } + + @Override + public void setSelfDestructOnPingSecondsLifetime(int seconds) throws SQLException { + super.setSelfDestructOnPingSecondsLifetime(seconds); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSelfDestructOnPingSecondsLifetime(seconds); + } + } + + @Override + public void setSelfDestructOnPingMaxOperations(int maxOperations) throws SQLException { + super.setSelfDestructOnPingMaxOperations(maxOperations); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setSelfDestructOnPingMaxOperations(maxOperations); + } + } + + @Override + public void setUseColumnNamesInFindColumn(boolean flag) { + super.setUseColumnNamesInFindColumn(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseColumnNamesInFindColumn(flag); + } + } + + @Override + public void setUseLocalTransactionState(boolean flag) { + super.setUseLocalTransactionState(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseLocalTransactionState(flag); + } + } + + @Override + public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag) { + super.setCompensateOnDuplicateKeyUpdateCounts(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setCompensateOnDuplicateKeyUpdateCounts(flag); + } + } + + @Override + public void setUseAffectedRows(boolean flag) { + super.setUseAffectedRows(flag); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setUseAffectedRows(flag); + } + } + + @Override + public void setPasswordCharacterEncoding(String characterSet) { + super.setPasswordCharacterEncoding(characterSet); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setPasswordCharacterEncoding(characterSet); + } + } + + @Override + public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) throws SQLException { + super.setLoadBalanceBlacklistTimeout(loadBalanceBlacklistTimeout); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceBlacklistTimeout(loadBalanceBlacklistTimeout); + } + } + + @Override + public void setRetriesAllDown(int retriesAllDown) throws SQLException { + super.setRetriesAllDown(retriesAllDown); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setRetriesAllDown(retriesAllDown); + } + } + + @Override + public void setExceptionInterceptors(String exceptionInterceptors) { + super.setExceptionInterceptors(exceptionInterceptors); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setExceptionInterceptors(exceptionInterceptors); + } + } + + @Override + public void setQueryTimeoutKillsConnection(boolean queryTimeoutKillsConnection) { + super.setQueryTimeoutKillsConnection(queryTimeoutKillsConnection); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setQueryTimeoutKillsConnection(queryTimeoutKillsConnection); + } + } + + @Override + public void setLoadBalancePingTimeout(int loadBalancePingTimeout) throws SQLException { + super.setLoadBalancePingTimeout(loadBalancePingTimeout); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalancePingTimeout(loadBalancePingTimeout); + } + } + + @Override + public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer) { + super.setLoadBalanceValidateConnectionOnSwapServer(loadBalanceValidateConnectionOnSwapServer); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceValidateConnectionOnSwapServer(loadBalanceValidateConnectionOnSwapServer); + } + } + + @Override + public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup) { + super.setLoadBalanceConnectionGroup(loadBalanceConnectionGroup); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceConnectionGroup(loadBalanceConnectionGroup); + } + } + + @Override + public void setLoadBalanceExceptionChecker(String loadBalanceExceptionChecker) { + super.setLoadBalanceExceptionChecker(loadBalanceExceptionChecker); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceExceptionChecker(loadBalanceExceptionChecker); + } + } + + @Override + public void setLoadBalanceSQLStateFailover(String loadBalanceSQLStateFailover) { + super.setLoadBalanceSQLStateFailover(loadBalanceSQLStateFailover); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceSQLStateFailover(loadBalanceSQLStateFailover); + } + } + + @Override + public void setLoadBalanceSQLExceptionSubclassFailover(String loadBalanceSQLExceptionSubclassFailover) { + super.setLoadBalanceSQLExceptionSubclassFailover(loadBalanceSQLExceptionSubclassFailover); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceSQLExceptionSubclassFailover(loadBalanceSQLExceptionSubclassFailover); + } + } + + @Override + public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX) { + super.setLoadBalanceEnableJMX(loadBalanceEnableJMX); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceEnableJMX(loadBalanceEnableJMX); + } + } + + @Override + public void setLoadBalanceAutoCommitStatementThreshold(int loadBalanceAutoCommitStatementThreshold) throws SQLException { + super.setLoadBalanceAutoCommitStatementThreshold(loadBalanceAutoCommitStatementThreshold); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceAutoCommitStatementThreshold(loadBalanceAutoCommitStatementThreshold); + } + } + + @Override + public void setLoadBalanceAutoCommitStatementRegex(String loadBalanceAutoCommitStatementRegex) { + super.setLoadBalanceAutoCommitStatementRegex(loadBalanceAutoCommitStatementRegex); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setLoadBalanceAutoCommitStatementRegex(loadBalanceAutoCommitStatementRegex); + } + } + + @Override + public void setAuthenticationPlugins(String authenticationPlugins) { + super.setAuthenticationPlugins(authenticationPlugins); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setAuthenticationPlugins(authenticationPlugins); + } + } + + @Override + public void setDisabledAuthenticationPlugins(String disabledAuthenticationPlugins) { + super.setDisabledAuthenticationPlugins(disabledAuthenticationPlugins); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDisabledAuthenticationPlugins(disabledAuthenticationPlugins); + } + } + + @Override + public void setDefaultAuthenticationPlugin(String defaultAuthenticationPlugin) { + super.setDefaultAuthenticationPlugin(defaultAuthenticationPlugin); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDefaultAuthenticationPlugin(defaultAuthenticationPlugin); + } + } + + @Override + public void setParseInfoCacheFactory(String factoryClassname) { + super.setParseInfoCacheFactory(factoryClassname); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setParseInfoCacheFactory(factoryClassname); + } + } + + @Override + public void setServerConfigCacheFactory(String factoryClassname) { + super.setServerConfigCacheFactory(factoryClassname); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setServerConfigCacheFactory(factoryClassname); + } + } + + @Override + public void setDisconnectOnExpiredPasswords(boolean disconnectOnExpiredPasswords) { + super.setDisconnectOnExpiredPasswords(disconnectOnExpiredPasswords); + for (ConnectionProperties cp : this.serverConnections.values()) { + cp.setDisconnectOnExpiredPasswords(disconnectOnExpiredPasswords); + } + } + + @Override + public void setGetProceduresReturnsFunctions(boolean getProcedureReturnsFunctions) { + super.setGetProceduresReturnsFunctions(getProcedureReturnsFunctions); + } + + // com.mysql.jdbc.Connection + + public int getActiveStatementCount() { + return -1; + } + + public long getIdleFor() { + return -1; + } + + public Log getLog() { + return this.log; + } + + public boolean isMasterConnection() { + return false; + } + + public boolean isNoBackslashEscapesSet() { + return false; + } + + public boolean isSameResource(Connection c) { + return false; + } + + public boolean parserKnowsUnicode() { + return false; + } + + public void ping() throws SQLException { + } + + public void resetServerState() throws SQLException { + } + + public void setFailedOver(boolean flag) { + } + + @Deprecated + public void setPreferSlaveDuringFailover(boolean flag) { + } + + public void setStatementComment(String comment) { + } + + public void reportQueryTime(long millisOrNanos) { + } + + public boolean isAbonormallyLongQuery(long millisOrNanos) { + return false; + } + + public void initializeExtension(Extension ex) throws SQLException { + } + + public int getAutoIncrementIncrement() { + return -1; + } + + public boolean hasSameProperties(Connection c) { + return false; + } + + public Properties getProperties() { + return null; + } + + public void setSchema(String schema) throws SQLException { + } + + public String getSchema() throws SQLException { + return null; + } + + public void abort(Executor executor) throws SQLException { + } + + public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException { + } + + public int getNetworkTimeout() throws SQLException { + return -1; + } + + public void checkClosed() throws SQLException { + } + + public Object getConnectionMutex() { + return this; + } + + public void setSessionMaxRows(int max) throws SQLException { + for (Connection c : this.serverConnections.values()) { + c.setSessionMaxRows(max); + } + } + + public int getSessionMaxRows() { + return getActiveConnectionPassive().getSessionMaxRows(); + } + + // MySQLConnection + public boolean isProxySet() { + return false; + } + + public Connection duplicate() throws SQLException { + return null; + } + + public CachedResultSetMetaData getCachedMetaData(String sql) { + return null; + } + + public Timer getCancelTimer() { + return null; + } + + public SingleByteCharsetConverter getCharsetConverter(String javaEncodingName) throws SQLException { + return null; + } + + /** + * @deprecated replaced by getEncodingForIndex(int charsetIndex) + */ + @Deprecated + public String getCharsetNameForIndex(int charsetIndex) throws SQLException { + return getEncodingForIndex(charsetIndex); + } + + public String getEncodingForIndex(int charsetIndex) throws SQLException { + return null; + } + + public TimeZone getDefaultTimeZone() { + return null; + } + + public String getErrorMessageEncoding() { + return null; + } + + @Override + public ExceptionInterceptor getExceptionInterceptor() { + if (this.currentConnection == null) { + return null; + } + + return this.currentConnection.getExceptionInterceptor(); + } + + public String getHost() { + return null; + } + + public String getHostPortPair() { + return getActiveMySQLConnectionPassive().getHostPortPair(); + } + + public long getId() { + return -1; + } + + public int getMaxBytesPerChar(String javaCharsetName) throws SQLException { + return -1; + } + + public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) throws SQLException { + return -1; + } + + public int getNetBufferLength() { + return -1; + } + + public boolean getRequiresEscapingEncoder() { + return false; + } + + public int getServerMajorVersion() { + return -1; + } + + public int getServerMinorVersion() { + return -1; + } + + public int getServerSubMinorVersion() { + return -1; + } + + public String getServerVariable(String variableName) { + return null; + } + + public String getServerVersion() { + return null; + } + + public Calendar getSessionLockedCalendar() { + return null; + } + + public String getStatementComment() { + return null; + } + + public List getStatementInterceptorsInstances() { + return null; + } + + public String getURL() { + return null; + } + + public String getUser() { + return null; + } + + public Calendar getUtcCalendar() { + return null; + } + + public void incrementNumberOfPreparedExecutes() { + } + + public void incrementNumberOfPrepares() { + } + + public void incrementNumberOfResultSetsCreated() { + } + + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + } + + public void initializeSafeStatementInterceptors() throws SQLException { + } + + public boolean isClientTzUTC() { + return false; + } + + public boolean isCursorFetchEnabled() throws SQLException { + return false; + } + + public boolean isReadInfoMsgEnabled() { + return false; + } + + public boolean isServerTzUTC() { + return false; + } + + public boolean lowerCaseTableNames() { + return getActiveMySQLConnectionPassive().lowerCaseTableNames(); + } + + public void maxRowsChanged(com.mysql.jdbc.Statement stmt) { + } + + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + } + + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + } + + public void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + } + + public void registerQueryExecutionTime(long queryTimeMs) { + } + + public void registerStatement(com.mysql.jdbc.Statement stmt) { + } + + public void reportNumberOfTablesAccessed(int numTablesAccessed) { + } + + public boolean serverSupportsConvertFn() throws SQLException { + return getActiveMySQLConnection().serverSupportsConvertFn(); + } + + public void setReadInfoMsgEnabled(boolean flag) { + } + + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + } + + public boolean storesLowerCaseTableName() { + return getActiveMySQLConnectionPassive().storesLowerCaseTableName(); + } + + public void throwConnectionClosedException() throws SQLException { + } + + public void unregisterStatement(com.mysql.jdbc.Statement stmt) { + } + + public void unsetMaxRows(com.mysql.jdbc.Statement stmt) throws SQLException { + } + + public boolean useAnsiQuotedIdentifiers() { + return false; + } + + public boolean useMaxRows() { + return false; + } + + // java.sql.Connection + public void clearWarnings() { + } + + public Properties getClientInfo() { + return null; + } + + public String getClientInfo(String name) { + return null; + } + + public int getHoldability() { + return -1; + } + + public int getTransactionIsolation() { + return -1; + } + + public Map> getTypeMap() { + return null; + } + + public SQLWarning getWarnings() throws SQLException { + return getActiveMySQLConnection().getWarnings(); + } + + public String nativeSQL(String sql) throws SQLException { + return getActiveMySQLConnection().nativeSQL(sql); + } + + public ProfilerEventHandler getProfilerEventHandlerInstance() { + return null; + } + + public void setProfilerEventHandlerInstance(ProfilerEventHandler h) { + } + + public void decachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDataSource.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDataSource.java new file mode 100644 index 0000000..d6e3cfc --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDataSource.java @@ -0,0 +1,191 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.Driver; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.Properties; + +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; + +/** + * DataSource used to create connections to a MySQL fabric. + */ +public class FabricMySQLDataSource extends MysqlDataSource implements FabricMySQLConnectionProperties { + + private static final long serialVersionUID = 1L; + + /** Driver used to create connections. */ + private final static Driver driver; + + static { + try { + driver = new FabricMySQLDriver(); + } catch (Exception ex) { + throw new RuntimeException("Can create driver", ex); + } + } + + /** + * Creates a connection using the specified properties. + * copied directly from MysqlDataSource.getConnection(). + * No easy way to override the static `mysqlDriver' without + * globally affecting the driver. + * + * @param props + * the properties to connect with + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + @Override + protected java.sql.Connection getConnection(Properties props) throws SQLException { + String jdbcUrlToUse = null; + + if (!this.explicitUrl) { + StringBuilder jdbcUrl = new StringBuilder("jdbc:mysql:fabric://"); + + if (this.hostName != null) { + jdbcUrl.append(this.hostName); + } + + jdbcUrl.append(":"); + jdbcUrl.append(this.port); + jdbcUrl.append("/"); + + if (this.databaseName != null) { + jdbcUrl.append(this.databaseName); + } + + jdbcUrlToUse = jdbcUrl.toString(); + } else { + jdbcUrlToUse = this.url; + } + + // + // URL should take precedence over properties + // + + Properties urlProps = ((FabricMySQLDriver) driver).parseFabricURL(jdbcUrlToUse, null); + urlProps.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + urlProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + urlProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + + Iterator keys = urlProps.keySet().iterator(); + + while (keys.hasNext()) { + String key = (String) keys.next(); + + props.setProperty(key, urlProps.getProperty(key)); + } + + if (this.fabricShardKey != null) { + props.setProperty(FabricMySQLDriver.FABRIC_SHARD_KEY_PROPERTY_KEY, this.fabricShardKey); + } + if (this.fabricShardTable != null) { + props.setProperty(FabricMySQLDriver.FABRIC_SHARD_TABLE_PROPERTY_KEY, this.fabricShardTable); + } + if (this.fabricServerGroup != null) { + props.setProperty(FabricMySQLDriver.FABRIC_SERVER_GROUP_PROPERTY_KEY, this.fabricServerGroup); + } + props.setProperty(FabricMySQLDriver.FABRIC_PROTOCOL_PROPERTY_KEY, this.fabricProtocol); + if (this.fabricUsername != null) { + props.setProperty(FabricMySQLDriver.FABRIC_USERNAME_PROPERTY_KEY, this.fabricUsername); + } + if (this.fabricPassword != null) { + props.setProperty(FabricMySQLDriver.FABRIC_PASSWORD_PROPERTY_KEY, this.fabricPassword); + } + props.setProperty(FabricMySQLDriver.FABRIC_REPORT_ERRORS_PROPERTY_KEY, Boolean.toString(this.fabricReportErrors)); + + return driver.connect(jdbcUrlToUse, props); + } + + private String fabricShardKey; + private String fabricShardTable; + private String fabricServerGroup; + private String fabricProtocol = "http"; + private String fabricUsername; + private String fabricPassword; + private boolean fabricReportErrors = false; + + public void setFabricShardKey(String value) { + this.fabricShardKey = value; + } + + public String getFabricShardKey() { + return this.fabricShardKey; + } + + public void setFabricShardTable(String value) { + this.fabricShardTable = value; + } + + public String getFabricShardTable() { + return this.fabricShardTable; + } + + public void setFabricServerGroup(String value) { + this.fabricServerGroup = value; + } + + public String getFabricServerGroup() { + return this.fabricServerGroup; + } + + public void setFabricProtocol(String value) { + this.fabricProtocol = value; + } + + public String getFabricProtocol() { + return this.fabricProtocol; + } + + public void setFabricUsername(String value) { + this.fabricUsername = value; + } + + public String getFabricUsername() { + return this.fabricUsername; + } + + public void setFabricPassword(String value) { + this.fabricPassword = value; + } + + public String getFabricPassword() { + return this.fabricPassword; + } + + public void setFabricReportErrors(boolean value) { + this.fabricReportErrors = value; + } + + public boolean getFabricReportErrors() { + return this.fabricReportErrors; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDriver.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDriver.java new file mode 100644 index 0000000..713fb1d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/FabricMySQLDriver.java @@ -0,0 +1,109 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.lang.reflect.Constructor; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import java.util.logging.Logger; + +import com.mysql.jdbc.NonRegisteringDriver; + +/** + * JDBC driver for Fabric MySQL connections. This driver will create connections for URLs of the form: + * jdbc:mysql:fabric://host:port/?fabricShardTable=employees.employees&fabricShardKey=4621. + */ +public class FabricMySQLDriver extends NonRegisteringDriver implements Driver { + // may be extended to support other protocols in the future + public static final String FABRIC_URL_PREFIX = "jdbc:mysql:fabric://"; + + // connection property keys + public static final String FABRIC_SHARD_KEY_PROPERTY_KEY = "fabricShardKey"; + public static final String FABRIC_SHARD_TABLE_PROPERTY_KEY = "fabricShardTable"; + public static final String FABRIC_SERVER_GROUP_PROPERTY_KEY = "fabricServerGroup"; + public static final String FABRIC_PROTOCOL_PROPERTY_KEY = "fabricProtocol"; + public static final String FABRIC_USERNAME_PROPERTY_KEY = "fabricUsername"; + public static final String FABRIC_PASSWORD_PROPERTY_KEY = "fabricPassword"; + public static final String FABRIC_REPORT_ERRORS_PROPERTY_KEY = "fabricReportErrors"; + + // Register ourselves with the DriverManager + static { + try { + DriverManager.registerDriver(new FabricMySQLDriver()); + } catch (SQLException ex) { + throw new RuntimeException("Can't register driver", ex); + } + } + + public FabricMySQLDriver() throws SQLException { + } + + @Override + public Connection connect(String url, Properties info) throws SQLException { + Properties parsedProps = parseFabricURL(url, info); + + if (parsedProps == null) { + return null; + } + + parsedProps.setProperty(FABRIC_PROTOCOL_PROPERTY_KEY, "http"); + if (com.mysql.jdbc.Util.isJdbc4()) { + try { + Constructor jdbc4proxy = Class.forName("com.mysql.fabric.jdbc.JDBC4FabricMySQLConnectionProxy") + .getConstructor(new Class[] { Properties.class }); + return (Connection) com.mysql.jdbc.Util.handleNewInstance(jdbc4proxy, new Object[] { parsedProps }, null); + } catch (Exception e) { + throw (SQLException) new SQLException(e.getMessage()).initCause(e); + } + } + + return new FabricMySQLConnectionProxy(parsedProps); + } + + /** + * Determine whether this is a valid Fabric MySQL URL. It should be of the form: + * jdbc:mysql:fabric://host:port/?options. + */ + @Override + public boolean acceptsURL(String url) throws SQLException { + return parseFabricURL(url, null) != null; + } + + /* static */Properties parseFabricURL(String url, Properties defaults) throws SQLException { + if (!url.startsWith("jdbc:mysql:fabric://")) { + return null; + } + // We have to fudge the URL here to get NonRegisteringDriver.parseURL() to parse it for us. + // It actually checks the prefix and bails if it's not recognized. + // jdbc:mysql:fabric:// => jdbc:mysql:// + return super.parseURL(url.replaceAll("fabric:", ""), defaults); + } + + public Logger getParentLogger() throws SQLException { + throw new SQLException("no logging"); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnection.java new file mode 100644 index 0000000..5c7b4ed --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnection.java @@ -0,0 +1,100 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.SQLException; +import java.util.Set; + +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; + +public interface JDBC4FabricMySQLConnection extends com.mysql.jdbc.JDBC4MySQLConnection { + /** + * Clear all the state that is used to determine which server to + * send queries to. + */ + void clearServerSelectionCriteria() throws SQLException; + + /** + * Set the shard key for the data being accessed. + */ + void setShardKey(String shardKey) throws SQLException; + + /** + * Get the shard key for the data being accessed. + */ + String getShardKey(); + + /** + * Set the table being accessed. Can be a table name or a + * "database.table" pair. The table must be known by Fabric + * as a sharded table. + */ + void setShardTable(String shardTable) throws SQLException; + + /** + * Get the table being accessed. + */ + String getShardTable(); + + /** + * Set the server group name to connect to. Direct server group selection + * is mutually exclusive of sharded data access. + */ + void setServerGroupName(String serverGroupName) throws SQLException; + + /** + * Get the server group name when using direct server group selection. + */ + String getServerGroupName(); + + /** + * Get the current server group. + * + * @returns The currently chosen group if sufficient server group selection + * criteria has been provided. Otherwise null. + */ + ServerGroup getCurrentServerGroup(); + + /** + * Clear the list of tables for the last query. This also clears the + * shard mapping/table and must be given again for the next query via {@link setShardTable} or {@addQueryTable}. + */ + void clearQueryTables() throws SQLException; + + /** + * Add a table to the set of tables used for the next query on this connection. + * This is used for: + *
    + *
  • Choosing a shard given the tables used
  • + *
  • Preventing cross-shard queries
  • + *
+ */ + void addQueryTable(String tableName) throws SQLException; + + /** + * The set of tables to be used in the next query on this connection. + */ + Set getQueryTables(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnectionProxy.java new file mode 100644 index 0000000..5031c81 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/jdbc/JDBC4FabricMySQLConnectionProxy.java @@ -0,0 +1,150 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.jdbc; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Savepoint; +import java.sql.Struct; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TimerTask; +import java.util.Timer; +import java.util.TimeZone; +import java.util.concurrent.Executor; + +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.CachedResultSetMetaData; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.ConnectionPropertiesImpl; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.Extension; +import com.mysql.jdbc.Field; +import com.mysql.jdbc.JDBC4Connection; +import com.mysql.jdbc.JDBC4ClientInfoProvider; +import com.mysql.jdbc.JDBC4MySQLConnection; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlIO; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.ServerPreparedStatement; +import com.mysql.jdbc.SingleByteCharsetConverter; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.StatementInterceptorV2; +import com.mysql.jdbc.log.Log; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.FabricConnection; +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; +import com.mysql.fabric.ServerMode; +import com.mysql.fabric.ShardMapping; + +/** + * Limitations: + *
    + *
  • One shard table can be specified
  • + *
  • One shard key can be specified
  • + *
+ */ +public class JDBC4FabricMySQLConnectionProxy extends FabricMySQLConnectionProxy implements JDBC4FabricMySQLConnection, FabricMySQLConnectionProperties { + + private static final long serialVersionUID = 5845485979107347258L; + + private FabricConnection fabricConnection; + + public JDBC4FabricMySQLConnectionProxy(Properties props) throws SQLException { + super(props); + } + + public Blob createBlob() { + try { + transactionBegun(); + return getActiveConnection().createBlob(); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + } + + public Clob createClob() { + try { + transactionBegun(); + return getActiveConnection().createClob(); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + } + + public NClob createNClob() { + try { + transactionBegun(); + return getActiveConnection().createNClob(); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + } + + public SQLXML createSQLXML() throws SQLException { + transactionBegun(); + return getActiveConnection().createSQLXML(); + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + for (Connection c : serverConnections.values()) + c.setClientInfo(properties); + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + for (Connection c : serverConnections.values()) + c.setClientInfo(name, value); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return getActiveConnection().createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + transactionBegun(); + return getActiveConnection().createStruct(typeName, attributes); + } + + public JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + return ((JDBC4MySQLConnection) getActiveConnection()).getClientInfoProviderImpl(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/AuthenticatedXmlRpcMethodCaller.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/AuthenticatedXmlRpcMethodCaller.java new file mode 100644 index 0000000..ef221c8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/AuthenticatedXmlRpcMethodCaller.java @@ -0,0 +1,74 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.mysql.fabric.FabricCommunicationException; + +/** + * An XML-RPC method caller which wraps another caller with RFC-2617 authentication. + */ +public class AuthenticatedXmlRpcMethodCaller implements XmlRpcMethodCaller { + private XmlRpcMethodCaller underlyingCaller; + + private String url; + private String username; + private String password; + + public AuthenticatedXmlRpcMethodCaller(XmlRpcMethodCaller underlyingCaller, String url, String username, String password) { + this.underlyingCaller = underlyingCaller; + this.url = url; + this.username = username; + this.password = password; + } + + public void setHeader(String name, String value) { + this.underlyingCaller.setHeader(name, value); + } + + public void clearHeader(String name) { + this.underlyingCaller.clearHeader(name); + } + + public List call(String methodName, Object args[]) throws FabricCommunicationException { + String authenticateHeader; + + try { + authenticateHeader = DigestAuthentication.getChallengeHeader(this.url); + } catch (IOException ex) { + throw new FabricCommunicationException("Unable to obtain challenge header for authentication", ex); + } + + Map digestChallenge = DigestAuthentication.parseDigestChallenge(authenticateHeader); + + String authorizationHeader = DigestAuthentication.generateAuthorizationHeader(digestChallenge, this.username, this.password); + + this.underlyingCaller.setHeader("Authorization", authorizationHeader); + + return this.underlyingCaller.call(methodName, args); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/DigestAuthentication.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/DigestAuthentication.java new file mode 100644 index 0000000..b9cb68b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/DigestAuthentication.java @@ -0,0 +1,227 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +/** + * HTTP/1.1 Digest Authentication - RFC 2617 + */ +public class DigestAuthentication { + + private static Random random = new Random(); + + /** + * Get the digest challenge header by connecting to the resource + * with no credentials. + */ + public static String getChallengeHeader(String url) throws IOException { + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + conn.setDoOutput(true); + conn.getOutputStream().close(); + try { + conn.getInputStream().close(); + } catch (IOException ex) { + if (401 == conn.getResponseCode()) { + // we expect a 401-unauthorized response with the + // WWW-Authenticate header to create the request with the + // necessary auth data + String hdr = conn.getHeaderField("WWW-Authenticate"); + if (hdr != null && !"".equals(hdr)) { + return hdr; + } + } else if (400 == conn.getResponseCode()) { + // 400 usually means that auth is disabled on the Fabric node + throw new IOException("Fabric returns status 400. If authentication is disabled on the Fabric node, " + + "omit the `fabricUsername' and `fabricPassword' properties from your connection."); + } else { + throw ex; + } + } + return null; + } + + /** + * Calculate the request digest for algorithm=MD5. + */ + public static String calculateMD5RequestDigest(String uri, String username, String password, String realm, String nonce, String nc, String cnonce, + String qop) { + String reqA1 = username + ":" + realm + ":" + password; + // valid only for qop="auth" + String reqA2 = "POST:" + uri; + + String hashA1 = checksumMD5(reqA1); + String hashA2 = checksumMD5(reqA2); + String requestDigest = digestMD5(hashA1, nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + hashA2); + + return requestDigest; + } + + /** + * MD5 version of the "H()" function from rfc2617. + */ + private static String checksumMD5(String data) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Unable to create MD5 instance", ex); + } + // TODO encoding + return hexEncode(md5.digest(data.getBytes())); + } + + /** + * MD5 version of the "KD()" function from rfc2617. + */ + private static String digestMD5(String secret, String data) { + return checksumMD5(secret + ":" + data); + } + + /** + * hex-encode a byte array + */ + private static String hexEncode(byte data[]) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; ++i) { + sb.append(String.format("%02x", data[i])); + } + return sb.toString(); + } + + /** + * Serialize a parameter map into a digest response. This is used + * as the "Authorization" header in the request. All parameters in + * the supplied map will be added to the header. + */ + public static String serializeDigestResponse(Map paramMap) { + StringBuilder sb = new StringBuilder("Digest "); + + boolean prefixComma = false; + for (Map.Entry entry : paramMap.entrySet()) { + if (!prefixComma) { + prefixComma = true; + } else { + sb.append(", "); + } + sb.append(entry.getKey()); + sb.append("="); + sb.append(entry.getValue()); + } + + return sb.toString(); + } + + /** + * Parse a digest challenge from the WWW-Authenticate header + * return as the initial response during the authentication + * exchange. + */ + public static Map parseDigestChallenge(String headerValue) { + if (!headerValue.startsWith("Digest ")) { + throw new IllegalArgumentException("Header is not a digest challenge"); + } + + String params = headerValue.substring(7); + Map paramMap = new HashMap(); + for (String param : params.split(",\\s*")) { + String pieces[] = param.split("="); + paramMap.put(pieces[0], pieces[1].replaceAll("^\"(.*)\"$", "$1")); + } + return paramMap; + } + + /** + * Generate the cnonce value. This allows the client provide a + * value used in the digest calculation. Same as Python. (no + * motivation given for this algorithm) + */ + @SuppressWarnings("deprecation") + public static String generateCnonce(String nonce, String nc) { + // Random string, keep it in basic printable ASCII range + byte buf[] = new byte[8]; + random.nextBytes(buf); + for (int i = 0; i < 8; ++i) { + buf[i] = (byte) (0x20 + (buf[i] % 95)); + } + + String combo = String.format("%s:%s:%s:%s", nonce, nc, new Date().toGMTString(), new String(buf)); + MessageDigest sha1 = null; + try { + sha1 = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Unable to create SHA-1 instance", ex); + } + + return hexEncode(sha1.digest(combo.getBytes())); + } + + /** + * Quote a parameter to be included in the header. Parameters with + * embedded quotes will be rejected. + */ + private static String quoteParam(String param) { + if (param.contains("\"") || param.contains("'")) { + throw new IllegalArgumentException("Invalid character in parameter"); + } + return "\"" + param + "\""; + } + + /** + * Generate the Authorization header to make the authenticated + * request. + */ + public static String generateAuthorizationHeader(Map digestChallenge, String username, String password) { + String nonce = digestChallenge.get("nonce"); + String nc = "00000001"; + String cnonce = generateCnonce(nonce, nc); + String qop = "auth"; + String uri = "/RPC2"; + String realm = digestChallenge.get("realm"); + String opaque = digestChallenge.get("opaque"); + + String requestDigest = calculateMD5RequestDigest(uri, username, password, realm, nonce, nc, cnonce, qop); + Map digestResponseMap = new HashMap(); + digestResponseMap.put("algorithm", "MD5"); + digestResponseMap.put("username", quoteParam(username)); + digestResponseMap.put("realm", quoteParam(realm)); + digestResponseMap.put("nonce", quoteParam(nonce)); + digestResponseMap.put("uri", quoteParam(uri)); + digestResponseMap.put("qop", qop); + digestResponseMap.put("nc", nc); + digestResponseMap.put("cnonce", quoteParam(cnonce)); + digestResponseMap.put("response", quoteParam(requestDigest)); + digestResponseMap.put("opaque", quoteParam(opaque)); + + return serializeDigestResponse(digestResponseMap); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/InternalXmlRpcMethodCaller.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/InternalXmlRpcMethodCaller.java new file mode 100644 index 0000000..dce28b3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/InternalXmlRpcMethodCaller.java @@ -0,0 +1,119 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.xmlrpc.Client; +import com.mysql.fabric.xmlrpc.base.Array; +import com.mysql.fabric.xmlrpc.base.Member; +import com.mysql.fabric.xmlrpc.base.MethodCall; +import com.mysql.fabric.xmlrpc.base.MethodResponse; +import com.mysql.fabric.xmlrpc.base.Param; +import com.mysql.fabric.xmlrpc.base.Params; +import com.mysql.fabric.xmlrpc.base.Struct; +import com.mysql.fabric.xmlrpc.base.Value; + +/** + * An XML-RPC method caller which uses the internal XML-RPC client + * library. + */ +public class InternalXmlRpcMethodCaller implements XmlRpcMethodCaller { + private Client xmlRpcClient; + + public InternalXmlRpcMethodCaller(String url) throws FabricCommunicationException { + try { + this.xmlRpcClient = new Client(url); + } catch (MalformedURLException ex) { + throw new FabricCommunicationException(ex); + } + } + + /** + * Unwrap the underlying object from the Value wrapper. + */ + private Object unwrapValue(Value v) { + if (v.getType() == Value.TYPE_array) { + return methodResponseArrayToList((Array) v.getValue()); + } else if (v.getType() == Value.TYPE_struct) { + Map s = new HashMap(); + for (Member m : ((Struct) v.getValue()).getMember()) { + s.put(m.getName(), unwrapValue(m.getValue())); + } + return s; + } + return v.getValue(); + } + + private List methodResponseArrayToList(Array array) { + List result = new ArrayList(); + for (Value v : array.getData().getValue()) { + result.add(unwrapValue(v)); + } + return result; + } + + public void setHeader(String name, String value) { + this.xmlRpcClient.setHeader(name, value); + } + + public void clearHeader(String name) { + this.xmlRpcClient.clearHeader(name); + } + + public List call(String methodName, Object args[]) throws FabricCommunicationException { + MethodCall methodCall = new MethodCall(); + Params p = new Params(); + if (args == null) { + args = new Object[] {}; + } + for (int i = 0; i < args.length; ++i) { + if (args[i] == null) { + throw new NullPointerException("nil args unsupported"); + } else if (String.class.isAssignableFrom(args[i].getClass())) { + p.addParam(new Param(new Value((String) args[i]))); + } else if (Double.class.isAssignableFrom(args[i].getClass())) { + p.addParam(new Param(new Value((Double) args[i]))); + } else if (Integer.class.isAssignableFrom(args[i].getClass())) { + p.addParam(new Param(new Value((Integer) args[i]))); + } else { + throw new IllegalArgumentException("Unknown argument type: " + args[i].getClass()); + } + } + methodCall.setMethodName(methodName); + methodCall.setParams(p); + try { + MethodResponse resp = this.xmlRpcClient.execute(methodCall); + return methodResponseArrayToList((Array) resp.getParams().getParam().get(0).getValue().getValue()); + } catch (Exception ex) { + throw new FabricCommunicationException("Error during call to `" + methodName + "' (args=" + Arrays.toString(args) + ")", ex); //irrecoverable + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/ResultSetParser.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/ResultSetParser.java new file mode 100644 index 0000000..6a8e8dc --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/ResultSetParser.java @@ -0,0 +1,60 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parser for result data returned from Fabric XML-RPC protocol. + */ +public class ResultSetParser { + public ResultSetParser() { + } + + /** + * Transform the Fabric formatted result into a list of + * hashes/rows. + */ + public List> parse(Map info, List> rows) { + @SuppressWarnings("unchecked") + List fieldNames = (List) info.get("names"); + Map fieldNameIndexes = new HashMap(); + for (int i = 0; i < fieldNames.size(); ++i) { + fieldNameIndexes.put(fieldNames.get(i), i); + } + + List> result = new ArrayList>(rows.size()); + for (List r : rows) { + Map resultRow = new HashMap(); + for (Map.Entry f : fieldNameIndexes.entrySet()) { + resultRow.put(f.getKey(), r.get(f.getValue())); + } + result.add(resultRow); + } + return result; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcClient.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcClient.java new file mode 100644 index 0000000..eb019c0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcClient.java @@ -0,0 +1,369 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.FabricStateResponse; +import com.mysql.fabric.Response; +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; +import com.mysql.fabric.ServerMode; +import com.mysql.fabric.ServerRole; +import com.mysql.fabric.ShardIndex; +import com.mysql.fabric.ShardMapping; +import com.mysql.fabric.ShardMappingFactory; +import com.mysql.fabric.ShardTable; +import com.mysql.fabric.ShardingType; + +/** + * Fabric client using the XML-RPC protocol. + */ +public class XmlRpcClient { + // name used to identify client to Fabric server for error reports + private static final String THREAT_REPORTER_NAME = "MySQL Connector/J"; + + // method names + private static final String METHOD_DUMP_FABRIC_NODES = "dump.fabric_nodes"; + private static final String METHOD_DUMP_SERVERS = "dump.servers"; + private static final String METHOD_DUMP_SHARD_TABLES = "dump.shard_tables"; + private static final String METHOD_DUMP_SHARD_INDEX = "dump.shard_index"; + private static final String METHOD_DUMP_SHARD_MAPS = "dump.shard_maps"; + private static final String METHOD_SHARDING_LOOKUP_SERVERS = "sharding.lookup_servers"; + private static final String METHOD_SHARDING_CREATE_DEFINITION = "sharding.create_definition"; + private static final String METHOD_SHARDING_ADD_TABLE = "sharding.add_table"; + private static final String METHOD_SHARDING_ADD_SHARD = "sharding.add_shard"; + private static final String METHOD_GROUP_LOOKUP_GROUPS = "group.lookup_groups"; + private static final String METHOD_GROUP_CREATE = "group.create"; + private static final String METHOD_GROUP_ADD = "group.add"; + private static final String METHOD_GROUP_REMOVE = "group.remove"; + private static final String METHOD_GROUP_PROMOTE = "group.promote"; + private static final String METHOD_GROUP_DESTROY = "group.destroy"; + private static final String METHOD_THREAT_REPORT_ERROR = "threat.report_error"; + private static final String METHOD_THREAT_REPORT_FAILURE = "threat.report_failure"; + + // field names for Fabric result sets + private static final String FIELD_MODE = "mode"; + private static final String FIELD_STATUS = "status"; + private static final String FIELD_HOST = "host"; + private static final String FIELD_PORT = "port"; + private static final String FIELD_ADDRESS = "address"; + private static final String FIELD_GROUP_ID = "group_id"; + private static final String FIELD_SERVER_UUID = "server_uuid"; + private static final String FIELD_WEIGHT = "weight"; + private static final String FIELD_SCHEMA_NAME = "schema_name"; + private static final String FIELD_TABLE_NAME = "table_name"; + private static final String FIELD_COLUMN_NAME = "column_name"; + private static final String FIELD_LOWER_BOUND = "lower_bound"; + private static final String FIELD_SHARD_ID = "shard_id"; + private static final String FIELD_MAPPING_ID = "mapping_id"; + private static final String FIELD_GLOBAL_GROUP_ID = "global_group_id"; + private static final String FIELD_TYPE_NAME = "type_name"; + private static final String FIELD_RESULT = "result"; + + private XmlRpcMethodCaller methodCaller; + + public XmlRpcClient(String url, String username, String password) throws FabricCommunicationException { + this.methodCaller = new InternalXmlRpcMethodCaller(url); + if (username != null && !"".equals(username) && password != null) { + this.methodCaller = new AuthenticatedXmlRpcMethodCaller(this.methodCaller, url, username, password); + } + } + + /** + * Unmarshall a response representing a server. + */ + private static Server unmarshallServer(Map serverData) throws FabricCommunicationException { + ServerMode mode; + ServerRole role; + + String host; + int port; + + try { + // dump.servers returns integer mode/status + if (Integer.class.equals(serverData.get(FIELD_MODE).getClass())) { + mode = ServerMode.getFromConstant((Integer) serverData.get(FIELD_MODE)); + role = ServerRole.getFromConstant((Integer) serverData.get(FIELD_STATUS)); + host = (String) serverData.get(FIELD_HOST); + port = (Integer) serverData.get(FIELD_PORT); + } else { + // sharding.lookup_servers returns a different format + mode = ServerMode.valueOf((String) serverData.get(FIELD_MODE)); + role = ServerRole.valueOf((String) serverData.get(FIELD_STATUS)); + String hostnameAndPort[] = ((String) serverData.get(FIELD_ADDRESS)).split(":"); + host = hostnameAndPort[0]; + port = Integer.valueOf(hostnameAndPort[1]); + } + Server s = new Server((String) serverData.get(FIELD_GROUP_ID), (String) serverData.get(FIELD_SERVER_UUID), host, port, mode, role, + (Double) serverData.get(FIELD_WEIGHT)); + return s; + } catch (Exception ex) { + throw new FabricCommunicationException("Unable to parse server definition", ex); + } + } + + /** + * Convert a list of string/string/bool to Server objects. + */ + private static Set toServerSet(List> l) throws FabricCommunicationException { + Set servers = new HashSet(); + for (Map serverData : l) { + servers.add(unmarshallServer(serverData)); + } + return servers; + } + + /** + * Call a method and return the result only if the call is successful. + * + * @throws FabricCommunicationException + * If comm fails or the server reports that the method call failed. + */ + private Response errorSafeCallMethod(String methodName, Object args[]) throws FabricCommunicationException { + List responseData = this.methodCaller.call(methodName, args); + Response response = new Response(responseData); + if (response.getErrorMessage() != null) { + throw new FabricCommunicationException("Call failed to method `" + methodName + "':\n" + response.getErrorMessage()); + } + return response; + } + + /** + * Return a list of Fabric servers. + */ + public Set getFabricNames() throws FabricCommunicationException { + Response resp = errorSafeCallMethod(METHOD_DUMP_FABRIC_NODES, new Object[] {}); + Set names = new HashSet(); + for (Map node : resp.getResultSet()) { + names.add(node.get(FIELD_HOST) + ":" + node.get(FIELD_PORT)); + } + return names; + } + + /** + * Return a list of groups present in this fabric. + */ + public Set getGroupNames() throws FabricCommunicationException { + Set groupNames = new HashSet(); + for (Map row : errorSafeCallMethod(METHOD_GROUP_LOOKUP_GROUPS, null).getResultSet()) { + groupNames.add((String) row.get(FIELD_GROUP_ID)); + } + return groupNames; + } + + public ServerGroup getServerGroup(String groupName) throws FabricCommunicationException { + Set groups = getServerGroups(groupName).getData(); + if (groups.size() == 1) { + return groups.iterator().next(); + } + return null; + } + + public Set getServersForKey(String tableName, int key) throws FabricCommunicationException { + Response r = errorSafeCallMethod(METHOD_SHARDING_LOOKUP_SERVERS, new Object[] { tableName, key }); + return toServerSet(r.getResultSet()); + } + + /** + * Facade for "dump.servers". Will not return empty server groups. + */ + public FabricStateResponse> getServerGroups(String groupPattern) throws FabricCommunicationException { + int version = 0; // necessary but unused + Response response = errorSafeCallMethod(METHOD_DUMP_SERVERS, new Object[] { version, groupPattern }); + // collect all servers by group name + Map> serversByGroupName = new HashMap>(); + for (Map server : response.getResultSet()) { + Server s = unmarshallServer(server); + if (serversByGroupName.get(s.getGroupName()) == null) { + serversByGroupName.put(s.getGroupName(), new HashSet()); + } + serversByGroupName.get(s.getGroupName()).add(s); + } + // create group set + Set serverGroups = new HashSet(); + for (Map.Entry> entry : serversByGroupName.entrySet()) { + ServerGroup g = new ServerGroup(entry.getKey(), entry.getValue()); + serverGroups.add(g); + } + return new FabricStateResponse>(serverGroups, response.getTtl()); + } + + public FabricStateResponse> getServerGroups() throws FabricCommunicationException { + return getServerGroups(""); + } + + private FabricStateResponse> getShardTables(int shardMappingId) throws FabricCommunicationException { + int version = 0; + Object args[] = new Object[] { version, String.valueOf(shardMappingId) }; + Response tablesResponse = errorSafeCallMethod(METHOD_DUMP_SHARD_TABLES, args); + Set tables = new HashSet(); + // construct the tables + for (Map rawTable : tablesResponse.getResultSet()) { + String database = (String) rawTable.get(FIELD_SCHEMA_NAME); + String table = (String) rawTable.get(FIELD_TABLE_NAME); + String column = (String) rawTable.get(FIELD_COLUMN_NAME); + ShardTable st = new ShardTable(database, table, column); + tables.add(st); + } + return new FabricStateResponse>(tables, tablesResponse.getTtl()); + } + + private FabricStateResponse> getShardIndices(int shardMappingId) throws FabricCommunicationException { + int version = 0; + Object args[] = new Object[] { version, String.valueOf(shardMappingId) }; + Response indexResponse = errorSafeCallMethod(METHOD_DUMP_SHARD_INDEX, args); + Set indices = new HashSet(); + + // construct the index + for (Map rawIndexEntry : indexResponse.getResultSet()) { + String bound = (String) rawIndexEntry.get(FIELD_LOWER_BOUND); + int shardId = (Integer) rawIndexEntry.get(FIELD_SHARD_ID); + String groupName = (String) rawIndexEntry.get(FIELD_GROUP_ID); + ShardIndex si = new ShardIndex(bound, shardId, groupName); + indices.add(si); + } + return new FabricStateResponse>(indices, indexResponse.getTtl()); + } + + /** + * Retrieve a set of complete shard mappings. The returned mappings include all information + * available about the mapping. + * + * @param shardMappingIdPattern + * the shard mapping id to retrieve + */ + public FabricStateResponse> getShardMappings(String shardMappingIdPattern) throws FabricCommunicationException { + int version = 0; + Object args[] = new Object[] { version, shardMappingIdPattern }; // common to all calls + Response mapsResponse = errorSafeCallMethod(METHOD_DUMP_SHARD_MAPS, args); + // use the lowest ttl of all the calls + long minExpireTimeMillis = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(mapsResponse.getTtl()); + int baseTtl = mapsResponse.getTtl(); + + // construct the maps + Set mappings = new HashSet(); + for (Map rawMapping : mapsResponse.getResultSet()) { + int mappingId = (Integer) rawMapping.get(FIELD_MAPPING_ID); + ShardingType shardingType = ShardingType.valueOf((String) rawMapping.get(FIELD_TYPE_NAME)); + String globalGroupName = (String) rawMapping.get(FIELD_GLOBAL_GROUP_ID); + + FabricStateResponse> tables = getShardTables(mappingId); + FabricStateResponse> indices = getShardIndices(mappingId); + + if (tables.getExpireTimeMillis() < minExpireTimeMillis) { + minExpireTimeMillis = tables.getExpireTimeMillis(); + } + if (indices.getExpireTimeMillis() < minExpireTimeMillis) { + minExpireTimeMillis = indices.getExpireTimeMillis(); + } + + ShardMapping m = new ShardMappingFactory().createShardMapping(mappingId, shardingType, globalGroupName, tables.getData(), indices.getData()); + mappings.add(m); + } + + return new FabricStateResponse>(mappings, baseTtl, minExpireTimeMillis); + } + + public FabricStateResponse> getShardMappings() throws FabricCommunicationException { + return getShardMappings(""); + } + + /** + * Create a new HA group. + */ + public void createGroup(String groupName) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_GROUP_CREATE, new Object[] { groupName }); + } + + /** + * Destroy an HA group. + */ + public void destroyGroup(String groupName) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_GROUP_DESTROY, new Object[] { groupName }); + } + + /** + * Create a new server in the given group. + */ + public void createServerInGroup(String groupName, String hostname, int port) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_GROUP_ADD, new Object[] { groupName, hostname + ":" + port }); + } + + /** + * Create a new shard mapping. + * + * @param type + * method by which data is distributed to shards + * @param globalGroupName + * name of global group of the shard mapping + * @returns id of the new shard mapping. + */ + public int createShardMapping(ShardingType type, String globalGroupName) throws FabricCommunicationException { + Response r = errorSafeCallMethod(METHOD_SHARDING_CREATE_DEFINITION, new Object[] { type.toString(), globalGroupName }); + return (Integer) r.getResultSet().get(0).get(FIELD_RESULT); + } + + public void createShardTable(int shardMappingId, String database, String table, String column) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_SHARDING_ADD_TABLE, new Object[] { shardMappingId, database + "." + table, column }); + } + + public void createShardIndex(int shardMappingId, String groupNameLowerBoundList) throws FabricCommunicationException { + String status = "ENABLED"; + errorSafeCallMethod(METHOD_SHARDING_ADD_SHARD, new Object[] { shardMappingId, groupNameLowerBoundList, status }); + } + + public void addServerToGroup(String groupName, String hostname, int port) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_GROUP_ADD, new Object[] { groupName, hostname + ":" + port }); + } + + public void removeServerFromGroup(String groupName, String hostname, int port) throws FabricCommunicationException { + errorSafeCallMethod(METHOD_GROUP_REMOVE, new Object[] { groupName, hostname + ":" + port }); + } + + public void promoteServerInGroup(String groupName, String hostname, int port) throws FabricCommunicationException { + ServerGroup serverGroup = getServerGroup(groupName); + for (Server s : serverGroup.getServers()) { + if (s.getHostname().equals(hostname) && s.getPort() == port) { + errorSafeCallMethod(METHOD_GROUP_PROMOTE, new Object[] { groupName, s.getUuid() }); + break; + } + } + } + + public void reportServerError(Server server, String errorDescription, boolean forceFaulty) throws FabricCommunicationException { + String reporter = THREAT_REPORTER_NAME; + String command = METHOD_THREAT_REPORT_ERROR; + if (forceFaulty) { + command = METHOD_THREAT_REPORT_FAILURE; + } + errorSafeCallMethod(command, new Object[] { server.getUuid(), reporter, errorDescription }); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcMethodCaller.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcMethodCaller.java new file mode 100644 index 0000000..928cb70 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/proto/xmlrpc/XmlRpcMethodCaller.java @@ -0,0 +1,39 @@ +/* + Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.proto.xmlrpc; + +import java.util.List; + +import com.mysql.fabric.FabricCommunicationException; + +/** + * Implementations of this class perform XML-RPC method calls. + */ +public interface XmlRpcMethodCaller { + List call(String methodName, Object args[]) throws FabricCommunicationException; + + void setHeader(String name, String value); + + void clearHeader(String name); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/Client.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/Client.java new file mode 100644 index 0000000..8f552eb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/Client.java @@ -0,0 +1,114 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXException; + +import com.mysql.fabric.xmlrpc.base.MethodCall; +import com.mysql.fabric.xmlrpc.base.MethodResponse; +import com.mysql.fabric.xmlrpc.base.ResponseParser; +import com.mysql.fabric.xmlrpc.exceptions.MySQLFabricException; + +/** + * XML-RPC client. + */ +public class Client { + private URL url; + private Map headers = new HashMap(); + + public Client(String url) throws MalformedURLException { + this.url = new URL(url); + } + + public void setHeader(String name, String value) { + this.headers.put(name, value); + } + + public void clearHeader(String name) { + this.headers.remove(name); + } + + public MethodResponse execute(MethodCall methodCall) throws IOException, ParserConfigurationException, SAXException, MySQLFabricException { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) this.url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("User-Agent", "MySQL XML-RPC"); + connection.setRequestProperty("Content-Type", "text/xml"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + // apply headers + for (Map.Entry entry : this.headers.entrySet()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + + String out = methodCall.toString(); + + // Send request + OutputStream os = connection.getOutputStream(); + os.write(out.getBytes()); + os.flush(); + os.close(); + + // Get Response + InputStream is = connection.getInputStream(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser parser = factory.newSAXParser(); + ResponseParser saxp = new ResponseParser(); + + parser.parse(is, saxp); + + is.close(); + + MethodResponse resp = saxp.getMethodResponse(); + if (resp.getFault() != null) { + throw new MySQLFabricException(resp.getFault()); + } + + return resp; + + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Array.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Array.java new file mode 100644 index 0000000..ff69432 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Array.java @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class Array { + + protected Data data; + + /** + * Gets the value of the data property. + */ + public Data getData() { + return this.data; + } + + /** + * Sets the value of the data property. + */ + public void setData(Data value) { + this.data = value; + } + + public void addValue(Value v) { + if (this.data == null) { + this.data = new Data(); + } + this.data.addValue(v); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + sb.append(this.data.toString()); + sb.append(""); + return sb.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Data.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Data.java new file mode 100644 index 0000000..dd4f114 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Data.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +import java.util.ArrayList; +import java.util.List; + +public class Data { + + protected List value; + + public List getValue() { + if (this.value == null) { + this.value = new ArrayList(); + } + return this.value; + } + + public void addValue(Value v) { + getValue().add(v); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (this.value != null) { + sb.append(""); + for (int i = 0; i < this.value.size(); i++) { + sb.append(this.value.get(i).toString()); + } + sb.append(""); + } + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Fault.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Fault.java new file mode 100644 index 0000000..1481ae0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Fault.java @@ -0,0 +1,54 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class Fault { + + protected Value value; + + /** + * Gets the value of the value property. + */ + public Value getValue() { + return this.value; + } + + /** + * Sets the value of the value property. + */ + public void setValue(Value value) { + this.value = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (this.value != null) { + sb.append(""); + sb.append(this.value.toString()); + sb.append(""); + } + return sb.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Member.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Member.java new file mode 100644 index 0000000..5ff7f39 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Member.java @@ -0,0 +1,76 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class Member { + + protected String name; + protected Value value; + + public Member() { + } + + public Member(String name, Value value) { + setName(name); + setValue(value); + } + + /** + * Gets the value of the name property. + */ + public String getName() { + return this.name; + } + + /** + * Sets the value of the name property. + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the value property. + */ + public Value getValue() { + return this.value; + } + + /** + * Sets the value of the value property. + */ + public void setValue(Value value) { + this.value = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("" + this.name + ""); + sb.append(this.value.toString()); + sb.append(""); + return sb.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodCall.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodCall.java new file mode 100644 index 0000000..959a30c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodCall.java @@ -0,0 +1,71 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class MethodCall { + + protected String methodName; + protected Params params; + + /** + * Gets the value of the methodName property. + */ + public String getMethodName() { + return this.methodName; + } + + /** + * Sets the value of the methodName property. + */ + public void setMethodName(String value) { + this.methodName = value; + } + + /** + * Gets the value of the params property. + */ + public Params getParams() { + return this.params; + } + + /** + * Sets the value of the params property. + */ + public void setParams(Params value) { + this.params = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + sb.append(""); + sb.append(" " + this.methodName + ""); + if (this.params != null) { + sb.append(this.params.toString()); + } + sb.append(""); + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodResponse.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodResponse.java new file mode 100644 index 0000000..dbd63fd --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/MethodResponse.java @@ -0,0 +1,73 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class MethodResponse { + + protected Params params; + protected Fault fault; + + /** + * Gets the value of the params property. + */ + public Params getParams() { + return this.params; + } + + /** + * Sets the value of the params property. + */ + public void setParams(Params value) { + this.params = value; + } + + /** + * Gets the value of the fault property. + */ + public Fault getFault() { + return this.fault; + } + + /** + * Sets the value of the fault property. + */ + public void setFault(Fault value) { + this.fault = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + sb.append(""); + if (this.params != null) { + sb.append(this.params.toString()); + } + if (this.fault != null) { + sb.append(this.fault.toString()); + } + sb.append(""); + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Param.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Param.java new file mode 100644 index 0000000..4f53db6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Param.java @@ -0,0 +1,59 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +public class Param { + + protected Value value; + + public Param() { + } + + public Param(Value value) { + this.value = value; + } + + /** + * Gets the value of the value property. + */ + public Value getValue() { + return this.value; + } + + /** + * Sets the value of the value property. + */ + public void setValue(Value value) { + this.value = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + sb.append(this.value.toString()); + sb.append(""); + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Params.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Params.java new file mode 100644 index 0000000..61e3461 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Params.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +import java.util.ArrayList; +import java.util.List; + +public class Params { + + protected List param; + + public List getParam() { + if (this.param == null) { + this.param = new ArrayList(); + } + return this.param; + } + + public void addParam(Param p) { + getParam().add(p); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (this.param != null) { + sb.append(""); + for (int i = 0; i < this.param.size(); i++) { + sb.append(this.param.get(i).toString()); + } + sb.append(""); + } + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/ResponseParser.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/ResponseParser.java new file mode 100644 index 0000000..dc10f1d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/ResponseParser.java @@ -0,0 +1,151 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +import java.util.Stack; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +public class ResponseParser extends DefaultHandler { + + private MethodResponse resp = null; + + public MethodResponse getMethodResponse() { + return this.resp; + } + + Stack elNames = new Stack(); + Stack objects = new Stack(); + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + + String thisElement = qName; + if (thisElement != null) { + this.elNames.push(thisElement); + + if (thisElement.equals("methodResponse")) { + this.objects.push(new MethodResponse()); + } else if (thisElement.equals("params")) { + this.objects.push(new Params()); + } else if (thisElement.equals("param")) { + this.objects.push(new Param()); + } else if (thisElement.equals("value")) { + this.objects.push(new Value()); + } else if (thisElement.equals("array")) { + this.objects.push(new Array()); + } else if (thisElement.equals("data")) { + this.objects.push(new Data()); + } else if (thisElement.equals("struct")) { + this.objects.push(new Struct()); + } else if (thisElement.equals("member")) { + this.objects.push(new Member()); + } else if (thisElement.equals("fault")) { + this.objects.push(new Fault()); + } + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + String thisElement = (String) this.elNames.pop(); + if (thisElement != null) { + if (thisElement.equals("methodResponse")) { + this.resp = (MethodResponse) this.objects.pop(); + } else if (thisElement.equals("params")) { + Params pms = (Params) this.objects.pop(); + MethodResponse parent = (MethodResponse) this.objects.peek(); + parent.setParams(pms); + } else if (thisElement.equals("param")) { + Param p = (Param) this.objects.pop(); + Params parent = (Params) this.objects.peek(); + parent.addParam(p); + } else if (thisElement.equals("value")) { + Value v = (Value) this.objects.pop(); + Object parent = this.objects.peek(); + if (parent instanceof Data) { + ((Data) parent).addValue(v); + } else if (parent instanceof Param) { + ((Param) parent).setValue(v); + } else if (parent instanceof Member) { + ((Member) parent).setValue(v); + } else if (parent instanceof Fault) { + ((Fault) parent).setValue(v); + } + } else if (thisElement.equals("array")) { + Array a = (Array) this.objects.pop(); + Value parent = (Value) this.objects.peek(); + parent.setArray(a); + } else if (thisElement.equals("data")) { + Data d = (Data) this.objects.pop(); + Array parent = (Array) this.objects.peek(); + parent.setData(d); + } else if (thisElement.equals("struct")) { + Struct s = (Struct) this.objects.pop(); + Value parent = (Value) this.objects.peek(); + parent.setStruct(s); + } else if (thisElement.equals("member")) { + Member m = (Member) this.objects.pop(); + Struct parent = (Struct) this.objects.peek(); + parent.addMember(m); + } else if (thisElement.equals("fault")) { + Fault f = (Fault) this.objects.pop(); + MethodResponse parent = (MethodResponse) this.objects.peek(); + parent.setFault(f); + } + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + try { + String thisElement = (String) this.elNames.peek(); + if (thisElement != null) { + if (thisElement.equals("name")) { + ((Member) this.objects.peek()).setName(new String(ch, start, length)); + } else if (thisElement.equals("value")) { + ((Value) this.objects.peek()).appendString(new String(ch, start, length)); + } else if (thisElement.equals("i4") || thisElement.equals("int")) { + ((Value) this.objects.peek()).setInt(new String(ch, start, length)); + } else if (thisElement.equals("boolean")) { + ((Value) this.objects.peek()).setBoolean(new String(ch, start, length)); + } else if (thisElement.equals("string")) { + ((Value) this.objects.peek()).appendString(new String(ch, start, length)); + } else if (thisElement.equals("double")) { + ((Value) this.objects.peek()).setDouble(new String(ch, start, length)); + } else if (thisElement.equals("dateTime.iso8601")) { + ((Value) this.objects.peek()).setDateTime(new String(ch, start, length)); + } else if (thisElement.equals("base64")) { + ((Value) this.objects.peek()).setBase64(new String(ch, start, length).getBytes()); + } + } + } catch (Exception e) { + throw new SAXParseException(e.getMessage(), null, e); + } + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Struct.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Struct.java new file mode 100644 index 0000000..db3f534 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Struct.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +import java.util.ArrayList; +import java.util.List; + +public class Struct { + + protected List member; + + public List getMember() { + if (this.member == null) { + this.member = new ArrayList(); + } + return this.member; + } + + public void addMember(Member m) { + getMember().add(m); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (this.member != null) { + sb.append(""); + for (int i = 0; i < this.member.size(); i++) { + sb.append(this.member.get(i).toString()); + } + sb.append(""); + } + return sb.toString(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Value.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Value.java new file mode 100644 index 0000000..0d6b9ea --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/base/Value.java @@ -0,0 +1,231 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.base; + +import java.util.Arrays; +import java.util.GregorianCalendar; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +public class Value { + + public static final byte TYPE_i4 = 0; + public static final byte TYPE_int = 1; + public static final byte TYPE_boolean = 2; + public static final byte TYPE_string = 3; + public static final byte TYPE_double = 4; + public static final byte TYPE_dateTime_iso8601 = 5; + public static final byte TYPE_base64 = 6; + public static final byte TYPE_struct = 7; + public static final byte TYPE_array = 8; + + protected Object objValue = ""; + protected byte objType = TYPE_string; + private DatatypeFactory dtf = null; + + public Value() { + } + + public Value(int value) { + setInt(value); + } + + public Value(String value) { + setString(value); + } + + public Value(boolean value) { + setBoolean(value); + } + + public Value(double value) { + setDouble(value); + } + + public Value(GregorianCalendar value) throws DatatypeConfigurationException { + setDateTime(value); + } + + public Value(byte[] value) { + setBase64(value); + } + + public Value(Struct value) { + setStruct(value); + } + + public Value(Array value) { + setArray(value); + } + + public Object getValue() { + return this.objValue; + } + + public byte getType() { + return this.objType; + } + + public void setInt(int value) { + this.objValue = Integer.valueOf(value); + this.objType = TYPE_int; + } + + public void setInt(String value) { + this.objValue = Integer.valueOf(value); + this.objType = TYPE_int; + } + + public void setString(String value) { + this.objValue = value; + this.objType = TYPE_string; + } + + public void appendString(String value) { + this.objValue = this.objValue != null ? this.objValue + value : value; + this.objType = TYPE_string; + } + + public void setBoolean(boolean value) { + this.objValue = Boolean.valueOf(value); + this.objType = TYPE_boolean; + } + + public void setBoolean(String value) { + if (value.trim().equals("1") || value.trim().equalsIgnoreCase("true")) { + this.objValue = true; + } else { + this.objValue = false; + } + this.objType = TYPE_boolean; + } + + public void setDouble(double value) { + this.objValue = Double.valueOf(value); + this.objType = TYPE_double; + } + + public void setDouble(String value) { + this.objValue = Double.valueOf(value); + this.objType = TYPE_double; + } + + public void setDateTime(GregorianCalendar value) throws DatatypeConfigurationException { + if (this.dtf == null) { + this.dtf = DatatypeFactory.newInstance(); + } + this.objValue = this.dtf.newXMLGregorianCalendar(value); + this.objType = TYPE_dateTime_iso8601; + } + + public void setDateTime(String value) throws DatatypeConfigurationException { + if (this.dtf == null) { + this.dtf = DatatypeFactory.newInstance(); + } + this.objValue = this.dtf.newXMLGregorianCalendar(value); + this.objType = TYPE_dateTime_iso8601; + } + + public void setBase64(byte[] value) { + this.objValue = value; + this.objType = TYPE_base64; + } + + public void setStruct(Struct value) { + this.objValue = value; + this.objType = TYPE_struct; + } + + public void setArray(Array value) { + this.objValue = value; + this.objType = TYPE_array; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(""); + switch (this.objType) { + case Value.TYPE_i4: + sb.append("" + ((Integer) this.objValue).toString() + ""); + break; + case Value.TYPE_int: + sb.append("" + ((Integer) this.objValue).toString() + ""); + break; + + case Value.TYPE_boolean: + sb.append("" + (((Boolean) this.objValue) ? 1 : 0) + ""); + break; + + case Value.TYPE_double: + sb.append("" + ((Double) this.objValue).toString() + ""); + break; + + case Value.TYPE_dateTime_iso8601: + sb.append("" + ((XMLGregorianCalendar) this.objValue).toString() + ""); + break; + + case Value.TYPE_base64: + sb.append("" + Arrays.toString((byte[]) this.objValue) + ""); // TODO it's wrong but no harm because it isn't used in fabric protocol + break; + + case Value.TYPE_struct: + sb.append(((Struct) this.objValue).toString()); + break; + + case Value.TYPE_array: + sb.append(((Array) this.objValue).toString()); + break; + + default: + sb.append("" + escapeXMLChars(this.objValue.toString()) + ""); + } + sb.append(""); + return sb.toString(); + } + + private String escapeXMLChars(String s) { + StringBuilder sb = new StringBuilder(s.length()); + char c; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + switch (c) { + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + default: + sb.append(c); + break; + } + } + return sb.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/exceptions/MySQLFabricException.java b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/exceptions/MySQLFabricException.java new file mode 100644 index 0000000..641e6d0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/fabric/xmlrpc/exceptions/MySQLFabricException.java @@ -0,0 +1,55 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.fabric.xmlrpc.exceptions; + +import java.sql.SQLException; + +import com.mysql.fabric.xmlrpc.base.Fault; +import com.mysql.fabric.xmlrpc.base.Struct; + +public class MySQLFabricException extends SQLException { + + static final long serialVersionUID = -8776763137552613517L; + + public MySQLFabricException() { + super(); + } + + public MySQLFabricException(Fault fault) { + super((String) ((Struct) fault.getValue().getValue()).getMember().get(1).getValue().getValue(), "", + (Integer) ((Struct) fault.getValue().getValue()).getMember().get(0).getValue().getValue()); + } + + public MySQLFabricException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLFabricException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLFabricException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AbandonedConnectionCleanupThread.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AbandonedConnectionCleanupThread.java new file mode 100644 index 0000000..8da014e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AbandonedConnectionCleanupThread.java @@ -0,0 +1,67 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.ref.Reference; + +import com.mysql.jdbc.NonRegisteringDriver.ConnectionPhantomReference; + +public class AbandonedConnectionCleanupThread extends Thread { + private static boolean running = true; + private static Thread threadRef = null; + + public AbandonedConnectionCleanupThread() { + super("Abandoned connection cleanup thread"); + } + + @Override + public void run() { + threadRef = this; + while (running) { + try { + Reference ref = NonRegisteringDriver.refQueue.remove(100); + if (ref != null) { + try { + ((ConnectionPhantomReference) ref).cleanup(); + } finally { + NonRegisteringDriver.connectionPhantomRefs.remove(ref); + } + } + + } catch (Exception ex) { + // no where to really log this if we're static + } + } + } + + public static void shutdown() throws InterruptedException { + running = false; + if (threadRef != null) { + threadRef.interrupt(); + threadRef.join(); + threadRef = null; + } + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AssertionFailedException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AssertionFailedException.java new file mode 100644 index 0000000..5014f5c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AssertionFailedException.java @@ -0,0 +1,55 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Assertions for empty code paths that should never be executed. + */ +public class AssertionFailedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Convenience method. + * + * @param ex + * the exception that should never have been thrown. + * @throws AssertionFailedException + * for the exception ex. + */ + public static void shouldNotHappen(Exception ex) throws AssertionFailedException { + throw new AssertionFailedException(ex); + } + + /** + * Creates an AssertionFailedException for the given exception that should + * never have been thrown. + * + * @param ex + * the exception that should never have been thrown. + */ + public AssertionFailedException(Exception ex) { + super(Messages.getString("AssertionFailedException.0") + ex.toString() + Messages.getString("AssertionFailedException.1")); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AuthenticationPlugin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AuthenticationPlugin.java new file mode 100644 index 0000000..555b1f9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/AuthenticationPlugin.java @@ -0,0 +1,113 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.List; + +/** + * Implementors of this interface can be installed via the "authenticationPlugins" configuration property. + * + * The driver will create one instance of a given plugin per {@link MysqlIO} instance if it's reusable (see {@link #isReusable()}) or a new instance + * in each {@link MysqlIO#proceedHandshakeWithPluggableAuthentication(String, String, String, Buffer)} call. + */ +public interface AuthenticationPlugin extends Extension { + + /** + * Returns the name that the MySQL server uses on + * the wire for this plugin + */ + String getProtocolPluginName(); + + /** + * Does this plugin require the connection itself to be confidential + * (i.e. tls/ssl)...Highly recommended to return "true" for plugins + * that return the credentials in the clear. + */ + boolean requiresConfidentiality(); + + /** + * @return true if plugin instance may be reused, false otherwise + */ + boolean isReusable(); + + /** + * This method called from cJ before first nextAuthenticationStep + * call. Values of user and password parameters are passed from + * those in MysqlIO.changeUser(String userName, String password, + * String database) or MysqlIO.doHandshake(String user, String + * password, String database). + * + * Plugin should use these values instead of values from connection + * properties because parent method may be a changeUser call which + * saves user and password into connection only after successful + * handshake. + * + * @param user + * @param password + */ + void setAuthenticationParameters(String user, String password); + + /** + * Process authentication handshake data from server and optionally + * produce data to be sent back to the server. The driver will keep + * calling this method until either a SQLException is thrown + * (authentication failure, please use appropriate SQLStates) or the + * method returns false or driver receives an OK packet from the server + * which indicates that the connection has been already approved. + * + * If, on return from this method, toServer is a non-empty list of + * buffers, then these buffers should be sent to the server in order and + * without any reads in between them. If toServer is an empty list, no + * data should be sent to server. + * + * If method returns true, it means that this plugin does not need any + * more data from the server to conclude the handshake and this method + * should not be called again. (Note that server can send an Auth Method + * Switch request and then another handshake will start, possibly using a + * different plugin.) + * + * If this method returns false, it means that plugin needs more data from + * the server to conclude the handshake. In that case next handshake data + * payload should be read from the server (after possibly writing data + * from toServer as explained above). Then this method should be called + * again with the new data in fromServer parameter. + * + * In case of errors the method should throw SQLException with appropriate + * SQLStates. + * + * @param fromServer + * a buffer containing handshake data payload from + * server (can be empty). + * @param toServer + * list of buffers with data to be sent to the server + * (the list can be empty, but buffers in the list + * should contain data). + * + * @return False if more data should be read from the server and next call + * to this method made, true otherwise. + */ + boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException; + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BalanceStrategy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BalanceStrategy.java new file mode 100644 index 0000000..cf8f7a0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BalanceStrategy.java @@ -0,0 +1,73 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +/** + * Implement this interface to provide a new load balancing strategy for URLs of the form "jdbc:mysql:loadbalance://..", and provide the implementation class + * name as the configuration parameter "loadBalanceStrategy". + * + * The driver will not pass in a Connection instance when calling init(), but it will pass in the Properties, otherwise it acts like a normal Extension. + * + * One instance of a strategy *per* JDBC connection instance will be created. If you need singleton-like behavior, you're on your own to provide it. + */ +public interface BalanceStrategy extends Extension { + /** + * Called by the driver to pick a new connection to route requests over. + * + * @param proxy + * the InvocationHandler that deals with actual method calls to + * the JDBC connection, and serves as a factory for new + * connections for this strategy via the + * createConnectionForHost() method. + * + * This proxy takes care of maintaining the response time list, map of + * host/ports to live connections, and taking connections out of the live + * connections map if they receive a network-related error while they are in + * use by the application. + * @param configuredHosts + * the list of hosts/ports (in "host:port" form) as passed in by + * the user. + * @param liveConnections + * a map of host/ports to "live" connections to them. + * @param responseTimes + * the list of response times for a transaction + * for each host in the configured hosts list. + * @param numRetries + * the number of times the driver expects this strategy to re-try + * connection attempts if creating a new connection fails. + * @return the physical JDBC connection for the application to use, based + * upon the strategy employed. + * @throws SQLException + * if a new connection can not be found or created by this + * strategy. + * + * @see LoadBalancedConnectionProxy#createConnectionForHost(String) + */ + public abstract ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException; +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BestResponseTimeBalanceStrategy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BestResponseTimeBalanceStrategy.java new file mode 100644 index 0000000..a6a853e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BestResponseTimeBalanceStrategy.java @@ -0,0 +1,115 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class BestResponseTimeBalanceStrategy implements BalanceStrategy { + + public BestResponseTimeBalanceStrategy() { + } + + public void destroy() { + // we don't have anything to clean up + } + + public void init(Connection conn, Properties props) throws SQLException { + // we don't have anything to initialize + } + + public ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + + Map blackList = proxy.getGlobalBlacklist(); + + SQLException ex = null; + + for (int attempts = 0; attempts < numRetries;) { + long minResponseTime = Long.MAX_VALUE; + + int bestHostIndex = 0; + + // safety + if (blackList.size() == configuredHosts.size()) { + blackList = proxy.getGlobalBlacklist(); + } + + for (int i = 0; i < responseTimes.length; i++) { + long candidateResponseTime = responseTimes[i]; + + if (candidateResponseTime < minResponseTime && !blackList.containsKey(configuredHosts.get(i))) { + if (candidateResponseTime == 0) { + bestHostIndex = i; + + break; + } + + bestHostIndex = i; + minResponseTime = candidateResponseTime; + } + } + + String bestHost = configuredHosts.get(bestHostIndex); + + ConnectionImpl conn = liveConnections.get(bestHost); + + if (conn == null) { + try { + conn = proxy.createConnectionForHost(bestHost); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (proxy.shouldExceptionTriggerConnectionSwitch(sqlEx)) { + proxy.addToGlobalBlacklist(bestHost); + blackList.put(bestHost, null); + + if (blackList.size() == configuredHosts.size()) { + attempts++; + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + blackList = proxy.getGlobalBlacklist(); // try again after a little bit + } + + continue; + } + + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Blob.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Blob.java new file mode 100644 index 0000000..57e2573 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Blob.java @@ -0,0 +1,363 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; + +/** + * The representation (mapping) in the JavaTM programming language of an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object + * as a column value in a row of a database table. The driver implements Blob using an SQL locator(BLOB), which means that a Blob object contains a logical + * pointer to the SQL BLOB data rather than the data itself. A Blob object is valid for the duration of the transaction in which is was created. Methods in + * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob + * interface provides methods for getting the length of an SQL BLOB (Binary Large Object) value, for materializing a BLOB value on the client, and for + * determining the position of a pattern of bytes within a BLOB value. This class is new in the JDBC 2.0 API. + */ +public class Blob implements java.sql.Blob, OutputStreamWatcher { + + // + // This is a real brain-dead implementation of BLOB. Once I add streamability to the I/O for MySQL this will be more efficiently implemented + // (except for the position() method, ugh). + // + + /** The binary data that makes up this BLOB */ + private byte[] binaryData = null; + private boolean isClosed = false; + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates a Blob without data + */ + Blob(ExceptionInterceptor exceptionInterceptor) { + setBinaryData(Constants.EMPTY_BYTE_ARRAY); + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Creates a BLOB encapsulating the given binary data + * + * @param data + */ + Blob(byte[] data, ExceptionInterceptor exceptionInterceptor) { + setBinaryData(data); + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Creates an updatable BLOB that can update in-place (not implemented yet). + * + * @param data + * @param creatorResultSetToSet + * @param columnIndexToSet + */ + Blob(byte[] data, ResultSetInternalMethods creatorResultSetToSet, int columnIndexToSet) { + setBinaryData(data); + } + + private synchronized byte[] getBinaryData() { + return this.binaryData; + } + + /** + * Retrieves the BLOB designated by this Blob instance as a stream. + * + * @return this BLOB represented as a binary stream of bytes. + * + * @throws SQLException + * if a database error occurs + */ + public synchronized java.io.InputStream getBinaryStream() throws SQLException { + checkClosed(); + + return new ByteArrayInputStream(getBinaryData()); + } + + /** + * Returns as an array of bytes, part or all of the BLOB value that this + * Blob object designates. + * + * @param pos + * where to start the part of the BLOB + * @param length + * the length of the part of the BLOB you want returned. + * + * @return the bytes stored in the blob starting at position pos and having a length of length. + * + * @throws SQLException + * if a database error occurs + */ + public synchronized byte[] getBytes(long pos, int length) throws SQLException { + checkClosed(); + + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + pos--; + + if (pos > this.binaryData.length) { + throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + if (pos + length > this.binaryData.length) { + throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + byte[] newData = new byte[length]; + System.arraycopy(getBinaryData(), (int) (pos), newData, 0, length); + + return newData; + } + + /** + * Returns the number of bytes in the BLOB value designated by this Blob + * object. + * + * @return the length of this blob + * + * @throws SQLException + * if a database error occurs + */ + public synchronized long length() throws SQLException { + checkClosed(); + + return getBinaryData().length; + } + + /** + * @see java.sql.Blob#position(byte[], long) + */ + public synchronized long position(byte[] pattern, long start) throws SQLException { + throw SQLError.createSQLException("Not implemented", this.exceptionInterceptor); + } + + /** + * Finds the position of the given pattern in this BLOB. + * + * @param pattern + * the pattern to find + * @param start + * where to start finding the pattern + * + * @return the position where the pattern is found in the BLOB, -1 if not + * found + * + * @throws SQLException + * if a database error occurs + */ + public synchronized long position(java.sql.Blob pattern, long start) throws SQLException { + checkClosed(); + + return position(pattern.getBytes(0, (int) pattern.length()), start); + } + + private synchronized void setBinaryData(byte[] newBinaryData) { + this.binaryData = newBinaryData; + } + + /** + * @see Blob#setBinaryStream(long) + */ + public synchronized OutputStream setBinaryStream(long indexToWriteAt) throws SQLException { + checkClosed(); + + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.0"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableOutputStream bytesOut = new WatchableOutputStream(); + bytesOut.setWatcher(this); + + if (indexToWriteAt > 0) { + bytesOut.write(this.binaryData, 0, (int) (indexToWriteAt - 1)); + } + + return bytesOut; + } + + /** + * @see Blob#setBytes(long, byte[]) + */ + public synchronized int setBytes(long writeAt, byte[] bytes) throws SQLException { + checkClosed(); + + return setBytes(writeAt, bytes, 0, bytes.length); + } + + /** + * @see Blob#setBytes(long, byte[], int, int) + */ + public synchronized int setBytes(long writeAt, byte[] bytes, int offset, int length) throws SQLException { + checkClosed(); + + OutputStream bytesOut = setBinaryStream(writeAt); + + try { + bytesOut.write(bytes, offset, length); + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Blob.1"), SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + sqlEx.initCause(ioEx); + + throw sqlEx; + } finally { + try { + bytesOut.close(); + } catch (IOException doNothing) { + // do nothing + } + } + + return length; + } + + /** + * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[]) + */ + public synchronized void streamClosed(byte[] byteData) { + this.binaryData = byteData; + } + + /** + * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[]) + */ + public synchronized void streamClosed(WatchableOutputStream out) { + int streamSize = out.size(); + + if (streamSize < this.binaryData.length) { + out.write(this.binaryData, streamSize, this.binaryData.length - streamSize); + } + + this.binaryData = out.toByteArray(); + } + + /** + * Truncates the BLOB value that this Blob object represents to be len bytes in length. + *

+ * Note: If the value specified for len is greater then the length+1 of the BLOB value then the behavior is undefined. Some + * JDBC drivers may throw a SQLException while other drivers may support this operation. + * + * @param len + * the length, in bytes, to which the BLOB value + * that this Blob object represents should be truncated + * @exception SQLException + * if there is an error accessing the BLOB value or if len is less than 0 + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.4 + */ + public synchronized void truncate(long len) throws SQLException { + checkClosed(); + + if (len < 0) { + throw SQLError.createSQLException("\"len\" argument can not be < 1.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (len > this.binaryData.length) { + throw SQLError.createSQLException("\"len\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + // TODO: Do this without copying byte[]s by maintaining some end pointer on the original data + + byte[] newData = new byte[(int) len]; + System.arraycopy(getBinaryData(), 0, newData, 0, (int) len); + this.binaryData = newData; + } + + /** + * This method frees the Blob object and releases the resources that + * it holds. The object is invalid once the free method is called. + *

+ * After free has been called, any attempt to invoke a method other than free will result in a SQLException being + * thrown. If free is called multiple times, the subsequent calls to free are treated as a no-op. + *

+ * + * @throws SQLException + * if an error occurs releasing + * the Blob's resources + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.6 + */ + + public synchronized void free() throws SQLException { + this.binaryData = null; + this.isClosed = true; + } + + /** + * Returns an InputStream object that contains a partial Blob value, + * starting with the byte specified by pos, which is length bytes in length. + * + * @param pos + * the offset to the first byte of the partial value to be retrieved. + * The first byte in the Blob is at position 1 + * @param length + * the length in bytes of the partial value to be retrieved + * @return InputStream through which the partial Blob value can be read. + * @throws SQLException + * if pos is less than 1 or if pos is greater than the number of bytes + * in the Blob or if pos + length is greater than the number of bytes + * in the Blob + * + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.6 + */ + public synchronized InputStream getBinaryStream(long pos, long length) throws SQLException { + checkClosed(); + + if (pos < 1) { + throw SQLError.createSQLException("\"pos\" argument can not be < 1.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + pos--; + + if (pos > this.binaryData.length) { + throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + if (pos + length > this.binaryData.length) { + throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + return new ByteArrayInputStream(getBinaryData(), (int) pos, (int) length); + } + + private synchronized void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException("Invalid operation on closed BLOB", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BlobFromLocator.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BlobFromLocator.java new file mode 100644 index 0000000..70a1237 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BlobFromLocator.java @@ -0,0 +1,664 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * The representation (mapping) in the JavaTM programming language of an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object + * as a column value in a row of a database table. The driver implements Blob using an SQL locator(BLOB), which means that a Blob object contains a logical + * pointer to the SQL BLOB data rather than the data itself. A Blob object is valid for the duration of the transaction in which is was created. Methods in + * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob + * interface provides methods for getting the length of an SQL BLOB (Binary Large Object) value, for materializing a BLOB value on the client, and for + * determining the position of a pattern of bytes within a BLOB value. This class is new in the JDBC 2.0 API. + */ +public class BlobFromLocator implements java.sql.Blob { + private List primaryKeyColumns = null; + + private List primaryKeyValues = null; + + /** The ResultSet that created this BLOB */ + private ResultSetImpl creatorResultSet; + + private String blobColumnName = null; + + private String tableName = null; + + private int numColsInResultSet = 0; + + private int numPrimaryKeys = 0; + + private String quotedId; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates an updatable BLOB that can update in-place + */ + BlobFromLocator(ResultSetImpl creatorResultSetToSet, int blobColumnIndex, ExceptionInterceptor exceptionInterceptor) throws SQLException { + this.exceptionInterceptor = exceptionInterceptor; + this.creatorResultSet = creatorResultSetToSet; + + this.numColsInResultSet = this.creatorResultSet.fields.length; + this.quotedId = this.creatorResultSet.connection.getMetaData().getIdentifierQuoteString(); + + if (this.numColsInResultSet > 1) { + this.primaryKeyColumns = new ArrayList(); + this.primaryKeyValues = new ArrayList(); + + for (int i = 0; i < this.numColsInResultSet; i++) { + if (this.creatorResultSet.fields[i].isPrimaryKey()) { + StringBuilder keyName = new StringBuilder(); + keyName.append(this.quotedId); + + String originalColumnName = this.creatorResultSet.fields[i].getOriginalName(); + + if ((originalColumnName != null) && (originalColumnName.length() > 0)) { + keyName.append(originalColumnName); + } else { + keyName.append(this.creatorResultSet.fields[i].getName()); + } + + keyName.append(this.quotedId); + + this.primaryKeyColumns.add(keyName.toString()); + this.primaryKeyValues.add(this.creatorResultSet.getString(i + 1)); + } + } + } else { + notEnoughInformationInQuery(); + } + + this.numPrimaryKeys = this.primaryKeyColumns.size(); + + if (this.numPrimaryKeys == 0) { + notEnoughInformationInQuery(); + } + + if (this.creatorResultSet.fields[0].getOriginalTableName() != null) { + StringBuilder tableNameBuffer = new StringBuilder(); + + String databaseName = this.creatorResultSet.fields[0].getDatabaseName(); + + if ((databaseName != null) && (databaseName.length() > 0)) { + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(databaseName); + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append('.'); + } + + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(this.creatorResultSet.fields[0].getOriginalTableName()); + tableNameBuffer.append(this.quotedId); + + this.tableName = tableNameBuffer.toString(); + } else { + StringBuilder tableNameBuffer = new StringBuilder(); + + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(this.creatorResultSet.fields[0].getTableName()); + tableNameBuffer.append(this.quotedId); + + this.tableName = tableNameBuffer.toString(); + } + + this.blobColumnName = this.quotedId + this.creatorResultSet.getString(blobColumnIndex) + this.quotedId; + } + + private void notEnoughInformationInQuery() throws SQLException { + throw SQLError.createSQLException("Emulated BLOB locators must come from a ResultSet with only one table selected, and all primary keys selected", + SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + + /** + * @see Blob#setBinaryStream(long) + */ + public OutputStream setBinaryStream(long indexToWriteAt) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Retrieves the BLOB designated by this Blob instance as a stream. + * + * @return this BLOB represented as a binary stream of bytes. + * + * @throws SQLException + * if a database error occurs + */ + public java.io.InputStream getBinaryStream() throws SQLException { + // TODO: Make fetch size configurable + return new BufferedInputStream(new LocatorInputStream(), this.creatorResultSet.connection.getLocatorFetchBufferSize()); + } + + /** + * @see Blob#setBytes(long, byte[], int, int) + */ + public int setBytes(long writeAt, byte[] bytes, int offset, int length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + if ((offset + length) > bytes.length) { + length = bytes.length - offset; + } + + byte[] bytesToWrite = new byte[length]; + System.arraycopy(bytes, offset, bytesToWrite, 0, length); + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("UPDATE "); + query.append(this.tableName); + query.append(" SET "); + query.append(this.blobColumnName); + query.append(" = INSERT("); + query.append(this.blobColumnName); + query.append(", "); + query.append(writeAt); + query.append(", "); + query.append(length); + query.append(", ?) WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.connection.prepareStatement(query.toString()); + + pStmt.setBytes(1, bytesToWrite); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 2, this.primaryKeyValues.get(i)); + } + + int rowsUpdated = pStmt.executeUpdate(); + + if (rowsUpdated != 1) { + throw SQLError.createSQLException("BLOB data not found! Did primary keys change?", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + + return (int) length(); + } + + /** + * @see Blob#setBytes(long, byte[]) + */ + public int setBytes(long writeAt, byte[] bytes) throws SQLException { + return setBytes(writeAt, bytes, 0, bytes.length); + } + + /** + * Returns as an array of bytes, part or all of the BLOB value that this + * Blob object designates. + * + * @param pos + * where to start the part of the BLOB + * @param length + * the length of the part of the BLOB you want returned. + * + * @return the bytes stored in the blob starting at position pos and having a length of length. + * + * @throws SQLException + * if a database error occurs + */ + public byte[] getBytes(long pos, int length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + try { + + pStmt = createGetBytesStatement(); + + return getBytesInternal(pStmt, pos, length); + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * Returns the number of bytes in the BLOB value designated by this Blob + * object. + * + * @return the length of this blob + * + * @throws SQLException + * if a database error occurs + */ + public long length() throws SQLException { + java.sql.ResultSet blobRs = null; + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("SELECT LENGTH("); + query.append(this.blobColumnName); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.connection.prepareStatement(query.toString()); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 1, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return blobRs.getLong(1); + } + + throw SQLError.createSQLException("BLOB data not found! Did primary keys change?", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * Finds the position of the given pattern in this BLOB. + * + * @param pattern + * the pattern to find + * @param start + * where to start finding the pattern + * + * @return the position where the pattern is found in the BLOB, -1 if not + * found + * + * @throws SQLException + * if a database error occurs + */ + public long position(java.sql.Blob pattern, long start) throws SQLException { + return position(pattern.getBytes(0, (int) pattern.length()), start); + } + + /** + * @see java.sql.Blob#position(byte[], long) + */ + public long position(byte[] pattern, long start) throws SQLException { + java.sql.ResultSet blobRs = null; + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("SELECT LOCATE("); + query.append("?, "); + query.append(this.blobColumnName); + query.append(", "); + query.append(start); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.connection.prepareStatement(query.toString()); + pStmt.setBytes(1, pattern); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 2, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return blobRs.getLong(1); + } + + throw SQLError.createSQLException("BLOB data not found! Did primary keys change?", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * @see Blob#truncate(long) + */ + public void truncate(long length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("UPDATE "); + query.append(this.tableName); + query.append(" SET "); + query.append(this.blobColumnName); + query.append(" = LEFT("); + query.append(this.blobColumnName); + query.append(", "); + query.append(length); + query.append(") WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.connection.prepareStatement(query.toString()); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 1, this.primaryKeyValues.get(i)); + } + + int rowsUpdated = pStmt.executeUpdate(); + + if (rowsUpdated != 1) { + throw SQLError.createSQLException("BLOB data not found! Did primary keys change?", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + java.sql.PreparedStatement createGetBytesStatement() throws SQLException { + StringBuilder query = new StringBuilder("SELECT SUBSTRING("); + + query.append(this.blobColumnName); + query.append(", "); + query.append("?"); + query.append(", "); + query.append("?"); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + return this.creatorResultSet.connection.prepareStatement(query.toString()); + } + + byte[] getBytesInternal(java.sql.PreparedStatement pStmt, long pos, int length) throws SQLException { + + java.sql.ResultSet blobRs = null; + + try { + + pStmt.setLong(1, pos); + pStmt.setInt(2, length); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 3, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return ((com.mysql.jdbc.ResultSetImpl) blobRs).getBytes(1, true); + } + + throw SQLError.createSQLException("BLOB data not found! Did primary keys change?", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + } + } + + class LocatorInputStream extends InputStream { + long currentPositionInBlob = 0; + + long length = 0; + + java.sql.PreparedStatement pStmt = null; + + LocatorInputStream() throws SQLException { + this.length = length(); + this.pStmt = createGetBytesStatement(); + } + + @SuppressWarnings("synthetic-access") + LocatorInputStream(long pos, long len) throws SQLException { + this.length = pos + len; + this.currentPositionInBlob = pos; + long blobLength = length(); + + if (pos + len > blobLength) { + throw SQLError.createSQLException( + Messages.getString("Blob.invalidStreamLength", new Object[] { Long.valueOf(blobLength), Long.valueOf(pos), Long.valueOf(len) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, BlobFromLocator.this.exceptionInterceptor); + } + + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.invalidStreamPos"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + BlobFromLocator.this.exceptionInterceptor); + } + + if (pos > blobLength) { + throw SQLError.createSQLException(Messages.getString("Blob.invalidStreamPos"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + BlobFromLocator.this.exceptionInterceptor); + } + } + + @Override + public int read() throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob++) + 1, 1); + + if (asBytes == null) { + return -1; + } + + return asBytes[0]; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob) + 1, len); + + if (asBytes == null) { + return -1; + } + + System.arraycopy(asBytes, 0, b, off, asBytes.length); + + this.currentPositionInBlob += asBytes.length; + + return asBytes.length; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[]) + */ + @Override + public int read(byte[] b) throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob) + 1, b.length); + + if (asBytes == null) { + return -1; + } + + System.arraycopy(asBytes, 0, b, 0, asBytes.length); + + this.currentPositionInBlob += asBytes.length; + + return asBytes.length; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + if (this.pStmt != null) { + try { + this.pStmt.close(); + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + super.close(); + } + } + + public void free() throws SQLException { + this.creatorResultSet = null; + this.primaryKeyColumns = null; + this.primaryKeyValues = null; + } + + public InputStream getBinaryStream(long pos, long length) throws SQLException { + return new LocatorInputStream(pos, length); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Buffer.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Buffer.java new file mode 100644 index 0000000..f1b58a5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Buffer.java @@ -0,0 +1,677 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.sql.SQLException; + +/** + * Buffer contains code to read and write packets from/to the MySQL server. + */ +public class Buffer { + static final int MAX_BYTES_TO_DUMP = 512; + + static final int NO_LENGTH_LIMIT = -1; + + static final long NULL_LENGTH = -1; + + private int bufLength = 0; + + private byte[] byteBuffer; + + private int position = 0; + + protected boolean wasMultiPacket = false; + + /* Type ids of response packets. */ + public static final short TYPE_ID_ERROR = 0xFF; + public static final short TYPE_ID_EOF = 0xFE; + /** It has the same signature as EOF, but may be issued by server only during handshake phase **/ + public static final short TYPE_ID_AUTH_SWITCH = 0xFE; + public static final short TYPE_ID_LOCAL_INFILE = 0xFB; + public static final short TYPE_ID_OK = 0; + + public Buffer(byte[] buf) { + this.byteBuffer = buf; + setBufLength(buf.length); + } + + Buffer(int size) { + this.byteBuffer = new byte[size]; + setBufLength(this.byteBuffer.length); + this.position = MysqlIO.HEADER_LENGTH; + } + + final void clear() { + this.position = MysqlIO.HEADER_LENGTH; + } + + final void dump() { + dump(getBufLength()); + } + + final String dump(int numBytes) { + return StringUtils.dumpAsHex(getBytes(0, numBytes > getBufLength() ? getBufLength() : numBytes), numBytes > getBufLength() ? getBufLength() : numBytes); + } + + final String dumpClampedBytes(int numBytes) { + int numBytesToDump = numBytes < MAX_BYTES_TO_DUMP ? numBytes : MAX_BYTES_TO_DUMP; + + String dumped = StringUtils.dumpAsHex(getBytes(0, numBytesToDump > getBufLength() ? getBufLength() : numBytesToDump), + numBytesToDump > getBufLength() ? getBufLength() : numBytesToDump); + + if (numBytesToDump < numBytes) { + return dumped + " ....(packet exceeds max. dump length)"; + } + + return dumped; + } + + final void dumpHeader() { + for (int i = 0; i < MysqlIO.HEADER_LENGTH; i++) { + String hexVal = Integer.toHexString(readByte(i) & 0xff); + + if (hexVal.length() == 1) { + hexVal = "0" + hexVal; + } + + System.out.print(hexVal + " "); + } + } + + final void dumpNBytes(int start, int nBytes) { + StringBuilder asciiBuf = new StringBuilder(); + + for (int i = start; (i < (start + nBytes)) && (i < getBufLength()); i++) { + String hexVal = Integer.toHexString(readByte(i) & 0xff); + + if (hexVal.length() == 1) { + hexVal = "0" + hexVal; + } + + System.out.print(hexVal + " "); + + if ((readByte(i) > 32) && (readByte(i) < 127)) { + asciiBuf.append((char) readByte(i)); + } else { + asciiBuf.append("."); + } + + asciiBuf.append(" "); + } + + System.out.println(" " + asciiBuf.toString()); + } + + final void ensureCapacity(int additionalData) throws SQLException { + if ((this.position + additionalData) > getBufLength()) { + if ((this.position + additionalData) < this.byteBuffer.length) { + // byteBuffer.length is != getBufLength() all of the time due to re-using of packets (we don't shrink them) + // + // If we can, don't re-alloc, just set buffer length to size of current buffer + setBufLength(this.byteBuffer.length); + } else { + // + // Otherwise, re-size, and pad so we can avoid allocing again in the near future + // + int newLength = (int) (this.byteBuffer.length * 1.25); + + if (newLength < (this.byteBuffer.length + additionalData)) { + newLength = this.byteBuffer.length + (int) (additionalData * 1.25); + } + + if (newLength < this.byteBuffer.length) { + newLength = this.byteBuffer.length + additionalData; + } + + byte[] newBytes = new byte[newLength]; + + System.arraycopy(this.byteBuffer, 0, newBytes, 0, this.byteBuffer.length); + this.byteBuffer = newBytes; + setBufLength(this.byteBuffer.length); + } + } + } + + /** + * Skip over a length-encoded string + * + * @return The position past the end of the string + */ + public int fastSkipLenString() { + long len = this.readFieldLength(); + + this.position += len; + + return (int) len; + } + + public void fastSkipLenByteArray() { + long len = this.readFieldLength(); + + if (len == NULL_LENGTH || len == 0) { + return; + } + + this.position += len; + } + + protected final byte[] getBufferSource() { + return this.byteBuffer; + } + + public int getBufLength() { + return this.bufLength; + } + + /** + * Returns the array of bytes this Buffer is using to read from. + * + * @return byte array being read from + */ + public byte[] getByteBuffer() { + return this.byteBuffer; + } + + final byte[] getBytes(int len) { + byte[] b = new byte[len]; + System.arraycopy(this.byteBuffer, this.position, b, 0, len); + this.position += len; // update cursor + + return b; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.Buffer#getBytes(int, int) + */ + byte[] getBytes(int offset, int len) { + byte[] dest = new byte[len]; + System.arraycopy(this.byteBuffer, offset, dest, 0, len); + + return dest; + } + + int getCapacity() { + return this.byteBuffer.length; + } + + public ByteBuffer getNioBuffer() { + throw new IllegalArgumentException(Messages.getString("ByteArrayBuffer.0")); + } + + /** + * Returns the current position to write to/ read from + * + * @return the current position to write to/ read from + */ + public int getPosition() { + return this.position; + } + + final boolean isEOFPacket() { + return (this.byteBuffer[0] & 0xff) == TYPE_ID_EOF && (getBufLength() <= 5); + } + + final boolean isAuthMethodSwitchRequestPacket() { + return (this.byteBuffer[0] & 0xff) == TYPE_ID_AUTH_SWITCH; + } + + final boolean isOKPacket() { + return (this.byteBuffer[0] & 0xff) == TYPE_ID_OK; + } + + final boolean isResultSetOKPacket() { + return (this.byteBuffer[0] & 0xff) == TYPE_ID_EOF && (getBufLength() < 16777215); + } + + final boolean isRawPacket() { + return ((this.byteBuffer[0] & 0xff) == 1); + } + + final long newReadLength() { + int sw = this.byteBuffer[this.position++] & 0xff; + + switch (sw) { + case 251: + return 0; + + case 252: + return readInt(); + + case 253: + return readLongInt(); + + case 254: // changed for 64 bit lengths + return readLongLong(); + + default: + return sw; + } + } + + final byte readByte() { + return this.byteBuffer[this.position++]; + } + + final byte readByte(int readAt) { + return this.byteBuffer[readAt]; + } + + final long readFieldLength() { + int sw = this.byteBuffer[this.position++] & 0xff; + + switch (sw) { + case 251: + return NULL_LENGTH; + + case 252: + return readInt(); + + case 253: + return readLongInt(); + + case 254: + return readLongLong(); + + default: + return sw; + } + } + + final int readInt() { + byte[] b = this.byteBuffer; // a little bit optimization + + return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8); + } + + final int readIntAsLong() { + byte[] b = this.byteBuffer; + + return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8) | ((b[this.position++] & 0xff) << 16) | ((b[this.position++] & 0xff) << 24); + } + + final byte[] readLenByteArray(int offset) { + long len = this.readFieldLength(); + + if (len == NULL_LENGTH) { + return null; + } + + if (len == 0) { + return Constants.EMPTY_BYTE_ARRAY; + } + + this.position += offset; + + return getBytes((int) len); + } + + final long readLength() { + int sw = this.byteBuffer[this.position++] & 0xff; + + switch (sw) { + case 251: + return 0; + + case 252: + return readInt(); + + case 253: + return readLongInt(); + + case 254: + return readLong(); + + default: + return sw; + } + } + + final long readLong() { + byte[] b = this.byteBuffer; + + return ((long) b[this.position++] & 0xff) | (((long) b[this.position++] & 0xff) << 8) | ((long) (b[this.position++] & 0xff) << 16) + | ((long) (b[this.position++] & 0xff) << 24); + } + + final int readLongInt() { + byte[] b = this.byteBuffer; + + return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8) | ((b[this.position++] & 0xff) << 16); + } + + final long readLongLong() { + byte[] b = this.byteBuffer; + + return (b[this.position++] & 0xff) | ((long) (b[this.position++] & 0xff) << 8) | ((long) (b[this.position++] & 0xff) << 16) + | ((long) (b[this.position++] & 0xff) << 24) | ((long) (b[this.position++] & 0xff) << 32) | ((long) (b[this.position++] & 0xff) << 40) + | ((long) (b[this.position++] & 0xff) << 48) | ((long) (b[this.position++] & 0xff) << 56); + } + + final int readnBytes() { + int sw = this.byteBuffer[this.position++] & 0xff; + + switch (sw) { + case 1: + return this.byteBuffer[this.position++] & 0xff; + + case 2: + return this.readInt(); + + case 3: + return this.readLongInt(); + + case 4: + return (int) this.readLong(); + + default: + return 255; + } + } + + // + // Read a null-terminated string + // + // To avoid alloc'ing a new byte array, we do this by hand, rather than calling getNullTerminatedBytes() + // + public final String readString() { + int i = this.position; + int len = 0; + int maxLen = getBufLength(); + + while ((i < maxLen) && (this.byteBuffer[i] != 0)) { + len++; + i++; + } + + String s = StringUtils.toString(this.byteBuffer, this.position, len); + this.position += (len + 1); // update cursor + + return s; + } + + /** + * Read string[NUL] + * + * @param encoding + * @param exceptionInterceptor + * @throws SQLException + */ + final String readString(String encoding, ExceptionInterceptor exceptionInterceptor) throws SQLException { + int i = this.position; + int len = 0; + int maxLen = getBufLength(); + + while ((i < maxLen) && (this.byteBuffer[i] != 0)) { + len++; + i++; + } + + try { + return StringUtils.toString(this.byteBuffer, this.position, len, encoding); + } catch (UnsupportedEncodingException uEE) { + throw SQLError.createSQLException(Messages.getString("ByteArrayBuffer.1") + encoding + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + exceptionInterceptor); + } finally { + this.position += (len + 1); // update cursor + } + } + + /** + * Read string[$len] + */ + final String readString(String encoding, ExceptionInterceptor exceptionInterceptor, int expectedLength) throws SQLException { + if (this.position + expectedLength > getBufLength()) { + throw SQLError.createSQLException(Messages.getString("ByteArrayBuffer.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + try { + return StringUtils.toString(this.byteBuffer, this.position, expectedLength, encoding); + } catch (UnsupportedEncodingException uEE) { + throw SQLError.createSQLException(Messages.getString("ByteArrayBuffer.1") + encoding + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + exceptionInterceptor); + } finally { + this.position += expectedLength; // update cursor + } + } + + public void setBufLength(int bufLengthToSet) { + this.bufLength = bufLengthToSet; + } + + /** + * Sets the array of bytes to use as a buffer to read from. + * + * @param byteBuffer + * the array of bytes to use as a buffer + */ + public void setByteBuffer(byte[] byteBufferToSet) { + this.byteBuffer = byteBufferToSet; + } + + /** + * Set the current position to write to/ read from + * + * @param position + * the position (0-based index) + */ + public void setPosition(int positionToSet) { + this.position = positionToSet; + } + + /** + * Sets whether this packet was part of a multipacket + * + * @param flag + * was this packet part of a multipacket? + */ + public void setWasMultiPacket(boolean flag) { + this.wasMultiPacket = flag; + } + + @Override + public String toString() { + return dumpClampedBytes(getPosition()); + } + + public String toSuperString() { + return super.toString(); + } + + /** + * Was this packet part of a multipacket? + * + * @return was this packet part of a multipacket? + */ + public boolean wasMultiPacket() { + return this.wasMultiPacket; + } + + public final void writeByte(byte b) throws SQLException { + ensureCapacity(1); + + this.byteBuffer[this.position++] = b; + } + + // Write a byte array + public final void writeBytesNoNull(byte[] bytes) throws SQLException { + int len = bytes.length; + ensureCapacity(len); + System.arraycopy(bytes, 0, this.byteBuffer, this.position, len); + this.position += len; + } + + // Write a byte array with the given offset and length + final void writeBytesNoNull(byte[] bytes, int offset, int length) throws SQLException { + ensureCapacity(length); + System.arraycopy(bytes, offset, this.byteBuffer, this.position, length); + this.position += length; + } + + final void writeDouble(double d) throws SQLException { + long l = Double.doubleToLongBits(d); + writeLongLong(l); + } + + final void writeFieldLength(long length) throws SQLException { + if (length < 251) { + writeByte((byte) length); + } else if (length < 65536L) { + ensureCapacity(3); + writeByte((byte) 252); + writeInt((int) length); + } else if (length < 16777216L) { + ensureCapacity(4); + writeByte((byte) 253); + writeLongInt((int) length); + } else { + ensureCapacity(9); + writeByte((byte) 254); + writeLongLong(length); + } + } + + final void writeFloat(float f) throws SQLException { + ensureCapacity(4); + + int i = Float.floatToIntBits(f); + byte[] b = this.byteBuffer; + b[this.position++] = (byte) (i & 0xff); + b[this.position++] = (byte) (i >>> 8); + b[this.position++] = (byte) (i >>> 16); + b[this.position++] = (byte) (i >>> 24); + } + + final void writeInt(int i) throws SQLException { + ensureCapacity(2); + + byte[] b = this.byteBuffer; + b[this.position++] = (byte) (i & 0xff); + b[this.position++] = (byte) (i >>> 8); + } + + // Write a String using the specified character encoding + final void writeLenBytes(byte[] b) throws SQLException { + int len = b.length; + ensureCapacity(len + 9); + writeFieldLength(len); + System.arraycopy(b, 0, this.byteBuffer, this.position, len); + this.position += len; + } + + // Write a String using the specified character encoding + final void writeLenString(String s, String encoding, String serverEncoding, SingleByteCharsetConverter converter, boolean parserKnowsUnicode, + MySQLConnection conn) throws UnsupportedEncodingException, SQLException { + byte[] b = null; + + if (converter != null) { + b = converter.toBytes(s); + } else { + b = StringUtils.getBytes(s, encoding, serverEncoding, parserKnowsUnicode, conn, conn.getExceptionInterceptor()); + } + + int len = b.length; + ensureCapacity(len + 9); + writeFieldLength(len); + System.arraycopy(b, 0, this.byteBuffer, this.position, len); + this.position += len; + } + + final void writeLong(long i) throws SQLException { + ensureCapacity(4); + + byte[] b = this.byteBuffer; + b[this.position++] = (byte) (i & 0xff); + b[this.position++] = (byte) (i >>> 8); + b[this.position++] = (byte) (i >>> 16); + b[this.position++] = (byte) (i >>> 24); + } + + final void writeLongInt(int i) throws SQLException { + ensureCapacity(3); + byte[] b = this.byteBuffer; + b[this.position++] = (byte) (i & 0xff); + b[this.position++] = (byte) (i >>> 8); + b[this.position++] = (byte) (i >>> 16); + } + + final void writeLongLong(long i) throws SQLException { + ensureCapacity(8); + byte[] b = this.byteBuffer; + b[this.position++] = (byte) (i & 0xff); + b[this.position++] = (byte) (i >>> 8); + b[this.position++] = (byte) (i >>> 16); + b[this.position++] = (byte) (i >>> 24); + b[this.position++] = (byte) (i >>> 32); + b[this.position++] = (byte) (i >>> 40); + b[this.position++] = (byte) (i >>> 48); + b[this.position++] = (byte) (i >>> 56); + } + + // Write null-terminated string + final void writeString(String s) throws SQLException { + ensureCapacity((s.length() * 3) + 1); + writeStringNoNull(s); + this.byteBuffer[this.position++] = 0; + } + + // Write null-terminated string in the given encoding + final void writeString(String s, String encoding, MySQLConnection conn) throws SQLException { + ensureCapacity((s.length() * 3) + 1); + try { + writeStringNoNull(s, encoding, encoding, false, conn); + } catch (UnsupportedEncodingException ue) { + throw new SQLException(ue.toString(), SQLError.SQL_STATE_GENERAL_ERROR); + } + + this.byteBuffer[this.position++] = 0; + } + + // Write string, with no termination + final void writeStringNoNull(String s) throws SQLException { + int len = s.length(); + ensureCapacity(len * 3); + System.arraycopy(StringUtils.getBytes(s), 0, this.byteBuffer, this.position, len); + this.position += len; + + // for (int i = 0; i < len; i++) + // { + // this.byteBuffer[this.position++] = (byte)s.charAt(i); + // } + } + + // Write a String using the specified character encoding + final void writeStringNoNull(String s, String encoding, String serverEncoding, boolean parserKnowsUnicode, MySQLConnection conn) + throws UnsupportedEncodingException, SQLException { + byte[] b = StringUtils.getBytes(s, encoding, serverEncoding, parserKnowsUnicode, conn, conn.getExceptionInterceptor()); + + int len = b.length; + ensureCapacity(len); + System.arraycopy(b, 0, this.byteBuffer, this.position, len); + this.position += len; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BufferRow.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BufferRow.java new file mode 100644 index 0000000..5310d77 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/BufferRow.java @@ -0,0 +1,748 @@ +/* + Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TimeZone; + +/** + * A RowHolder implementation that holds one row packet (which is re-used by the driver, and thus saves memory allocations), and tries when possible to avoid + * allocations to break out the results as individual byte[]s. + * + * (this isn't possible when doing things like reading floating point values). + */ +public class BufferRow extends ResultSetRow { + private Buffer rowFromServer; + + /** + * The beginning of the row packet + */ + private int homePosition = 0; + + /** + * The home position before the is-null bitmask for server-side prepared statement result sets + */ + private int preNullBitmaskHomePosition = 0; + + /** + * The last-requested index, used as an optimization, if you ask for the same index, we won't seek to find it. If you ask for an index that is > + * than the last one requested, we start seeking from the last requested index. + */ + private int lastRequestedIndex = -1; + + /** + * The position of the last-requested index, optimization in concert with lastRequestedIndex. + */ + private int lastRequestedPos; + + /** + * The metadata of the fields of this result set. + */ + private Field[] metadata; + + /** + * Is this a row from a server-side prepared statement? If so, they're encoded differently, so we have different ways of finding where each column is, and + * unpacking them. + */ + private boolean isBinaryEncoded; + + /** + * If binary-encoded, the NULL status of each column is at the beginning of the row, so we + */ + private boolean[] isNull; + + private List openStreams; + + public BufferRow(Buffer buf, Field[] fields, boolean isBinaryEncoded, ExceptionInterceptor exceptionInterceptor) throws SQLException { + super(exceptionInterceptor); + + this.rowFromServer = buf; + this.metadata = fields; + this.isBinaryEncoded = isBinaryEncoded; + this.homePosition = this.rowFromServer.getPosition(); + this.preNullBitmaskHomePosition = this.homePosition; + + if (fields != null) { + setMetadata(fields); + } + } + + @Override + public synchronized void closeOpenStreams() { + if (this.openStreams != null) { + // This would've looked slicker in a "for" loop but we want to skip over streams that fail to close (they probably won't ever) to be more robust and + // close everything we _can_ + + Iterator iter = this.openStreams.iterator(); + + while (iter.hasNext()) { + + try { + iter.next().close(); + } catch (IOException e) { + // ignore - it can't really happen in this case + } + } + + this.openStreams.clear(); + } + } + + private int findAndSeekToOffset(int index) throws SQLException { + if (!this.isBinaryEncoded) { + + if (index == 0) { + this.lastRequestedIndex = 0; + this.lastRequestedPos = this.homePosition; + this.rowFromServer.setPosition(this.homePosition); + + return 0; + } + + if (index == this.lastRequestedIndex) { + this.rowFromServer.setPosition(this.lastRequestedPos); + + return this.lastRequestedPos; + } + + int startingIndex = 0; + + if (index > this.lastRequestedIndex) { + if (this.lastRequestedIndex >= 0) { + startingIndex = this.lastRequestedIndex; + } else { + startingIndex = 0; + } + + this.rowFromServer.setPosition(this.lastRequestedPos); + } else { + this.rowFromServer.setPosition(this.homePosition); + } + + for (int i = startingIndex; i < index; i++) { + this.rowFromServer.fastSkipLenByteArray(); + } + + this.lastRequestedIndex = index; + this.lastRequestedPos = this.rowFromServer.getPosition(); + + return this.lastRequestedPos; + } + + return findAndSeekToOffsetForBinaryEncoding(index); + } + + private int findAndSeekToOffsetForBinaryEncoding(int index) throws SQLException { + if (index == 0) { + this.lastRequestedIndex = 0; + this.lastRequestedPos = this.homePosition; + this.rowFromServer.setPosition(this.homePosition); + + return 0; + } + + if (index == this.lastRequestedIndex) { + this.rowFromServer.setPosition(this.lastRequestedPos); + + return this.lastRequestedPos; + } + + int startingIndex = 0; + + if (index > this.lastRequestedIndex) { + if (this.lastRequestedIndex >= 0) { + startingIndex = this.lastRequestedIndex; + } else { + // First-time "scan" + startingIndex = 0; + this.lastRequestedPos = this.homePosition; + } + + this.rowFromServer.setPosition(this.lastRequestedPos); + } else { + this.rowFromServer.setPosition(this.homePosition); + } + + for (int i = startingIndex; i < index; i++) { + if (this.isNull[i]) { + continue; + } + + int curPosition = this.rowFromServer.getPosition(); + + switch (this.metadata[i].getMysqlType()) { + case MysqlDefs.FIELD_TYPE_NULL: + break; // for dummy binds + + case MysqlDefs.FIELD_TYPE_TINY: + + this.rowFromServer.setPosition(curPosition + 1); + break; + + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + this.rowFromServer.setPosition(curPosition + 2); + + break; + case MysqlDefs.FIELD_TYPE_LONG: + case MysqlDefs.FIELD_TYPE_INT24: + this.rowFromServer.setPosition(curPosition + 4); + + break; + case MysqlDefs.FIELD_TYPE_LONGLONG: + this.rowFromServer.setPosition(curPosition + 8); + + break; + case MysqlDefs.FIELD_TYPE_FLOAT: + this.rowFromServer.setPosition(curPosition + 4); + + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + this.rowFromServer.setPosition(curPosition + 8); + + break; + case MysqlDefs.FIELD_TYPE_TIME: + this.rowFromServer.fastSkipLenByteArray(); + + break; + case MysqlDefs.FIELD_TYPE_DATE: + + this.rowFromServer.fastSkipLenByteArray(); + + break; + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + this.rowFromServer.fastSkipLenByteArray(); + + break; + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + case MysqlDefs.FIELD_TYPE_GEOMETRY: + case MysqlDefs.FIELD_TYPE_BIT: + case MysqlDefs.FIELD_TYPE_JSON: + this.rowFromServer.fastSkipLenByteArray(); + + break; + + default: + throw SQLError.createSQLException( + Messages.getString("MysqlIO.97") + this.metadata[i].getMysqlType() + Messages.getString("MysqlIO.98") + (i + 1) + + Messages.getString("MysqlIO.99") + this.metadata.length + Messages.getString("MysqlIO.100"), + SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } + + this.lastRequestedIndex = index; + this.lastRequestedPos = this.rowFromServer.getPosition(); + + return this.lastRequestedPos; + } + + @Override + public synchronized InputStream getBinaryInputStream(int columnIndex) throws SQLException { + if (this.isBinaryEncoded) { + if (isNull(columnIndex)) { + return null; + } + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + if (length == Buffer.NULL_LENGTH) { + return null; + } + + InputStream stream = new ByteArrayInputStream(this.rowFromServer.getByteBuffer(), offset, (int) length); + + if (this.openStreams == null) { + this.openStreams = new LinkedList(); + } + + return stream; + } + + @Override + public byte[] getColumnValue(int index) throws SQLException { + findAndSeekToOffset(index); + + if (!this.isBinaryEncoded) { + return this.rowFromServer.readLenByteArray(0); + } + + if (this.isNull[index]) { + return null; + } + + switch (this.metadata[index].getMysqlType()) { + case MysqlDefs.FIELD_TYPE_NULL: + return null; + + case MysqlDefs.FIELD_TYPE_TINY: + return new byte[] { this.rowFromServer.readByte() }; + + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + return this.rowFromServer.getBytes(2); + + case MysqlDefs.FIELD_TYPE_LONG: + case MysqlDefs.FIELD_TYPE_INT24: + return this.rowFromServer.getBytes(4); + + case MysqlDefs.FIELD_TYPE_LONGLONG: + return this.rowFromServer.getBytes(8); + + case MysqlDefs.FIELD_TYPE_FLOAT: + return this.rowFromServer.getBytes(4); + + case MysqlDefs.FIELD_TYPE_DOUBLE: + return this.rowFromServer.getBytes(8); + + case MysqlDefs.FIELD_TYPE_TIME: + case MysqlDefs.FIELD_TYPE_DATE: + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + case MysqlDefs.FIELD_TYPE_GEOMETRY: + case MysqlDefs.FIELD_TYPE_BIT: + case MysqlDefs.FIELD_TYPE_JSON: + return this.rowFromServer.readLenByteArray(0); + + default: + throw SQLError.createSQLException( + Messages.getString("MysqlIO.97") + this.metadata[index].getMysqlType() + Messages.getString("MysqlIO.98") + (index + 1) + + Messages.getString("MysqlIO.99") + this.metadata.length + Messages.getString("MysqlIO.100"), + SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } + + @Override + public int getInt(int columnIndex) throws SQLException { + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + if (length == Buffer.NULL_LENGTH) { + return 0; + } + + return StringUtils.getInt(this.rowFromServer.getByteBuffer(), offset, offset + (int) length); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + if (length == Buffer.NULL_LENGTH) { + return 0; + } + + return StringUtils.getLong(this.rowFromServer.getByteBuffer(), offset, offset + (int) length); + } + + @Override + public double getNativeDouble(int columnIndex) throws SQLException { + if (isNull(columnIndex)) { + return 0; + } + + findAndSeekToOffset(columnIndex); + + int offset = this.rowFromServer.getPosition(); + + return getNativeDouble(this.rowFromServer.getByteBuffer(), offset); + } + + @Override + public float getNativeFloat(int columnIndex) throws SQLException { + if (isNull(columnIndex)) { + return 0; + } + + findAndSeekToOffset(columnIndex); + + int offset = this.rowFromServer.getPosition(); + + return getNativeFloat(this.rowFromServer.getByteBuffer(), offset); + } + + @Override + public int getNativeInt(int columnIndex) throws SQLException { + if (isNull(columnIndex)) { + return 0; + } + + findAndSeekToOffset(columnIndex); + + int offset = this.rowFromServer.getPosition(); + + return getNativeInt(this.rowFromServer.getByteBuffer(), offset); + } + + @Override + public long getNativeLong(int columnIndex) throws SQLException { + if (isNull(columnIndex)) { + return 0; + } + + findAndSeekToOffset(columnIndex); + + int offset = this.rowFromServer.getPosition(); + + return getNativeLong(this.rowFromServer.getByteBuffer(), offset); + } + + @Override + public short getNativeShort(int columnIndex) throws SQLException { + if (isNull(columnIndex)) { + return 0; + } + + findAndSeekToOffset(columnIndex); + + int offset = this.rowFromServer.getPosition(); + + return getNativeShort(this.rowFromServer.getByteBuffer(), offset); + } + + @Override + public Timestamp getNativeTimestamp(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getNativeTimestamp(this.rowFromServer.getByteBuffer(), offset, (int) length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public Reader getReader(int columnIndex) throws SQLException { + InputStream stream = getBinaryInputStream(columnIndex); + + if (stream == null) { + return null; + } + + try { + return new InputStreamReader(stream, this.metadata[columnIndex].getEncoding()); + } catch (UnsupportedEncodingException e) { + SQLException sqlEx = SQLError.createSQLException("", this.exceptionInterceptor); + + sqlEx.initCause(e); + + throw sqlEx; + } + } + + @Override + public String getString(int columnIndex, String encoding, MySQLConnection conn) throws SQLException { + if (this.isBinaryEncoded) { + if (isNull(columnIndex)) { + return null; + } + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + if (length == Buffer.NULL_LENGTH) { + return null; + } + + if (length == 0) { + return ""; + } + + // TODO: I don't like this, would like to push functionality back to the buffer class somehow + + int offset = this.rowFromServer.getPosition(); + + return getString(encoding, conn, this.rowFromServer.getByteBuffer(), offset, (int) length); + } + + @Override + public Time getTimeFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getTimeFast(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getTimestampFast(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public boolean isFloatingPointNumber(int index) throws SQLException { + if (this.isBinaryEncoded) { + switch (this.metadata[index].getSQLType()) { + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + return true; + default: + return false; + } + } + + findAndSeekToOffset(index); + + long length = this.rowFromServer.readFieldLength(); + + if (length == Buffer.NULL_LENGTH) { + return false; + } + + if (length == 0) { + return false; + } + + int offset = this.rowFromServer.getPosition(); + byte[] buffer = this.rowFromServer.getByteBuffer(); + + for (int i = 0; i < (int) length; i++) { + char c = (char) buffer[offset + i]; + + if ((c == 'e') || (c == 'E')) { + return true; + } + } + + return false; + } + + @Override + public boolean isNull(int index) throws SQLException { + if (!this.isBinaryEncoded) { + findAndSeekToOffset(index); + + return this.rowFromServer.readFieldLength() == Buffer.NULL_LENGTH; + } + + return this.isNull[index]; + } + + @Override + public long length(int index) throws SQLException { + findAndSeekToOffset(index); + + long length = this.rowFromServer.readFieldLength(); + + if (length == Buffer.NULL_LENGTH) { + return 0; + } + + return length; + } + + @Override + public void setColumnValue(int index, byte[] value) throws SQLException { + throw new OperationNotSupportedException(); + } + + @Override + public ResultSetRow setMetadata(Field[] f) throws SQLException { + super.setMetadata(f); + + if (this.isBinaryEncoded) { + setupIsNullBitmask(); + } + + return this; + } + + /** + * Unpacks the bitmask at the head of the row packet that tells us what + * columns hold null values, and sets the "home" position directly after the + * bitmask. + */ + private void setupIsNullBitmask() throws SQLException { + if (this.isNull != null) { + return; // we've already done this + } + + this.rowFromServer.setPosition(this.preNullBitmaskHomePosition); + + int nullCount = (this.metadata.length + 9) / 8; + + byte[] nullBitMask = new byte[nullCount]; + + for (int i = 0; i < nullCount; i++) { + nullBitMask[i] = this.rowFromServer.readByte(); + } + + this.homePosition = this.rowFromServer.getPosition(); + + this.isNull = new boolean[this.metadata.length]; + + int nullMaskPos = 0; + int bit = 4; // first two bits are reserved for future use + + for (int i = 0; i < this.metadata.length; i++) { + + this.isNull[i] = ((nullBitMask[nullMaskPos] & bit) != 0); + + if (((bit <<= 1) & 255) == 0) { + bit = 1; /* To next byte */ + + nullMaskPos++; + } + } + } + + @Override + public Date getDateFast(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar targetCalendar) throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getDateFast(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, conn, rs, targetCalendar); + } + + @Override + public java.sql.Date getNativeDate(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar cal) throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getNativeDate(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, conn, rs, cal); + } + + @Override + public Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar, int jdbcType, int mysqlType, TimeZone tz, boolean rollForward, + MySQLConnection conn, ResultSetImpl rs) throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getNativeDateTimeValue(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, targetCalendar, jdbcType, mysqlType, tz, + rollForward, conn, rs); + } + + @Override + public Time getNativeTime(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + if (isNull(columnIndex)) { + return null; + } + + findAndSeekToOffset(columnIndex); + + long length = this.rowFromServer.readFieldLength(); + + int offset = this.rowFromServer.getPosition(); + + return getNativeTime(columnIndex, this.rowFromServer.getByteBuffer(), offset, (int) length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public int getBytesSize() { + return this.rowFromServer.getBufLength(); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ByteArrayRow.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ByteArrayRow.java new file mode 100644 index 0000000..5d673ff --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ByteArrayRow.java @@ -0,0 +1,297 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * A RowHolder implementation that is for cached results (a-la mysql_store_result()). + */ +public class ByteArrayRow extends ResultSetRow { + + byte[][] internalRowData; + + public ByteArrayRow(byte[][] internalRowData, ExceptionInterceptor exceptionInterceptor) { + super(exceptionInterceptor); + + this.internalRowData = internalRowData; + } + + @Override + public byte[] getColumnValue(int index) throws SQLException { + return this.internalRowData[index]; + } + + @Override + public void setColumnValue(int index, byte[] value) throws SQLException { + this.internalRowData[index] = value; + } + + @Override + public String getString(int index, String encoding, MySQLConnection conn) throws SQLException { + byte[] columnData = this.internalRowData[index]; + + if (columnData == null) { + return null; + } + + return getString(encoding, conn, columnData, 0, columnData.length); + } + + @Override + public boolean isNull(int index) throws SQLException { + return this.internalRowData[index] == null; + } + + @Override + public boolean isFloatingPointNumber(int index) throws SQLException { + byte[] numAsBytes = this.internalRowData[index]; + + if (this.internalRowData[index] == null || this.internalRowData[index].length == 0) { + return false; + } + + for (int i = 0; i < numAsBytes.length; i++) { + if (((char) numAsBytes[i] == 'e') || ((char) numAsBytes[i] == 'E')) { + return true; + } + } + + return false; + } + + @Override + public long length(int index) throws SQLException { + if (this.internalRowData[index] == null) { + return 0; + } + + return this.internalRowData[index].length; + } + + @Override + public int getInt(int columnIndex) { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return StringUtils.getInt(this.internalRowData[columnIndex]); + } + + @Override + public long getLong(int columnIndex) { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return StringUtils.getLong(this.internalRowData[columnIndex]); + } + + @Override + public Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getTimestampFast(columnIndex, this.internalRowData[columnIndex], 0, columnValue.length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public double getNativeDouble(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return getNativeDouble(this.internalRowData[columnIndex], 0); + } + + @Override + public float getNativeFloat(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return getNativeFloat(this.internalRowData[columnIndex], 0); + } + + @Override + public int getNativeInt(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return getNativeInt(this.internalRowData[columnIndex], 0); + } + + @Override + public long getNativeLong(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return getNativeLong(this.internalRowData[columnIndex], 0); + } + + @Override + public short getNativeShort(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return 0; + } + + return getNativeShort(this.internalRowData[columnIndex], 0); + } + + @Override + public Timestamp getNativeTimestamp(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + byte[] bits = this.internalRowData[columnIndex]; + + if (bits == null) { + return null; + } + + return getNativeTimestamp(bits, 0, bits.length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public void closeOpenStreams() { + // no-op for this type + } + + @Override + public InputStream getBinaryInputStream(int columnIndex) throws SQLException { + if (this.internalRowData[columnIndex] == null) { + return null; + } + + return new ByteArrayInputStream(this.internalRowData[columnIndex]); + } + + @Override + public Reader getReader(int columnIndex) throws SQLException { + InputStream stream = getBinaryInputStream(columnIndex); + + if (stream == null) { + return null; + } + + try { + return new InputStreamReader(stream, this.metadata[columnIndex].getEncoding()); + } catch (UnsupportedEncodingException e) { + SQLException sqlEx = SQLError.createSQLException("", this.exceptionInterceptor); + + sqlEx.initCause(e); + + throw sqlEx; + } + } + + @Override + public Time getTimeFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getTimeFast(columnIndex, this.internalRowData[columnIndex], 0, columnValue.length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public Date getDateFast(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar targetCalendar) throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getDateFast(columnIndex, this.internalRowData[columnIndex], 0, columnValue.length, conn, rs, targetCalendar); + } + + @Override + public Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar, int jdbcType, int mysqlType, TimeZone tz, boolean rollForward, + MySQLConnection conn, ResultSetImpl rs) throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getNativeDateTimeValue(columnIndex, columnValue, 0, columnValue.length, targetCalendar, jdbcType, mysqlType, tz, rollForward, conn, rs); + } + + @Override + public Date getNativeDate(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar cal) throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getNativeDate(columnIndex, columnValue, 0, columnValue.length, conn, rs, cal); + } + + @Override + public Time getNativeTime(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException { + byte[] columnValue = this.internalRowData[columnIndex]; + + if (columnValue == null) { + return null; + } + + return getNativeTime(columnIndex, columnValue, 0, columnValue.length, targetCalendar, tz, rollForward, conn, rs); + } + + @Override + public int getBytesSize() { + if (this.internalRowData == null) { + return 0; + } + + int bytesSize = 0; + + for (int i = 0; i < this.internalRowData.length; i++) { + if (this.internalRowData[i] != null) { + bytesSize += this.internalRowData[i].length; + } + } + + return bytesSize; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapter.java new file mode 100644 index 0000000..8a5df19 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapter.java @@ -0,0 +1,38 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.util.Set; + +public interface CacheAdapter { + V get(K key); + + void put(K key, V value); + + void invalidate(K key); + + void invalidateAll(Set keys); + + void invalidateAll(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapterFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapterFactory.java new file mode 100644 index 0000000..d2a6038 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CacheAdapterFactory.java @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +public interface CacheAdapterFactory { + + public abstract CacheAdapter getInstance(Connection forConn, String url, int cacheMaxSize, int maxKeySize, Properties connectionProperties) + throws SQLException; + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CachedResultSetMetaData.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CachedResultSetMetaData.java new file mode 100644 index 0000000..5728b7a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CachedResultSetMetaData.java @@ -0,0 +1,56 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.util.Map; + +public class CachedResultSetMetaData { + /** Map column names (and all of their permutations) to column indices */ + Map columnNameToIndex = null; + + /** Cached Field info */ + Field[] fields; + + /** Map of fully-specified column names to column indices */ + Map fullColumnNameToIndex = null; + + /** Cached ResultSetMetaData */ + java.sql.ResultSetMetaData metadata; + + public Map getColumnNameToIndex() { + return this.columnNameToIndex; + } + + public Field[] getFields() { + return this.fields; + } + + public Map getFullColumnNameToIndex() { + return this.fullColumnNameToIndex; + } + + public java.sql.ResultSetMetaData getMetadata() { + return this.metadata; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CallableStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CallableStatement.java new file mode 100644 index 0000000..eb0ba45 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CallableStatement.java @@ -0,0 +1,2422 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Representation of stored procedures for JDBC + */ +public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement { + protected final static Constructor JDBC_4_CSTMT_2_ARGS_CTOR; + + protected final static Constructor JDBC_4_CSTMT_4_ARGS_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42CallableStatement" : "com.mysql.jdbc.JDBC4CallableStatement"; + JDBC_4_CSTMT_2_ARGS_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { MySQLConnection.class, CallableStatementParamInfo.class }); + JDBC_4_CSTMT_4_ARGS_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { MySQLConnection.class, String.class, String.class, Boolean.TYPE }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_CSTMT_4_ARGS_CTOR = null; + JDBC_4_CSTMT_2_ARGS_CTOR = null; + } + } + + protected static class CallableStatementParam { + int desiredJdbcType; + + int index; + + int inOutModifier; + + boolean isIn; + + boolean isOut; + + int jdbcType; + + short nullability; + + String paramName; + + int precision; + + int scale; + + String typeName; + + CallableStatementParam(String name, int idx, boolean in, boolean out, int jdbcType, String typeName, int precision, int scale, short nullability, + int inOutModifier) { + this.paramName = name; + this.isIn = in; + this.isOut = out; + this.index = idx; + + this.jdbcType = jdbcType; + this.typeName = typeName; + this.precision = precision; + this.scale = scale; + this.nullability = nullability; + this.inOutModifier = inOutModifier; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#clone() + */ + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + protected class CallableStatementParamInfo implements ParameterMetaData { + String catalogInUse; + + boolean isFunctionCall; + + String nativeSql; + + int numParameters; + + List parameterList; + + Map parameterMap; + + /** + * synchronized externally in checkReadOnlyProcedure() + */ + boolean isReadOnlySafeProcedure = false; + + /** + * synchronized externally in checkReadOnlyProcedure() + */ + boolean isReadOnlySafeChecked = false; + + /** + * Constructor that converts a full list of parameter metadata into one + * that only represents the placeholders present in the {CALL ()}. + * + * @param fullParamInfo + * the metadata for all parameters for this stored + * procedure or function. + */ + CallableStatementParamInfo(CallableStatementParamInfo fullParamInfo) { + this.nativeSql = CallableStatement.this.originalSql; + this.catalogInUse = CallableStatement.this.currentCatalog; + this.isFunctionCall = fullParamInfo.isFunctionCall; + @SuppressWarnings("synthetic-access") + int[] localParameterMap = CallableStatement.this.placeholderToParameterIndexMap; + int parameterMapLength = localParameterMap.length; + + this.isReadOnlySafeProcedure = fullParamInfo.isReadOnlySafeProcedure; + this.isReadOnlySafeChecked = fullParamInfo.isReadOnlySafeChecked; + this.parameterList = new ArrayList(fullParamInfo.numParameters); + this.parameterMap = new HashMap(fullParamInfo.numParameters); + + if (this.isFunctionCall) { + // Take the return value + this.parameterList.add(fullParamInfo.parameterList.get(0)); + } + + int offset = this.isFunctionCall ? 1 : 0; + + for (int i = 0; i < parameterMapLength; i++) { + if (localParameterMap[i] != 0) { + CallableStatementParam param = fullParamInfo.parameterList.get(localParameterMap[i] + offset); + + this.parameterList.add(param); + this.parameterMap.put(param.paramName, param); + } + } + + this.numParameters = this.parameterList.size(); + } + + @SuppressWarnings("synthetic-access") + CallableStatementParamInfo(java.sql.ResultSet paramTypesRs) throws SQLException { + boolean hadRows = paramTypesRs.last(); + + this.nativeSql = CallableStatement.this.originalSql; + this.catalogInUse = CallableStatement.this.currentCatalog; + this.isFunctionCall = CallableStatement.this.callingStoredFunction; + + if (hadRows) { + this.numParameters = paramTypesRs.getRow(); + + this.parameterList = new ArrayList(this.numParameters); + this.parameterMap = new HashMap(this.numParameters); + + paramTypesRs.beforeFirst(); + + addParametersFromDBMD(paramTypesRs); + } else { + this.numParameters = 0; + } + + if (this.isFunctionCall) { + this.numParameters += 1; + } + } + + private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs) throws SQLException { + int i = 0; + + while (paramTypesRs.next()) { + String paramName = paramTypesRs.getString(4); + int inOutModifier = paramTypesRs.getInt(5); + + boolean isOutParameter = false; + boolean isInParameter = false; + + if (i == 0 && this.isFunctionCall) { + isOutParameter = true; + isInParameter = false; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnInOut) { + isOutParameter = true; + isInParameter = true; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnIn) { + isOutParameter = false; + isInParameter = true; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnOut) { + isOutParameter = true; + isInParameter = false; + } + + int jdbcType = paramTypesRs.getInt(6); + String typeName = paramTypesRs.getString(7); + int precision = paramTypesRs.getInt(8); + int scale = paramTypesRs.getInt(10); + short nullability = paramTypesRs.getShort(12); + + CallableStatementParam paramInfoToAdd = new CallableStatementParam(paramName, i++, isInParameter, isOutParameter, jdbcType, typeName, precision, + scale, nullability, inOutModifier); + + this.parameterList.add(paramInfoToAdd); + this.parameterMap.put(paramName, paramInfoToAdd); + } + } + + protected void checkBounds(int paramIndex) throws SQLException { + int localParamIndex = paramIndex - 1; + + if ((paramIndex < 0) || (localParamIndex >= this.numParameters)) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.11") + paramIndex + Messages.getString("CallableStatement.12") + + this.numParameters + Messages.getString("CallableStatement.13"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#clone() + */ + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + CallableStatementParam getParameter(int index) { + return this.parameterList.get(index); + } + + CallableStatementParam getParameter(String name) { + return this.parameterMap.get(name); + } + + public String getParameterClassName(int arg0) throws SQLException { + String mysqlTypeName = getParameterTypeName(arg0); + + boolean isBinaryOrBlob = StringUtils.indexOfIgnoreCase(mysqlTypeName, "BLOB") != -1 || StringUtils.indexOfIgnoreCase(mysqlTypeName, "BINARY") != -1; + + boolean isUnsigned = StringUtils.indexOfIgnoreCase(mysqlTypeName, "UNSIGNED") != -1; + + int mysqlTypeIfKnown = 0; + + if (StringUtils.startsWithIgnoreCase(mysqlTypeName, "MEDIUMINT")) { + mysqlTypeIfKnown = MysqlDefs.FIELD_TYPE_INT24; + } + + return ResultSetMetaData.getClassNameForJavaType(getParameterType(arg0), isUnsigned, mysqlTypeIfKnown, isBinaryOrBlob, false, + CallableStatement.this.connection.getYearIsDateType()); + } + + public int getParameterCount() throws SQLException { + if (this.parameterList == null) { + return 0; + } + + return this.parameterList.size(); + } + + public int getParameterMode(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).inOutModifier; + } + + public int getParameterType(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).jdbcType; + } + + public String getParameterTypeName(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).typeName; + } + + public int getPrecision(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).precision; + } + + public int getScale(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).scale; + } + + public int isNullable(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).nullability; + } + + public boolean isSigned(int arg0) throws SQLException { + checkBounds(arg0); + + return false; + } + + Iterator iterator() { + return this.parameterList.iterator(); + } + + int numberOfParameters() { + return this.numParameters; + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE; + + private final static String PARAMETER_NAMESPACE_PREFIX = "@com_mysql_jdbc_outparam_"; + + private static String mangleParameterName(String origParameterName) { + //Fixed for 5.5+ in callers + if (origParameterName == null) { + return null; + } + + int offset = 0; + + if (origParameterName.length() > 0 && origParameterName.charAt(0) == '@') { + offset = 1; + } + + StringBuilder paramNameBuf = new StringBuilder(PARAMETER_NAMESPACE_PREFIX.length() + origParameterName.length()); + paramNameBuf.append(PARAMETER_NAMESPACE_PREFIX); + paramNameBuf.append(origParameterName.substring(offset)); + + return paramNameBuf.toString(); + } + + private boolean callingStoredFunction = false; + + private ResultSetInternalMethods functionReturnValueResults; + + private boolean hasOutputParams = false; + + // private List parameterList; + // private Map parameterMap; + private ResultSetInternalMethods outputParameterResults; + + protected boolean outputParamWasNull = false; + + private int[] parameterIndexToRsIndex; + + protected CallableStatementParamInfo paramInfo; + + private CallableStatementParam returnValueParam; + + /** + * Creates a new CallableStatement + * + * @param conn + * the connection creating this statement + * @param paramInfo + * the SQL to prepare + * + * @throws SQLException + * if an error occurs + */ + public CallableStatement(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + super(conn, paramInfo.nativeSql, paramInfo.catalogInUse); + + this.paramInfo = paramInfo; + this.callingStoredFunction = this.paramInfo.isFunctionCall; + + if (this.callingStoredFunction) { + this.parameterCount += 1; + } + + this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec + } + + /** + * Creates a callable statement instance -- We need to provide factory-style methods + * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise + * the class verifier complains when it tries to load JDBC4-only interface + * classes that are present in JDBC4 method signatures. + */ + + protected static CallableStatement getInstance(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + if (!Util.isJdbc4()) { + return new CallableStatement(conn, sql, catalog, isFunctionCall); + } + + return (CallableStatement) Util.handleNewInstance(JDBC_4_CSTMT_4_ARGS_CTOR, new Object[] { conn, sql, catalog, Boolean.valueOf(isFunctionCall) }, + conn.getExceptionInterceptor()); + } + + /** + * Creates a callable statement instance -- We need to provide factory-style methods + * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise + * the class verifier complains when it tries to load JDBC4-only interface + * classes that are present in JDBC4 method signatures. + */ + + protected static CallableStatement getInstance(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + if (!Util.isJdbc4()) { + return new CallableStatement(conn, paramInfo); + } + + return (CallableStatement) Util.handleNewInstance(JDBC_4_CSTMT_2_ARGS_CTOR, new Object[] { conn, paramInfo }, conn.getExceptionInterceptor()); + + } + + private int[] placeholderToParameterIndexMap; + + private void generateParameterMap() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo == null) { + return; + } + + // if the user specified some parameters as literals, we need to provide a map from the specified placeholders to the actual parameter numbers + + int parameterCountFromMetaData = this.paramInfo.getParameterCount(); + + // Ignore the first ? if this is a stored function, it doesn't count + + if (this.callingStoredFunction) { + parameterCountFromMetaData--; + } + + if (this.paramInfo != null && this.parameterCount != parameterCountFromMetaData) { + this.placeholderToParameterIndexMap = new int[this.parameterCount]; + + int startPos = this.callingStoredFunction ? StringUtils.indexOfIgnoreCase(this.originalSql, "SELECT") + : StringUtils.indexOfIgnoreCase(this.originalSql, "CALL"); + + if (startPos != -1) { + int parenOpenPos = this.originalSql.indexOf('(', startPos + 4); + + if (parenOpenPos != -1) { + int parenClosePos = StringUtils.indexOfIgnoreCase(parenOpenPos, this.originalSql, ")", "'", "'", StringUtils.SEARCH_MODE__ALL); + + if (parenClosePos != -1) { + List parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true); + + int numParsedParameters = parsedParameters.size(); + + // sanity check + + if (numParsedParameters != this.parameterCount) { + // bail? + } + + int placeholderCount = 0; + + for (int i = 0; i < numParsedParameters; i++) { + if (((String) parsedParameters.get(i)).equals("?")) { + this.placeholderToParameterIndexMap[placeholderCount++] = i; + } + } + } + } + } + } + } + } + + /** + * Creates a new CallableStatement + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL to prepare + * @param catalog + * the current catalog + * + * @throws SQLException + * if an error occurs + */ + public CallableStatement(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + super(conn, sql, catalog); + + this.callingStoredFunction = isFunctionCall; + + if (!this.callingStoredFunction) { + if (!StringUtils.startsWithIgnoreCaseAndWs(sql, "CALL")) { + // not really a stored procedure call + fakeParameterTypes(false); + } else { + determineParameterTypes(); + } + + generateParameterMap(); + } else { + determineParameterTypes(); + generateParameterMap(); + + this.parameterCount += 1; + } + + this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec + } + + /* + * (non-Javadoc) + * + * @see java.sql.PreparedStatement#addBatch() + */ + @Override + public void addBatch() throws SQLException { + setOutParams(); + + super.addBatch(); + } + + private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + if (this.callingStoredFunction) { + if (paramIndex == 1) { + + if (this.returnValueParam == null) { + this.returnValueParam = new CallableStatementParam("", 0, false, true, Types.VARCHAR, "VARCHAR", 0, 0, + java.sql.DatabaseMetaData.attributeNullableUnknown, java.sql.DatabaseMetaData.procedureColumnReturn); + } + + return this.returnValueParam; + } + + // Move to position in output result set + paramIndex--; + } + + checkParameterIndexBounds(paramIndex); + + int localParamIndex = paramIndex - 1; + + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + + CallableStatementParam paramDescriptor = this.paramInfo.getParameter(localParamIndex); + + // We don't have reliable metadata in this case, trust the caller + + if (this.connection.getNoAccessToProcedureBodies()) { + paramDescriptor.isOut = true; + paramDescriptor.isIn = true; + paramDescriptor.inOutModifier = java.sql.DatabaseMetaData.procedureColumnInOut; + } else if (!paramDescriptor.isOut) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.9") + paramIndex + Messages.getString("CallableStatement.10"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.hasOutputParams = true; + + return paramDescriptor; + } + } + + /** + * @param paramIndex + * + * @throws SQLException + */ + private void checkParameterIndexBounds(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.paramInfo.checkBounds(paramIndex); + } + } + + /** + * Checks whether or not this statement is supposed to be providing + * streamable result sets...If output parameters are registered, the driver + * can not stream the results. + * + * @throws SQLException + */ + private void checkStreamability() throws SQLException { + if (this.hasOutputParams && createStreamingResultSet()) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.14"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + } + + @Override + public void clearParameters() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.clearParameters(); + + try { + if (this.outputParameterResults != null) { + this.outputParameterResults.close(); + } + } finally { + this.outputParameterResults = null; + } + } + } + + /** + * Used to fake up some metadata when we don't have access to + * SHOW CREATE PROCEDURE or mysql.proc. + * + * @throws SQLException + * if we can't build the metadata. + */ + private void fakeParameterTypes(boolean isReallyProcedure) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Field[] fields = new Field[13]; + + fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0); + fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0); + fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0); + fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 0); + fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0); + fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0); + fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0); + fields[7] = new Field("", "PRECISION", Types.INTEGER, 0); + fields[8] = new Field("", "LENGTH", Types.INTEGER, 0); + fields[9] = new Field("", "SCALE", Types.SMALLINT, 0); + fields[10] = new Field("", "RADIX", Types.SMALLINT, 0); + fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0); + fields[12] = new Field("", "REMARKS", Types.CHAR, 0); + + String procName = isReallyProcedure ? extractProcedureName() : null; + + byte[] procNameAsBytes = null; + + try { + procNameAsBytes = procName == null ? null : StringUtils.getBytes(procName, "UTF-8"); + } catch (UnsupportedEncodingException ueEx) { + procNameAsBytes = StringUtils.s2b(procName, this.connection); + } + + ArrayList resultRows = new ArrayList(); + + for (int i = 0; i < this.parameterCount; i++) { + byte[][] row = new byte[13][]; + row[0] = null; // PROCEDURE_CAT + row[1] = null; // PROCEDURE_SCHEM + row[2] = procNameAsBytes; // PROCEDURE/NAME + row[3] = StringUtils.s2b(String.valueOf(i), this.connection); // COLUMN_NAME + + row[4] = StringUtils.s2b(String.valueOf(java.sql.DatabaseMetaData.procedureColumnIn), this.connection); + + row[5] = StringUtils.s2b(String.valueOf(Types.VARCHAR), this.connection); // DATA_TYPE + row[6] = StringUtils.s2b("VARCHAR", this.connection); // TYPE_NAME + row[7] = StringUtils.s2b(Integer.toString(65535), this.connection); // PRECISION + row[8] = StringUtils.s2b(Integer.toString(65535), this.connection); // LENGTH + row[9] = StringUtils.s2b(Integer.toString(0), this.connection); // SCALE + row[10] = StringUtils.s2b(Integer.toString(10), this.connection); // RADIX + + row[11] = StringUtils.s2b(Integer.toString(java.sql.DatabaseMetaData.procedureNullableUnknown), this.connection); // nullable + + row[12] = null; + + resultRows.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + + java.sql.ResultSet paramTypesRs = DatabaseMetaData.buildResultSet(fields, resultRows, this.connection); + + convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); + } + } + + private void determineParameterTypes() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + java.sql.ResultSet paramTypesRs = null; + + try { + //Bug#57022, we need to check for db.SPname notation first and pass on only SPname + String procName = extractProcedureName(); + String quotedId = ""; + try { + quotedId = this.connection.supportsQuotedIdentifiers() ? this.connection.getMetaData().getIdentifierQuoteString() : ""; + } catch (SQLException sqlEx) { + // Forced by API, never thrown from getIdentifierQuoteString() in + // this implementation. + AssertionFailedException.shouldNotHappen(sqlEx); + } + + List parseList = StringUtils.splitDBdotName(procName, "", quotedId, this.connection.isNoBackslashEscapesSet()); + String tmpCatalog = ""; + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tmpCatalog = (String) parseList.get(0); + procName = (String) parseList.get(1); + } else { + //keep values as they are + } + + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + + boolean useCatalog = false; + + if (tmpCatalog.length() <= 0) { + useCatalog = true; + } + + paramTypesRs = dbmd.getProcedureColumns(this.connection.versionMeetsMinimum(5, 0, 2) && useCatalog ? this.currentCatalog : tmpCatalog/* null */, + null, procName, "%"); + + boolean hasResults = false; + try { + if (paramTypesRs.next()) { + paramTypesRs.previous(); + hasResults = true; + } + } catch (Exception e) { + // paramTypesRs is empty, proceed with fake params. swallow, was expected + } + if (hasResults) { + convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); + } else { + fakeParameterTypes(true); + } + } finally { + SQLException sqlExRethrow = null; + + if (paramTypesRs != null) { + try { + paramTypesRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramTypesRs = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + } + } + + private void convertGetProcedureColumnsToInternalDescriptors(java.sql.ResultSet paramTypesRs) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.paramInfo = new CallableStatementParamInfo(paramTypesRs); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.PreparedStatement#execute() + */ + @Override + public boolean execute() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + boolean returnVal = false; + + checkStreamability(); + + setInOutParamsOnServer(); + setOutParams(); + + returnVal = super.execute(); + + if (this.callingStoredFunction) { + this.functionReturnValueResults = this.results; + this.functionReturnValueResults.next(); + this.results = null; + } + + retrieveOutParams(); + + if (!this.callingStoredFunction) { + return returnVal; + } + + // Functions can't return results + return false; + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.PreparedStatement#executeQuery() + */ + @Override + public java.sql.ResultSet executeQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + checkStreamability(); + + java.sql.ResultSet execResults = null; + + setInOutParamsOnServer(); + setOutParams(); + + execResults = super.executeQuery(); + + retrieveOutParams(); + + return execResults; + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.PreparedStatement#executeUpdate() + */ + @Override + public int executeUpdate() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate()); + } + + private String extractProcedureName() throws SQLException { + String sanitizedSql = StringUtils.stripComments(this.originalSql, "`\"'", "`\"'", true, false, true, true); + + // TODO: Do this with less memory allocation + int endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "CALL "); + int offset = 5; + + if (endCallIndex == -1) { + endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "SELECT "); + offset = 7; + } + + if (endCallIndex != -1) { + StringBuilder nameBuf = new StringBuilder(); + + String trimmedStatement = sanitizedSql.substring(endCallIndex + offset).trim(); + + int statementLength = trimmedStatement.length(); + + for (int i = 0; i < statementLength; i++) { + char c = trimmedStatement.charAt(i); + + if (Character.isWhitespace(c) || (c == '(') || (c == '?')) { + break; + } + nameBuf.append(c); + + } + + return nameBuf.toString(); + } + + throw SQLError.createSQLException(Messages.getString("CallableStatement.1"), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + /** + * Adds 'at' symbol to beginning of parameter names if needed. + * + * @param paramNameIn + * the parameter name to 'fix' + * + * @return the parameter name with an 'a' prepended, if needed + * + * @throws SQLException + * if the parameter name is null or empty. + */ + protected String fixParameterName(String paramNameIn) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + //Fixed for 5.5+ + if (((paramNameIn == null) || (paramNameIn.length() == 0)) && (!hasParametersView())) { + throw SQLError.createSQLException(((Messages.getString("CallableStatement.0") + paramNameIn) == null) + ? Messages.getString("CallableStatement.15") : Messages.getString("CallableStatement.16"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if ((paramNameIn == null) && (hasParametersView())) { + paramNameIn = "nullpn"; + } + + if (this.connection.getNoAccessToProcedureBodies()) { + throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return mangleParameterName(paramNameIn); + } + } + + /** + * @see java.sql.CallableStatement#getArray(int) + */ + public Array getArray(int i) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(i); + + Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getArray(java.lang.String) + */ + public Array getArray(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Array retValue = rs.getArray(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBigDecimal(int) + */ + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @param parameterIndex + * @param scale + * + * @throws SQLException + * + * @see java.sql.CallableStatement#getBigDecimal(int, int) + * @deprecated + */ + @Deprecated + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex), scale); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBigDecimal(java.lang.String) + */ + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBlob(int) + */ + public Blob getBlob(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Blob retValue = rs.getBlob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBlob(java.lang.String) + */ + public Blob getBlob(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Blob retValue = rs.getBlob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBoolean(int) + */ + public boolean getBoolean(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + boolean retValue = rs.getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBoolean(java.lang.String) + */ + public boolean getBoolean(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + boolean retValue = rs.getBoolean(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getByte(int) + */ + public byte getByte(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + byte retValue = rs.getByte(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getByte(java.lang.String) + */ + public byte getByte(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + byte retValue = rs.getByte(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBytes(int) + */ + public byte[] getBytes(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + byte[] retValue = rs.getBytes(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBytes(java.lang.String) + */ + public byte[] getBytes(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + byte[] retValue = rs.getBytes(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getClob(int) + */ + public Clob getClob(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Clob retValue = rs.getClob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getClob(java.lang.String) + */ + public Clob getClob(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Clob retValue = rs.getClob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(int) + */ + public Date getDate(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(int, java.util.Calendar) + */ + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(java.lang.String) + */ + public Date getDate(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Date retValue = rs.getDate(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(java.lang.String, java.util.Calendar) + */ + public Date getDate(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Date retValue = rs.getDate(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDouble(int) + */ + public double getDouble(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + double retValue = rs.getDouble(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDouble(java.lang.String) + */ + public double getDouble(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + double retValue = rs.getDouble(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getFloat(int) + */ + public float getFloat(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + float retValue = rs.getFloat(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getFloat(java.lang.String) + */ + public float getFloat(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + float retValue = rs.getFloat(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getInt(int) + */ + public int getInt(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + int retValue = rs.getInt(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getInt(java.lang.String) + */ + public int getInt(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + int retValue = rs.getInt(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getLong(int) + */ + public long getLong(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + long retValue = rs.getLong(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getLong(java.lang.String) + */ + public long getLong(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + long retValue = rs.getLong(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection.getNoAccessToProcedureBodies()) { + throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + //Fixed for 5.5+ in callers + if ((paramName == null) || (paramName.length() == 0)) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.paramInfo == null) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.3") + paramName + Messages.getString("CallableStatement.4"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + CallableStatementParam namedParamInfo = this.paramInfo.getParameter(paramName); + + if (forOut && !namedParamInfo.isOut) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.5") + paramName + Messages.getString("CallableStatement.6"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.placeholderToParameterIndexMap == null) { + return namedParamInfo.index + 1; // JDBC indices are 1-based + } + + for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { + if (this.placeholderToParameterIndexMap[i] == namedParamInfo.index) { + return i + 1; + } + } + + throw SQLError.createSQLException("Can't find local placeholder mapping for parameter named \"" + paramName + "\".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.CallableStatement#getObject(int) + */ + public Object getObject(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); + + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Object retVal = rs.getObjectStoredProc(mapOutputParameterIndexToRsIndex(parameterIndex), paramDescriptor.desiredJdbcType); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + /** + * @see java.sql.CallableStatement#getObject(int, java.util.Map) + */ + public Object getObject(int parameterIndex, Map> map) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Object retVal = rs.getObject(mapOutputParameterIndexToRsIndex(parameterIndex), map); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + /** + * @see java.sql.CallableStatement#getObject(java.lang.String) + */ + public Object getObject(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Object retValue = rs.getObject(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getObject(java.lang.String, java.util.Map) + */ + public Object getObject(String parameterName, Map> map) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Object retValue = rs.getObject(fixParameterName(parameterName), map); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + // JDBC-4.1 + public T getObject(int parameterIndex, Class type) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + // remove cast once 1.5, 1.6 EOL'd + T retVal = ((ResultSetImpl) rs).getObject(mapOutputParameterIndexToRsIndex(parameterIndex), type); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + public T getObject(String parameterName, Class type) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + T retValue = ((ResultSetImpl) rs).getObject(fixParameterName(parameterName), type); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * Returns the ResultSet that holds the output parameters, or throws an + * appropriate exception if none exist, or they weren't returned. + * + * @return the ResultSet that holds the output parameters + * + * @throws SQLException + * if no output parameters were defined, or if no output + * parameters were returned. + */ + protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.outputParamWasNull = false; + + if (paramIndex == 1 && this.callingStoredFunction && this.returnValueParam != null) { + return this.functionReturnValueResults; + } + + if (this.outputParameterResults == null) { + if (this.paramInfo.numberOfParameters() == 0) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.7"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + throw SQLError.createSQLException(Messages.getString("CallableStatement.8"), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + return this.outputParameterResults; + } + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.placeholderToParameterIndexMap == null) { + return this.paramInfo; + } + + return new CallableStatementParamInfo(this.paramInfo); + } + } + + /** + * @see java.sql.CallableStatement#getRef(int) + */ + public Ref getRef(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Ref retValue = rs.getRef(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getRef(java.lang.String) + */ + public Ref getRef(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Ref retValue = rs.getRef(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getShort(int) + */ + public short getShort(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + short retValue = rs.getShort(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getShort(java.lang.String) + */ + public short getShort(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + short retValue = rs.getShort(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getString(int) + */ + public String getString(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + String retValue = rs.getString(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getString(java.lang.String) + */ + public String getString(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + String retValue = rs.getString(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(int) + */ + public Time getTime(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(int, java.util.Calendar) + */ + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(java.lang.String) + */ + public Time getTime(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Time retValue = rs.getTime(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(java.lang.String, java.util.Calendar) + */ + public Time getTime(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Time retValue = rs.getTime(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(int) + */ + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar) + */ + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(java.lang.String) + */ + public Timestamp getTimestamp(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(java.lang.String, java.util.Calendar) + */ + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getURL(int) + */ + public URL getURL(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + URL retValue = rs.getURL(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getURL(java.lang.String) + */ + public URL getURL(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + URL retValue = rs.getURL(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + if (this.returnValueParam != null && paramIndex == 1) { + return 1; + } + + checkParameterIndexBounds(paramIndex); + + int localParamIndex = paramIndex - 1; + + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + + int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; + + if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.21") + paramIndex + Messages.getString("CallableStatement.22"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return rsIndex + 1; + } + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(int, int) + */ + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); + paramDescriptor.desiredJdbcType = sqlType; + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(int, int, int) + */ + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + registerOutParameter(parameterIndex, sqlType); + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(int, int, java.lang.String) + */ + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { + checkIsOutputParam(parameterIndex); + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int) + */ + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); + } + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, int) + */ + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, java.lang.String) + */ + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType, typeName); + } + + /** + * Issues a second query to retrieve all output parameters. + * + * @throws SQLException + * if an error occurs. + */ + private void retrieveOutParams() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + int numParameters = this.paramInfo.numberOfParameters(); + + this.parameterIndexToRsIndex = new int[numParameters]; + + for (int i = 0; i < numParameters; i++) { + this.parameterIndexToRsIndex[i] = NOT_OUTPUT_PARAMETER_INDICATOR; + } + + int localParamIndex = 0; + + if (numParameters > 0) { + StringBuilder outParameterQuery = new StringBuilder("SELECT "); + + boolean firstParam = true; + boolean hadOutputParams = false; + + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + CallableStatementParam retrParamInfo = paramIter.next(); + + if (retrParamInfo.isOut) { + hadOutputParams = true; + + this.parameterIndexToRsIndex[retrParamInfo.index] = localParamIndex++; + + if ((retrParamInfo.paramName == null) && (hasParametersView())) { + retrParamInfo.paramName = "nullnp" + retrParamInfo.index; + } + + String outParameterName = mangleParameterName(retrParamInfo.paramName); + + if (!firstParam) { + outParameterQuery.append(","); + } else { + firstParam = false; + } + + if (!outParameterName.startsWith("@")) { + outParameterQuery.append('@'); + } + + outParameterQuery.append(outParameterName); + } + } + + if (hadOutputParams) { + // We can't use 'ourself' to execute this query, or any pending result sets would be overwritten + java.sql.Statement outParameterStmt = null; + java.sql.ResultSet outParamRs = null; + + try { + outParameterStmt = this.connection.createStatement(); + outParamRs = outParameterStmt.executeQuery(outParameterQuery.toString()); + this.outputParameterResults = ((com.mysql.jdbc.ResultSetInternalMethods) outParamRs).copy(); + + if (!this.outputParameterResults.next()) { + this.outputParameterResults.close(); + this.outputParameterResults = null; + } + } finally { + if (outParameterStmt != null) { + outParameterStmt.close(); + } + } + } else { + this.outputParameterResults = null; + } + } else { + this.outputParameterResults = null; + } + } + } + + /** + * @see java.sql.CallableStatement#setAsciiStream(java.lang.String, java.io.InputStream, int) + */ + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x, length); + } + + /** + * @see java.sql.CallableStatement#setBigDecimal(java.lang.String, java.math.BigDecimal) + */ + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + setBigDecimal(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setBinaryStream(java.lang.String, java.io.InputStream, int) + */ + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x, length); + } + + /** + * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean) + */ + public void setBoolean(String parameterName, boolean x) throws SQLException { + setBoolean(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setByte(java.lang.String, byte) + */ + public void setByte(String parameterName, byte x) throws SQLException { + setByte(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[]) + */ + public void setBytes(String parameterName, byte[] x) throws SQLException { + setBytes(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setCharacterStream(java.lang.String, java.io.Reader, int) + */ + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); + } + + /** + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date) + */ + public void setDate(String parameterName, Date x) throws SQLException { + setDate(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date, java.util.Calendar) + */ + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + setDate(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setDouble(java.lang.String, double) + */ + public void setDouble(String parameterName, double x) throws SQLException { + setDouble(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setFloat(java.lang.String, float) + */ + public void setFloat(String parameterName, float x) throws SQLException { + setFloat(getNamedParamIndex(parameterName, false), x); + } + + private void setInOutParamsOnServer() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo.numParameters > 0) { + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + + CallableStatementParam inParamInfo = paramIter.next(); + + //Fix for 5.5+ + if (inParamInfo.isOut && inParamInfo.isIn) { + if ((inParamInfo.paramName == null) && (hasParametersView())) { + inParamInfo.paramName = "nullnp" + inParamInfo.index; + } + + String inOutParameterName = mangleParameterName(inParamInfo.paramName); + StringBuilder queryBuf = new StringBuilder(4 + inOutParameterName.length() + 1 + 1); + queryBuf.append("SET "); + queryBuf.append(inOutParameterName); + queryBuf.append("=?"); + + PreparedStatement setPstmt = null; + + try { + setPstmt = ((Wrapper) this.connection.clientPrepareStatement(queryBuf.toString())).unwrap(PreparedStatement.class); + + if (this.isNull[inParamInfo.index]) { + setPstmt.setBytesNoEscapeNoQuotes(1, "NULL".getBytes()); + + } else { + byte[] parameterAsBytes = getBytesRepresentation(inParamInfo.index); + + if (parameterAsBytes != null) { + if (parameterAsBytes.length > 8 && parameterAsBytes[0] == '_' && parameterAsBytes[1] == 'b' && parameterAsBytes[2] == 'i' + && parameterAsBytes[3] == 'n' && parameterAsBytes[4] == 'a' && parameterAsBytes[5] == 'r' + && parameterAsBytes[6] == 'y' && parameterAsBytes[7] == '\'') { + setPstmt.setBytesNoEscapeNoQuotes(1, parameterAsBytes); + } else { + int sqlType = inParamInfo.desiredJdbcType; + + switch (sqlType) { + case Types.BIT: + case Types.BINARY: + case Types.BLOB: + case Types.JAVA_OBJECT: + case Types.LONGVARBINARY: + case Types.VARBINARY: + setPstmt.setBytes(1, parameterAsBytes); + break; + default: + // the inherited PreparedStatement methods have already escaped and quoted these parameters + setPstmt.setBytesNoEscape(1, parameterAsBytes); + } + } + } else { + setPstmt.setNull(1, Types.NULL); + } + } + + setPstmt.executeUpdate(); + } finally { + if (setPstmt != null) { + setPstmt.close(); + } + } + } + } + } + } + } + + /** + * @see java.sql.CallableStatement#setInt(java.lang.String, int) + */ + public void setInt(String parameterName, int x) throws SQLException { + setInt(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setLong(java.lang.String, long) + */ + public void setLong(String parameterName, long x) throws SQLException { + setLong(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setNull(java.lang.String, int) + */ + public void setNull(String parameterName, int sqlType) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType); + } + + /** + * @see java.sql.CallableStatement#setNull(java.lang.String, int, java.lang.String) + */ + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType, typeName); + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object) + */ + public void setObject(String parameterName, Object x) throws SQLException { + setObject(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + setObject(getNamedParamIndex(parameterName, false), x, targetSqlType); + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + } + + private void setOutParams() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo.numParameters > 0) { + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + CallableStatementParam outParamInfo = paramIter.next(); + + if (!this.callingStoredFunction && outParamInfo.isOut) { + + if ((outParamInfo.paramName == null) && (hasParametersView())) { + outParamInfo.paramName = "nullnp" + outParamInfo.index; + } + + String outParameterName = mangleParameterName(outParamInfo.paramName); + + int outParamIndex = 0; + + if (this.placeholderToParameterIndexMap == null) { + outParamIndex = outParamInfo.index + 1; + } else { + // Find it, todo: remove this linear search + boolean found = false; + + for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { + if (this.placeholderToParameterIndexMap[i] == outParamInfo.index) { + outParamIndex = i + 1; /* JDBC is 1-based */ + found = true; + break; + } + } + + if (!found) { + throw SQLError.createSQLException( + Messages.getString("CallableStatement.21") + outParamInfo.paramName + Messages.getString("CallableStatement.22"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charConverter, this.charEncoding, + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor())); + } + } + } + } + } + + /** + * @see java.sql.CallableStatement#setShort(java.lang.String, short) + */ + public void setShort(String parameterName, short x) throws SQLException { + setShort(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setString(java.lang.String, java.lang.String) + */ + public void setString(String parameterName, String x) throws SQLException { + setString(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time) + */ + public void setTime(String parameterName, Time x) throws SQLException { + setTime(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time, java.util.Calendar) + */ + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + setTime(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp) + */ + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + setTimestamp(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp, java.util.Calendar) + */ + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + setTimestamp(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL) + */ + public void setURL(String parameterName, URL val) throws SQLException { + setURL(getNamedParamIndex(parameterName, false), val); + } + + /** + * @see java.sql.CallableStatement#wasNull() + */ + public boolean wasNull() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.outputParamWasNull; + } + } + + @Override + public int[] executeBatch() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeBatch()); + + } + + @Override + protected int getParameterIndexOffset() { + if (this.callingStoredFunction) { + return -1; + } + + return super.getParameterIndexOffset(); + } + + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x); + + } + + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x, length); + + } + + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x); + + } + + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x, length); + + } + + public void setBlob(String parameterName, Blob x) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), x); + + } + + public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), inputStream); + + } + + public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), inputStream, length); + + } + + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader); + + } + + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setClob(String parameterName, Clob x) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), x); + + } + + public void setClob(String parameterName, Reader reader) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), reader); + + } + + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + setNCharacterStream(getNamedParamIndex(parameterName, false), value); + + } + + public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + setNCharacterStream(getNamedParamIndex(parameterName, false), value, length); + + } + + /** + * Check whether the stored procedure alters any data or is safe for read-only usage. + * + * @return true if procedure does not alter data + * @throws SQLException + */ + private boolean checkReadOnlyProcedure() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection.getNoAccessToProcedureBodies()) { + return false; + } + + if (this.paramInfo.isReadOnlySafeChecked) { + return this.paramInfo.isReadOnlySafeProcedure; + } + + ResultSet rs = null; + java.sql.PreparedStatement ps = null; + + try { + String procName = extractProcedureName(); + + String catalog = this.currentCatalog; + + if (procName.indexOf(".") != -1) { + catalog = procName.substring(0, procName.indexOf(".")); + + if (StringUtils.startsWithIgnoreCaseAndWs(catalog, "`") && catalog.trim().endsWith("`")) { + catalog = catalog.substring(1, catalog.length() - 1); + } + + procName = procName.substring(procName.indexOf(".") + 1); + procName = StringUtils.toString(StringUtils.stripEnclosure(StringUtils.getBytes(procName), "`", "`")); + } + ps = this.connection.prepareStatement("SELECT SQL_DATA_ACCESS FROM information_schema.routines WHERE routine_schema = ? AND routine_name = ?"); + ps.setMaxRows(0); + ps.setFetchSize(0); + + ps.setString(1, catalog); + ps.setString(2, procName); + rs = ps.executeQuery(); + if (rs.next()) { + String sqlDataAccess = rs.getString(1); + if ("READS SQL DATA".equalsIgnoreCase(sqlDataAccess) || "NO SQL".equalsIgnoreCase(sqlDataAccess)) { + synchronized (this.paramInfo) { + this.paramInfo.isReadOnlySafeChecked = true; + this.paramInfo.isReadOnlySafeProcedure = true; + } + return true; + } + } + } catch (SQLException e) { + // swallow the Exception + } finally { + if (rs != null) { + rs.close(); + } + if (ps != null) { + ps.close(); + } + + } + this.paramInfo.isReadOnlySafeChecked = false; + this.paramInfo.isReadOnlySafeProcedure = false; + } + return false; + + } + + @Override + protected boolean checkReadOnlySafeStatement() throws SQLException { + return (super.checkReadOnlySafeStatement() || this.checkReadOnlyProcedure()); + } + + private boolean hasParametersView() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + if (this.connection.versionMeetsMinimum(5, 5, 0)) { + java.sql.DatabaseMetaData dbmd1 = new DatabaseMetaDataUsingInfoSchema(this.connection, this.connection.getCatalog()); + return ((DatabaseMetaDataUsingInfoSchema) dbmd1).gethasParametersView(); + } + + return false; + } catch (SQLException e) { + return false; + } + } + } + + /** + * JDBC 4.2 + */ + @Override + public long executeLargeUpdate() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + long returnVal = -1; + + checkStreamability(); + + if (this.callingStoredFunction) { + execute(); + + return -1; + } + + setInOutParamsOnServer(); + setOutParams(); + + returnVal = super.executeLargeUpdate(); + + retrieveOutParams(); + + return returnVal; + } + } + + @Override + public long[] executeLargeBatch() throws SQLException { + if (this.hasOutputParams) { + throw SQLError.createSQLException("Can't call executeBatch() on CallableStatement with OUTPUT parameters", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + return super.executeLargeBatch(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CharsetMapping.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CharsetMapping.java new file mode 100644 index 0000000..7851f07 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CharsetMapping.java @@ -0,0 +1,927 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.nio.charset.Charset; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Mapping between MySQL charset names and Java charset names. I've investigated placing these in a .properties file, but unfortunately under most appservers + * this complicates configuration because the security policy needs to be changed by the user to allow the driver to read them :( + */ +public class CharsetMapping { + + public static final int MAP_SIZE = 255; // Size of static maps + public static final String[] COLLATION_INDEX_TO_COLLATION_NAME; + public static final MysqlCharset[] COLLATION_INDEX_TO_CHARSET; + + public static final Map CHARSET_NAME_TO_CHARSET; + public static final Map CHARSET_NAME_TO_COLLATION_INDEX; + + private static final Map> JAVA_ENCODING_UC_TO_MYSQL_CHARSET; + + private static final Set MULTIBYTE_ENCODINGS; + private static final Map ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET; + + private static final Set ESCAPE_ENCODINGS; + + public static final Set UTF8MB4_INDEXES; + + private static final String MYSQL_CHARSET_NAME_armscii8 = "armscii8"; + private static final String MYSQL_CHARSET_NAME_ascii = "ascii"; + private static final String MYSQL_CHARSET_NAME_big5 = "big5"; + private static final String MYSQL_CHARSET_NAME_binary = "binary"; + private static final String MYSQL_CHARSET_NAME_cp1250 = "cp1250"; + private static final String MYSQL_CHARSET_NAME_cp1251 = "cp1251"; + private static final String MYSQL_CHARSET_NAME_cp1256 = "cp1256"; + private static final String MYSQL_CHARSET_NAME_cp1257 = "cp1257"; + private static final String MYSQL_CHARSET_NAME_cp850 = "cp850"; + private static final String MYSQL_CHARSET_NAME_cp852 = "cp852"; + private static final String MYSQL_CHARSET_NAME_cp866 = "cp866"; + private static final String MYSQL_CHARSET_NAME_cp932 = "cp932"; + private static final String MYSQL_CHARSET_NAME_dec8 = "dec8"; + private static final String MYSQL_CHARSET_NAME_eucjpms = "eucjpms"; + private static final String MYSQL_CHARSET_NAME_euckr = "euckr"; + private static final String MYSQL_CHARSET_NAME_gb18030 = "gb18030"; + private static final String MYSQL_CHARSET_NAME_gb2312 = "gb2312"; + private static final String MYSQL_CHARSET_NAME_gbk = "gbk"; + private static final String MYSQL_CHARSET_NAME_geostd8 = "geostd8"; + private static final String MYSQL_CHARSET_NAME_greek = "greek"; + private static final String MYSQL_CHARSET_NAME_hebrew = "hebrew"; + private static final String MYSQL_CHARSET_NAME_hp8 = "hp8"; + private static final String MYSQL_CHARSET_NAME_keybcs2 = "keybcs2"; + private static final String MYSQL_CHARSET_NAME_koi8r = "koi8r"; + private static final String MYSQL_CHARSET_NAME_koi8u = "koi8u"; + private static final String MYSQL_CHARSET_NAME_latin1 = "latin1"; + private static final String MYSQL_CHARSET_NAME_latin2 = "latin2"; + private static final String MYSQL_CHARSET_NAME_latin5 = "latin5"; + private static final String MYSQL_CHARSET_NAME_latin7 = "latin7"; + private static final String MYSQL_CHARSET_NAME_macce = "macce"; + private static final String MYSQL_CHARSET_NAME_macroman = "macroman"; + private static final String MYSQL_CHARSET_NAME_sjis = "sjis"; + private static final String MYSQL_CHARSET_NAME_swe7 = "swe7"; + private static final String MYSQL_CHARSET_NAME_tis620 = "tis620"; + private static final String MYSQL_CHARSET_NAME_ucs2 = "ucs2"; + private static final String MYSQL_CHARSET_NAME_ujis = "ujis"; + private static final String MYSQL_CHARSET_NAME_utf16 = "utf16"; + private static final String MYSQL_CHARSET_NAME_utf16le = "utf16le"; + private static final String MYSQL_CHARSET_NAME_utf32 = "utf32"; + private static final String MYSQL_CHARSET_NAME_utf8 = "utf8"; + private static final String MYSQL_CHARSET_NAME_utf8mb4 = "utf8mb4"; + + private static final String MYSQL_4_0_CHARSET_NAME_cp1251cias = "cp1251cias"; + private static final String MYSQL_4_0_CHARSET_NAME_cp1251csas = "cp1251csas"; + private static final String MYSQL_4_0_CHARSET_NAME_croat = "croat"; // 4.1 => 27 latin2 latin2_croatian_ci + private static final String MYSQL_4_0_CHARSET_NAME_czech = "czech"; // 4.1 => 2 latin2 latin2_czech_ci + private static final String MYSQL_4_0_CHARSET_NAME_danish = "danish"; // 4.1 => 15 latin1 latin1_danish_ci + private static final String MYSQL_4_0_CHARSET_NAME_dos = "dos"; // 4.1 => 4 cp850 cp850_general_ci + private static final String MYSQL_4_0_CHARSET_NAME_estonia = "estonia"; // 4.1 => 20 latin7 latin7_estonian_ci + private static final String MYSQL_4_0_CHARSET_NAME_euc_kr = "euc_kr"; // 4.1 => 19 euckr euckr_korean_ci + private static final String MYSQL_4_0_CHARSET_NAME_german1 = "german1"; // 4.1 => 5 latin1 latin1_german1_ci + private static final String MYSQL_4_0_CHARSET_NAME_hungarian = "hungarian"; // 4.1 => 21 latin2 latin2_hungarian_ci + private static final String MYSQL_4_0_CHARSET_NAME_koi8_ru = "koi8_ru"; // 4.1 => 7 koi8r koi8r_general_ci + private static final String MYSQL_4_0_CHARSET_NAME_koi8_ukr = "koi8_ukr"; // 4.1 => 22 koi8u koi8u_ukrainian_ci + private static final String MYSQL_4_0_CHARSET_NAME_latin1_de = "latin1_de"; // 4.1 => 31 latin1 latin1_german2_ci + private static final String MYSQL_4_0_CHARSET_NAME_latvian = "latvian"; + private static final String MYSQL_4_0_CHARSET_NAME_latvian1 = "latvian1"; + private static final String MYSQL_4_0_CHARSET_NAME_usa7 = "usa7"; // 4.1 => 11 ascii ascii_general_ci + private static final String MYSQL_4_0_CHARSET_NAME_win1250 = "win1250"; // 4.1 => 26 cp1250 cp1250_general_ci + private static final String MYSQL_4_0_CHARSET_NAME_win1251 = "win1251"; // 4.1 => 17 (removed) + private static final String MYSQL_4_0_CHARSET_NAME_win1251ukr = "win1251ukr"; // 4.1 => 23 cp1251 cp1251_ukrainian_ci + + private static final String NOT_USED = MYSQL_CHARSET_NAME_latin1; // punting for not-used character sets + + public static final int MYSQL_COLLATION_INDEX_utf8 = 33; + public static final int MYSQL_COLLATION_INDEX_binary = 63; + + private static int numberOfEncodingsConfigured = 0; + + static { + // complete list of mysql character sets and their corresponding java encoding names + MysqlCharset[] charset = new MysqlCharset[] { new MysqlCharset(MYSQL_4_0_CHARSET_NAME_usa7, 1, 0, new String[] { "US-ASCII" }, 4, 0), + new MysqlCharset(MYSQL_CHARSET_NAME_ascii, 1, 0, new String[] { "US-ASCII", "ASCII" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_big5, 2, 0, new String[] { "Big5" }), + new MysqlCharset(MYSQL_CHARSET_NAME_gbk, 2, 0, new String[] { "GBK" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_sjis, 2, 0, new String[] { "SHIFT_JIS", "Cp943", "WINDOWS-31J" }), // SJIS is alias for SHIFT_JIS, Cp943 is rather a cp932 but we map it to sjis for years + new MysqlCharset(MYSQL_CHARSET_NAME_cp932, 2, 1, new String[] { "WINDOWS-31J" }), // MS932 is alias for WINDOWS-31J + + new MysqlCharset(MYSQL_CHARSET_NAME_gb2312, 2, 0, new String[] { "GB2312" }), + new MysqlCharset(MYSQL_CHARSET_NAME_ujis, 3, 0, new String[] { "EUC_JP" }), + new MysqlCharset(MYSQL_CHARSET_NAME_eucjpms, 3, 0, new String[] { "EUC_JP_Solaris" }, 5, 0, 3), // "EUC_JP_Solaris = >5.0.3 eucjpms," + + new MysqlCharset(MYSQL_CHARSET_NAME_gb18030, 4, 0, new String[] { "GB18030" }, 5, 7, 4), + + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_euc_kr, 2, 0, new String[] { "EUC_KR" }, 4, 0), + new MysqlCharset(MYSQL_CHARSET_NAME_euckr, 2, 0, new String[] { "EUC-KR" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_latin1, 1, 1, new String[] { "Cp1252", "ISO8859_1" }), + new MysqlCharset(MYSQL_CHARSET_NAME_swe7, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_hp8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_dec8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_armscii8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_geostd8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + + new MysqlCharset(MYSQL_CHARSET_NAME_latin2, 1, 0, new String[] { "ISO8859_2" }), // latin2 is an alias + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_czech, 1, 0, new String[] { "ISO8859_2" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_hungarian, 1, 0, new String[] { "ISO8859_2" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_croat, 1, 0, new String[] { "ISO8859_2" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_greek, 1, 0, new String[] { "ISO8859_7", "greek" }), + new MysqlCharset(MYSQL_CHARSET_NAME_latin7, 1, 0, new String[] { "ISO-8859-13" }), // was ISO8859_7, that's incorrect; also + "LATIN7 = latin7," is wrong java encoding name + + new MysqlCharset(MYSQL_CHARSET_NAME_hebrew, 1, 0, new String[] { "ISO8859_8" }), // hebrew is an alias + new MysqlCharset(MYSQL_CHARSET_NAME_latin5, 1, 0, new String[] { "ISO8859_9" }), // LATIN5 is an alias + + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_latvian, 1, 0, new String[] { "ISO8859_13" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_latvian1, 1, 0, new String[] { "ISO8859_13" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_estonia, 1, 1, new String[] { "ISO8859_13" }, 4, 0), //, "ISO8859_13"); // punting for "estonia"; + + new MysqlCharset(MYSQL_CHARSET_NAME_cp850, 1, 0, new String[] { "Cp850", "Cp437" }), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_dos, 1, 0, new String[] { "Cp850", "Cp437" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp852, 1, 0, new String[] { "Cp852" }), + new MysqlCharset(MYSQL_CHARSET_NAME_keybcs2, 1, 0, new String[] { "Cp852" }), // new, Kamenicky encoding usually known as Cp895 but there is no official cp895 specification; close to Cp852, see http://ftp.muni.cz/pub/localization/charsets/cs-encodings-faq + + new MysqlCharset(MYSQL_CHARSET_NAME_cp866, 1, 0, new String[] { "Cp866" }), + + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_koi8_ru, 1, 0, new String[] { "KOI8_R" }, 4, 0), + new MysqlCharset(MYSQL_CHARSET_NAME_koi8r, 1, 1, new String[] { "KOI8_R" }), + new MysqlCharset(MYSQL_CHARSET_NAME_koi8u, 1, 0, new String[] { "KOI8_R" }), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_koi8_ukr, 1, 0, new String[] { "KOI8_R" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_tis620, 1, 0, new String[] { "TIS620" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1250, 1, 0, new String[] { "Cp1250" }), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_win1250, 1, 0, new String[] { "Cp1250" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1251, 1, 1, new String[] { "Cp1251" }), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_win1251, 1, 0, new String[] { "Cp1251" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_cp1251cias, 1, 0, new String[] { "Cp1251" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_cp1251csas, 1, 0, new String[] { "Cp1251" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_win1251ukr, 1, 0, new String[] { "Cp1251" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1256, 1, 0, new String[] { "Cp1256" }), + new MysqlCharset(MYSQL_CHARSET_NAME_cp1257, 1, 0, new String[] { "Cp1257" }), + new MysqlCharset(MYSQL_CHARSET_NAME_macroman, 1, 0, new String[] { "MacRoman" }), + new MysqlCharset(MYSQL_CHARSET_NAME_macce, 1, 0, new String[] { "MacCentralEurope" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_utf8, 3, 1, new String[] { "UTF-8" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf8mb4, 4, 0, new String[] { "UTF-8" }), // "UTF-8 = *> 5.5.2 utf8mb4," + + new MysqlCharset(MYSQL_CHARSET_NAME_ucs2, 2, 0, new String[] { "UnicodeBig" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_binary, 1, 1, new String[] { "ISO8859_1" }), // US-ASCII ? + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_latin1_de, 1, 0, new String[] { "ISO8859_1" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_german1, 1, 0, new String[] { "ISO8859_1" }, 4, 0), + new MysqlCharset(MYSQL_4_0_CHARSET_NAME_danish, 1, 0, new String[] { "ISO8859_1" }, 4, 0), + + new MysqlCharset(MYSQL_CHARSET_NAME_utf16, 4, 0, new String[] { "UTF-16" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf16le, 4, 0, new String[] { "UTF-16LE" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf32, 4, 0, new String[] { "UTF-32" }) + + }; + HashMap charsetNameToMysqlCharsetMap = new HashMap(); + HashMap> javaUcToMysqlCharsetMap = new HashMap>(); + Set tempMultibyteEncodings = new HashSet(); // Character sets that we can't convert ourselves. + Set tempEscapeEncodings = new HashSet(); // Eastern Unicode character sets which require escaping + for (int i = 0; i < charset.length; i++) { + String charsetName = charset[i].charsetName; + + charsetNameToMysqlCharsetMap.put(charsetName, charset[i]); + + numberOfEncodingsConfigured += charset[i].javaEncodingsUc.size(); + + for (String encUC : charset[i].javaEncodingsUc) { + + // fill javaUcToMysqlCharsetMap + List charsets = javaUcToMysqlCharsetMap.get(encUC); + if (charsets == null) { + charsets = new ArrayList(); + javaUcToMysqlCharsetMap.put(encUC, charsets); + } + charsets.add(charset[i]); + + // fill multi-byte charsets + if (charset[i].mblen > 1) { + tempMultibyteEncodings.add(encUC); + } + + } + + // fill EscapeEasternUnicode charsets + // TODO maybe needs more charsets, MS932 eg? + if (charsetName.equals(MYSQL_CHARSET_NAME_big5) || charsetName.equals(MYSQL_CHARSET_NAME_gbk) || charsetName.equals(MYSQL_CHARSET_NAME_sjis)) { + tempEscapeEncodings.addAll(charset[i].javaEncodingsUc); + } + + } + CHARSET_NAME_TO_CHARSET = Collections.unmodifiableMap(charsetNameToMysqlCharsetMap); + JAVA_ENCODING_UC_TO_MYSQL_CHARSET = Collections.unmodifiableMap(javaUcToMysqlCharsetMap); + MULTIBYTE_ENCODINGS = Collections.unmodifiableSet(tempMultibyteEncodings); + ESCAPE_ENCODINGS = Collections.unmodifiableSet(tempEscapeEncodings); + + // complete list of mysql collations and their corresponding character sets each element of collation[1]..collation[MAP_SIZE-1] must not be null + Collation[] collation = new Collation[MAP_SIZE]; + collation[1] = new Collation(1, "big5_chinese_ci", 1, MYSQL_CHARSET_NAME_big5); + collation[84] = new Collation(84, "big5_bin", 0, MYSQL_CHARSET_NAME_big5); + + collation[2] = new Collation(2, "latin2_czech_cs", 0, MYSQL_CHARSET_NAME_latin2); + collation[9] = new Collation(9, "latin2_general_ci", 1, MYSQL_CHARSET_NAME_latin2); + collation[21] = new Collation(21, "latin2_hungarian_ci", 0, MYSQL_CHARSET_NAME_latin2); + collation[27] = new Collation(27, "latin2_croatian_ci", 0, MYSQL_CHARSET_NAME_latin2); + collation[77] = new Collation(77, "latin2_bin", 0, MYSQL_CHARSET_NAME_latin2); + + collation[4] = new Collation(4, "cp850_general_ci", 1, MYSQL_CHARSET_NAME_cp850); + collation[80] = new Collation(80, "cp850_bin", 0, MYSQL_CHARSET_NAME_cp850); + + collation[5] = new Collation(5, "latin1_german1_ci", 1, MYSQL_CHARSET_NAME_latin1); + collation[8] = new Collation(8, "latin1_swedish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[15] = new Collation(15, "latin1_danish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[31] = new Collation(31, "latin1_german2_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[47] = new Collation(47, "latin1_bin", 0, MYSQL_CHARSET_NAME_latin1); + collation[48] = new Collation(48, "latin1_general_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[49] = new Collation(49, "latin1_general_cs", 0, MYSQL_CHARSET_NAME_latin1); + collation[76] = new Collation(76, "not_implemented", 0, NOT_USED); + collation[94] = new Collation(94, "latin1_spanish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[100] = new Collation(100, "not_implemented", 0, NOT_USED); + collation[125] = new Collation(125, "not_implemented", 0, NOT_USED); + collation[126] = new Collation(126, "not_implemented", 0, NOT_USED); + collation[127] = new Collation(127, "not_implemented", 0, NOT_USED); + collation[152] = new Collation(152, "not_implemented", 0, NOT_USED); + collation[153] = new Collation(153, "not_implemented", 0, NOT_USED); + collation[154] = new Collation(154, "not_implemented", 0, NOT_USED); + collation[155] = new Collation(155, "not_implemented", 0, NOT_USED); + collation[156] = new Collation(156, "not_implemented", 0, NOT_USED); + collation[157] = new Collation(157, "not_implemented", 0, NOT_USED); + collation[158] = new Collation(158, "not_implemented", 0, NOT_USED); + collation[184] = new Collation(184, "not_implemented", 0, NOT_USED); + collation[185] = new Collation(185, "not_implemented", 0, NOT_USED); + collation[186] = new Collation(186, "not_implemented", 0, NOT_USED); + collation[187] = new Collation(187, "not_implemented", 0, NOT_USED); + collation[188] = new Collation(188, "not_implemented", 0, NOT_USED); + collation[189] = new Collation(189, "not_implemented", 0, NOT_USED); + collation[190] = new Collation(190, "not_implemented", 0, NOT_USED); + collation[191] = new Collation(191, "not_implemented", 0, NOT_USED); + collation[216] = new Collation(216, "not_implemented", 0, NOT_USED); + collation[217] = new Collation(217, "not_implemented", 0, NOT_USED); + collation[218] = new Collation(218, "not_implemented", 0, NOT_USED); + collation[219] = new Collation(219, "not_implemented", 0, NOT_USED); + collation[220] = new Collation(220, "not_implemented", 0, NOT_USED); + collation[221] = new Collation(221, "not_implemented", 0, NOT_USED); + collation[222] = new Collation(222, "not_implemented", 0, NOT_USED); + collation[248] = new Collation(248, "gb18030_chinese_ci", 1, MYSQL_CHARSET_NAME_gb18030); + collation[249] = new Collation(249, "gb18030_bin", 0, MYSQL_CHARSET_NAME_gb18030); + collation[250] = new Collation(250, "gb18030_unicode_520_ci", 0, MYSQL_CHARSET_NAME_gb18030); + collation[251] = new Collation(251, "not_implemented", 0, NOT_USED); + collation[252] = new Collation(252, "not_implemented", 0, NOT_USED); + collation[253] = new Collation(253, "not_implemented", 0, NOT_USED); + collation[254] = new Collation(254, "not_implemented", 0, NOT_USED); + collation[10] = new Collation(10, "swe7_swedish_ci", 0, MYSQL_CHARSET_NAME_swe7); + collation[82] = new Collation(82, "swe7_bin", 0, MYSQL_CHARSET_NAME_swe7); + collation[6] = new Collation(6, "hp8_english_ci", 0, MYSQL_CHARSET_NAME_hp8); + collation[72] = new Collation(72, "hp8_bin", 0, MYSQL_CHARSET_NAME_hp8); + collation[3] = new Collation(3, "dec8_swedish_ci", 0, MYSQL_CHARSET_NAME_dec8); + collation[69] = new Collation(69, "dec8_bin", 0, MYSQL_CHARSET_NAME_dec8); + collation[32] = new Collation(32, "armscii8_general_ci", 0, MYSQL_CHARSET_NAME_armscii8); + collation[64] = new Collation(64, "armscii8_bin", 0, MYSQL_CHARSET_NAME_armscii8); + collation[92] = new Collation(92, "geostd8_general_ci", 0, MYSQL_CHARSET_NAME_geostd8); + collation[93] = new Collation(93, "geostd8_bin", 0, MYSQL_CHARSET_NAME_geostd8); + + collation[7] = new Collation(7, "koi8r_general_ci", 0, MYSQL_CHARSET_NAME_koi8r); + collation[74] = new Collation(74, "koi8r_bin", 0, MYSQL_CHARSET_NAME_koi8r); + + collation[11] = new Collation(11, "ascii_general_ci", 0, MYSQL_CHARSET_NAME_ascii); + collation[65] = new Collation(65, "ascii_bin", 0, MYSQL_CHARSET_NAME_ascii); + + collation[12] = new Collation(12, "ujis_japanese_ci", 0, MYSQL_CHARSET_NAME_ujis); + collation[91] = new Collation(91, "ujis_bin", 0, MYSQL_CHARSET_NAME_ujis); + + collation[13] = new Collation(13, "sjis_japanese_ci", 0, MYSQL_CHARSET_NAME_sjis); + collation[14] = new Collation(14, "cp1251_bulgarian_ci", 0, MYSQL_CHARSET_NAME_cp1251); + collation[16] = new Collation(16, "hebrew_general_ci", 0, MYSQL_CHARSET_NAME_hebrew); + collation[17] = new Collation(17, "latin1_german1_ci", 0, MYSQL_4_0_CHARSET_NAME_win1251); // removed since 4.1 + collation[18] = new Collation(18, "tis620_thai_ci", 0, MYSQL_CHARSET_NAME_tis620); + collation[19] = new Collation(19, "euckr_korean_ci", 0, MYSQL_CHARSET_NAME_euckr); + collation[20] = new Collation(20, "latin7_estonian_cs", 0, MYSQL_CHARSET_NAME_latin7); + collation[22] = new Collation(22, "koi8u_general_ci", 0, MYSQL_CHARSET_NAME_koi8u); + collation[23] = new Collation(23, "cp1251_ukrainian_ci", 0, MYSQL_CHARSET_NAME_cp1251); + collation[24] = new Collation(24, "gb2312_chinese_ci", 0, MYSQL_CHARSET_NAME_gb2312); + collation[25] = new Collation(25, "greek_general_ci", 0, MYSQL_CHARSET_NAME_greek); + collation[26] = new Collation(26, "cp1250_general_ci", 1, MYSQL_CHARSET_NAME_cp1250); + collation[28] = new Collation(28, "gbk_chinese_ci", 1, MYSQL_CHARSET_NAME_gbk); + collation[29] = new Collation(29, "cp1257_lithuanian_ci", 0, MYSQL_CHARSET_NAME_cp1257); + collation[30] = new Collation(30, "latin5_turkish_ci", 1, MYSQL_CHARSET_NAME_latin5); + collation[33] = new Collation(33, "utf8_general_ci", 1, MYSQL_CHARSET_NAME_utf8); + collation[34] = new Collation(34, "cp1250_czech_cs", 0, MYSQL_CHARSET_NAME_cp1250); + collation[35] = new Collation(35, "ucs2_general_ci", 1, MYSQL_CHARSET_NAME_ucs2); + collation[36] = new Collation(36, "cp866_general_ci", 1, MYSQL_CHARSET_NAME_cp866); + collation[37] = new Collation(37, "keybcs2_general_ci", 1, MYSQL_CHARSET_NAME_keybcs2); + collation[38] = new Collation(38, "macce_general_ci", 1, MYSQL_CHARSET_NAME_macce); + collation[39] = new Collation(39, "macroman_general_ci", 1, MYSQL_CHARSET_NAME_macroman); + collation[40] = new Collation(40, "cp852_general_ci", 1, MYSQL_CHARSET_NAME_cp852); + collation[41] = new Collation(41, "latin7_general_ci", 1, MYSQL_CHARSET_NAME_latin7); + collation[42] = new Collation(42, "latin7_general_cs", 0, MYSQL_CHARSET_NAME_latin7); + collation[43] = new Collation(43, "macce_bin", 0, MYSQL_CHARSET_NAME_macce); + collation[44] = new Collation(44, "cp1250_croatian_ci", 0, MYSQL_CHARSET_NAME_cp1250); + collation[45] = new Collation(45, "utf8mb4_general_ci", 1, MYSQL_CHARSET_NAME_utf8mb4); + collation[46] = new Collation(46, "utf8mb4_bin", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[50] = new Collation(50, "cp1251_bin", 0, MYSQL_CHARSET_NAME_cp1251); + collation[51] = new Collation(51, "cp1251_general_ci", 1, MYSQL_CHARSET_NAME_cp1251); + collation[52] = new Collation(52, "cp1251_general_cs", 0, MYSQL_CHARSET_NAME_cp1251); + collation[53] = new Collation(53, "macroman_bin", 0, MYSQL_CHARSET_NAME_macroman); + collation[54] = new Collation(54, "utf16_general_ci", 1, MYSQL_CHARSET_NAME_utf16); + collation[55] = new Collation(55, "utf16_bin", 0, MYSQL_CHARSET_NAME_utf16); + collation[56] = new Collation(56, "utf16le_general_ci", 1, MYSQL_CHARSET_NAME_utf16le); + collation[57] = new Collation(57, "cp1256_general_ci", 1, MYSQL_CHARSET_NAME_cp1256); + collation[58] = new Collation(58, "cp1257_bin", 0, MYSQL_CHARSET_NAME_cp1257); + collation[59] = new Collation(59, "cp1257_general_ci", 1, MYSQL_CHARSET_NAME_cp1257); + collation[60] = new Collation(60, "utf32_general_ci", 1, MYSQL_CHARSET_NAME_utf32); + collation[61] = new Collation(61, "utf32_bin", 0, MYSQL_CHARSET_NAME_utf32); + collation[62] = new Collation(62, "utf16le_bin", 0, MYSQL_CHARSET_NAME_utf16le); + collation[63] = new Collation(63, "binary", 1, MYSQL_CHARSET_NAME_binary); + collation[66] = new Collation(66, "cp1250_bin", 0, MYSQL_CHARSET_NAME_cp1250); + collation[67] = new Collation(67, "cp1256_bin", 0, MYSQL_CHARSET_NAME_cp1256); + collation[68] = new Collation(68, "cp866_bin", 0, MYSQL_CHARSET_NAME_cp866); + collation[70] = new Collation(70, "greek_bin", 0, MYSQL_CHARSET_NAME_greek); + collation[71] = new Collation(71, "hebrew_bin", 0, MYSQL_CHARSET_NAME_hebrew); + collation[73] = new Collation(73, "keybcs2_bin", 0, MYSQL_CHARSET_NAME_keybcs2); + collation[75] = new Collation(75, "koi8u_bin", 0, MYSQL_CHARSET_NAME_koi8u); + collation[78] = new Collation(78, "latin5_bin", 0, MYSQL_CHARSET_NAME_latin5); + collation[79] = new Collation(79, "latin7_bin", 0, MYSQL_CHARSET_NAME_latin7); + collation[81] = new Collation(81, "cp852_bin", 0, MYSQL_CHARSET_NAME_cp852); + collation[83] = new Collation(83, "utf8_bin", 0, MYSQL_CHARSET_NAME_utf8); + collation[85] = new Collation(85, "euckr_bin", 0, MYSQL_CHARSET_NAME_euckr); + collation[86] = new Collation(86, "gb2312_bin", 0, MYSQL_CHARSET_NAME_gb2312); + collation[87] = new Collation(87, "gbk_bin", 0, MYSQL_CHARSET_NAME_gbk); + collation[88] = new Collation(88, "sjis_bin", 0, MYSQL_CHARSET_NAME_sjis); + collation[89] = new Collation(89, "tis620_bin", 0, MYSQL_CHARSET_NAME_tis620); + collation[90] = new Collation(90, "ucs2_bin", 0, MYSQL_CHARSET_NAME_ucs2); + collation[95] = new Collation(95, "cp932_japanese_ci", 1, MYSQL_CHARSET_NAME_cp932); + collation[96] = new Collation(96, "cp932_bin", 0, MYSQL_CHARSET_NAME_cp932); + collation[97] = new Collation(97, "eucjpms_japanese_ci", 1, MYSQL_CHARSET_NAME_eucjpms); + collation[98] = new Collation(98, "eucjpms_bin", 0, MYSQL_CHARSET_NAME_eucjpms); + collation[99] = new Collation(99, "cp1250_polish_ci", 0, MYSQL_CHARSET_NAME_cp1250); + collation[101] = new Collation(101, "utf16_unicode_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[102] = new Collation(102, "utf16_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[103] = new Collation(103, "utf16_latvian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[104] = new Collation(104, "utf16_romanian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[105] = new Collation(105, "utf16_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[106] = new Collation(106, "utf16_polish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[107] = new Collation(107, "utf16_estonian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[108] = new Collation(108, "utf16_spanish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[109] = new Collation(109, "utf16_swedish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[110] = new Collation(110, "utf16_turkish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[111] = new Collation(111, "utf16_czech_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[112] = new Collation(112, "utf16_danish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[113] = new Collation(113, "utf16_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[114] = new Collation(114, "utf16_slovak_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[115] = new Collation(115, "utf16_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[116] = new Collation(116, "utf16_roman_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[117] = new Collation(117, "utf16_persian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[118] = new Collation(118, "utf16_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[119] = new Collation(119, "utf16_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[120] = new Collation(120, "utf16_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[121] = new Collation(121, "utf16_german2_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[122] = new Collation(122, "utf16_croatian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[123] = new Collation(123, "utf16_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[124] = new Collation(124, "utf16_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[128] = new Collation(128, "ucs2_unicode_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[129] = new Collation(129, "ucs2_icelandic_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[130] = new Collation(130, "ucs2_latvian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[131] = new Collation(131, "ucs2_romanian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[132] = new Collation(132, "ucs2_slovenian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[133] = new Collation(133, "ucs2_polish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[134] = new Collation(134, "ucs2_estonian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[135] = new Collation(135, "ucs2_spanish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[136] = new Collation(136, "ucs2_swedish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[137] = new Collation(137, "ucs2_turkish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[138] = new Collation(138, "ucs2_czech_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[139] = new Collation(139, "ucs2_danish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[140] = new Collation(140, "ucs2_lithuanian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[141] = new Collation(141, "ucs2_slovak_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[142] = new Collation(142, "ucs2_spanish2_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[143] = new Collation(143, "ucs2_roman_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[144] = new Collation(144, "ucs2_persian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[145] = new Collation(145, "ucs2_esperanto_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[146] = new Collation(146, "ucs2_hungarian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[147] = new Collation(147, "ucs2_sinhala_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[148] = new Collation(148, "ucs2_german2_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[149] = new Collation(149, "ucs2_croatian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[150] = new Collation(150, "ucs2_unicode_520_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[151] = new Collation(151, "ucs2_vietnamese_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[159] = new Collation(159, "ucs2_general_mysql500_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[160] = new Collation(160, "utf32_unicode_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[161] = new Collation(161, "utf32_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[162] = new Collation(162, "utf32_latvian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[163] = new Collation(163, "utf32_romanian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[164] = new Collation(164, "utf32_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[165] = new Collation(165, "utf32_polish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[166] = new Collation(166, "utf32_estonian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[167] = new Collation(167, "utf32_spanish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[168] = new Collation(168, "utf32_swedish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[169] = new Collation(169, "utf32_turkish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[170] = new Collation(170, "utf32_czech_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[171] = new Collation(171, "utf32_danish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[172] = new Collation(172, "utf32_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[173] = new Collation(173, "utf32_slovak_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[174] = new Collation(174, "utf32_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[175] = new Collation(175, "utf32_roman_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[176] = new Collation(176, "utf32_persian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[177] = new Collation(177, "utf32_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[178] = new Collation(178, "utf32_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[179] = new Collation(179, "utf32_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[180] = new Collation(180, "utf32_german2_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[181] = new Collation(181, "utf32_croatian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[182] = new Collation(182, "utf32_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[183] = new Collation(183, "utf32_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[192] = new Collation(192, "utf8_unicode_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[193] = new Collation(193, "utf8_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[194] = new Collation(194, "utf8_latvian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[195] = new Collation(195, "utf8_romanian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[196] = new Collation(196, "utf8_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[197] = new Collation(197, "utf8_polish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[198] = new Collation(198, "utf8_estonian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[199] = new Collation(199, "utf8_spanish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[200] = new Collation(200, "utf8_swedish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[201] = new Collation(201, "utf8_turkish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[202] = new Collation(202, "utf8_czech_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[203] = new Collation(203, "utf8_danish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[204] = new Collation(204, "utf8_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[205] = new Collation(205, "utf8_slovak_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[206] = new Collation(206, "utf8_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[207] = new Collation(207, "utf8_roman_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[208] = new Collation(208, "utf8_persian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[209] = new Collation(209, "utf8_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[210] = new Collation(210, "utf8_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[211] = new Collation(211, "utf8_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[212] = new Collation(212, "utf8_german2_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[213] = new Collation(213, "utf8_croatian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[214] = new Collation(214, "utf8_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[215] = new Collation(215, "utf8_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[223] = new Collation(223, "utf8_general_mysql500_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[224] = new Collation(224, "utf8mb4_unicode_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[225] = new Collation(225, "utf8mb4_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[226] = new Collation(226, "utf8mb4_latvian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[227] = new Collation(227, "utf8mb4_romanian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[228] = new Collation(228, "utf8mb4_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[229] = new Collation(229, "utf8mb4_polish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[230] = new Collation(230, "utf8mb4_estonian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[231] = new Collation(231, "utf8mb4_spanish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[232] = new Collation(232, "utf8mb4_swedish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[233] = new Collation(233, "utf8mb4_turkish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[234] = new Collation(234, "utf8mb4_czech_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[235] = new Collation(235, "utf8mb4_danish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[236] = new Collation(236, "utf8mb4_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[237] = new Collation(237, "utf8mb4_slovak_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[238] = new Collation(238, "utf8mb4_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[239] = new Collation(239, "utf8mb4_roman_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[240] = new Collation(240, "utf8mb4_persian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[241] = new Collation(241, "utf8mb4_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[242] = new Collation(242, "utf8mb4_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[243] = new Collation(243, "utf8mb4_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[244] = new Collation(244, "utf8mb4_german2_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[245] = new Collation(245, "utf8mb4_croatian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[246] = new Collation(246, "utf8mb4_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[247] = new Collation(247, "utf8mb4_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + + COLLATION_INDEX_TO_COLLATION_NAME = new String[MAP_SIZE]; + COLLATION_INDEX_TO_CHARSET = new MysqlCharset[MAP_SIZE]; + Map charsetNameToCollationIndexMap = new TreeMap(); + Map charsetNameToCollationPriorityMap = new TreeMap(); + Set tempUTF8MB4Indexes = new HashSet(); + + for (int i = 1; i < MAP_SIZE; i++) { + COLLATION_INDEX_TO_COLLATION_NAME[i] = collation[i].collationName; + COLLATION_INDEX_TO_CHARSET[i] = collation[i].mysqlCharset; + String charsetName = collation[i].mysqlCharset.charsetName; + + if (!charsetNameToCollationIndexMap.containsKey(charsetName) || charsetNameToCollationPriorityMap.get(charsetName) < collation[i].priority) { + charsetNameToCollationIndexMap.put(charsetName, i); + charsetNameToCollationPriorityMap.put(charsetName, collation[i].priority); + } + + // Filling indexes of utf8mb4 collations + if (charsetName.equals(MYSQL_CHARSET_NAME_utf8mb4)) { + tempUTF8MB4Indexes.add(i); + } + } + + // Sanity check + for (int i = 1; i < MAP_SIZE; i++) { + if (COLLATION_INDEX_TO_COLLATION_NAME[i] == null) { + throw new RuntimeException("Assertion failure: No mapping from charset index " + i + " to a mysql collation"); + } + if (COLLATION_INDEX_TO_COLLATION_NAME[i] == null) { + throw new RuntimeException("Assertion failure: No mapping from charset index " + i + " to a Java character set"); + } + } + + CHARSET_NAME_TO_COLLATION_INDEX = Collections.unmodifiableMap(charsetNameToCollationIndexMap); + UTF8MB4_INDEXES = Collections.unmodifiableSet(tempUTF8MB4Indexes); + + /** + * Charsets for error messages for servers < 5.5 + */ + Map tempMap = new HashMap(); + tempMap.put("czech", "latin2"); + tempMap.put("danish", "latin1"); + tempMap.put("dutch", "latin1"); + tempMap.put("english", "latin1"); + tempMap.put("estonian", "latin7"); + tempMap.put("french", "latin1"); + tempMap.put("german", "latin1"); + tempMap.put("greek", "greek"); + tempMap.put("hungarian", "latin2"); + tempMap.put("italian", "latin1"); + tempMap.put("japanese", "ujis"); + tempMap.put("japanese-sjis", "sjis"); + tempMap.put("korean", "euckr"); + tempMap.put("norwegian", "latin1"); + tempMap.put("norwegian-ny", "latin1"); + tempMap.put("polish", "latin2"); + tempMap.put("portuguese", "latin1"); + tempMap.put("romanian", "latin2"); + tempMap.put("russian", "koi8r"); + tempMap.put("serbian", "cp1250"); + tempMap.put("slovak", "latin2"); + tempMap.put("spanish", "latin1"); + tempMap.put("swedish", "latin1"); + tempMap.put("ukrainian", "koi8u"); + ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET = Collections.unmodifiableMap(tempMap); + } + + public final static String getMysqlCharsetForJavaEncoding(String javaEncoding, Connection conn) throws SQLException { + + try { + List mysqlCharsets = CharsetMapping.JAVA_ENCODING_UC_TO_MYSQL_CHARSET.get(javaEncoding.toUpperCase(Locale.ENGLISH)); + + if (mysqlCharsets != null) { + Iterator iter = mysqlCharsets.iterator(); + + MysqlCharset versionedProp = null; + + while (iter.hasNext()) { + MysqlCharset charset = iter.next(); + + if (conn == null) { + // Take the first one we get + + return charset.charsetName; + } + + if (versionedProp == null || versionedProp.major < charset.major || versionedProp.minor < charset.minor + || versionedProp.subminor < charset.subminor || versionedProp.priority < charset.priority) { + if (charset.isOkayForVersion(conn)) { + versionedProp = charset; + } + } + } + + if (versionedProp != null) { + return versionedProp.charsetName; + } + } + + return null; + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + } + + public static int getCollationIndexForJavaEncoding(String javaEncoding, java.sql.Connection conn) throws SQLException { + String charsetName = getMysqlCharsetForJavaEncoding(javaEncoding, (Connection) conn); + if (charsetName != null) { + Integer ci = CHARSET_NAME_TO_COLLATION_INDEX.get(charsetName); + if (ci != null) { + return ci.intValue(); + } + } + return 0; + } + + public static String getMysqlCharsetNameForCollationIndex(Integer collationIndex) { + if (collationIndex != null && collationIndex > 0 && collationIndex < MAP_SIZE) { + return COLLATION_INDEX_TO_CHARSET[collationIndex].charsetName; + } + return null; + } + + /** + * MySQL charset could map to several Java encodings. + * So here we choose the one according to next rules: + *

  • if there is no static mapping for this charset then return javaEncoding value as is because this + * could be a custom charset for example + *
  • if static mapping exists and javaEncoding equals to one of Java encoding canonical names or aliases available + * for this mapping then javaEncoding value as is; this is required when result should match to connection encoding, for example if connection encoding is + * Cp943 we must avoid getting SHIFT_JIS for sjis mysql charset + *
  • if static mapping exists and javaEncoding doesn't match any Java encoding canonical + * names or aliases available for this mapping then return default Java encoding (the first in mapping list) + * + * @param mysqlCharsetName + * @param javaEncoding + */ + public static String getJavaEncodingForMysqlCharset(String mysqlCharsetName, String javaEncoding) { + String res = javaEncoding; + MysqlCharset cs = CHARSET_NAME_TO_CHARSET.get(mysqlCharsetName); + if (cs != null) { + res = cs.getMatchingJavaEncoding(javaEncoding); + } + return res; + } + + public static String getJavaEncodingForMysqlCharset(String mysqlCharsetName) { + return getJavaEncodingForMysqlCharset(mysqlCharsetName, null); + } + + public static String getJavaEncodingForCollationIndex(Integer collationIndex, String javaEncoding) { + if (collationIndex != null && collationIndex > 0 && collationIndex < MAP_SIZE) { + MysqlCharset cs = COLLATION_INDEX_TO_CHARSET[collationIndex]; + return cs.getMatchingJavaEncoding(javaEncoding); + } + return null; + } + + public static String getJavaEncodingForCollationIndex(Integer collationIndex) { + return getJavaEncodingForCollationIndex(collationIndex, null); + } + + final static int getNumberOfCharsetsConfigured() { + return numberOfEncodingsConfigured; + } + + /** + * Returns the character encoding for error messages returned from the + * server. Doesn't return useful values other than Cp1252 until the driver + * has gone through initialization phase and determined server configuration, + * as not enough information is available to make an intelligent decision + * until then. + * + * @param conn + * the connection to the MySQL server + * @return the Java encoding name that error messages use + * @throws SQLException + * if determination of the character encoding fails + */ + final static String getCharacterEncodingForErrorMessages(ConnectionImpl conn) throws SQLException { + + // As of MySQL 5.5, the server constructs error messages using UTF-8 and returns them to clients in the character set specified by the + // character_set_results system variable. + if (conn.versionMeetsMinimum(5, 5, 0)) { + String errorMessageCharsetName = conn.getServerVariable(ConnectionImpl.JDBC_LOCAL_CHARACTER_SET_RESULTS); + if (errorMessageCharsetName != null) { + String javaEncoding = getJavaEncodingForMysqlCharset(errorMessageCharsetName); + if (javaEncoding != null) { + return javaEncoding; + } + } + + return "UTF-8"; + } + + String errorMessageFile = conn.getServerVariable("language"); + + if (errorMessageFile == null || errorMessageFile.length() == 0) { + // punt + return "Cp1252"; + } + + int endWithoutSlash = errorMessageFile.length(); + + if (errorMessageFile.endsWith("/") || errorMessageFile.endsWith("\\")) { + endWithoutSlash--; + } + + int lastSlashIndex = errorMessageFile.lastIndexOf('/', endWithoutSlash - 1); + + if (lastSlashIndex == -1) { + lastSlashIndex = errorMessageFile.lastIndexOf('\\', endWithoutSlash - 1); + } + + if (lastSlashIndex == -1) { + lastSlashIndex = 0; + } + + if (lastSlashIndex == endWithoutSlash || endWithoutSlash < lastSlashIndex) { + // punt + return "Cp1252"; + } + + errorMessageFile = errorMessageFile.substring(lastSlashIndex + 1, endWithoutSlash); + + String errorMessageEncodingMysql = ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET.get(errorMessageFile); + + if (errorMessageEncodingMysql == null) { + // punt + return "Cp1252"; + } + + String javaEncoding = getJavaEncodingForMysqlCharset(errorMessageEncodingMysql); + + if (javaEncoding == null) { + // punt + return "Cp1252"; + } + + return javaEncoding; + } + + final static boolean requiresEscapeEasternUnicode(String javaEncodingName) { + return ESCAPE_ENCODINGS.contains(javaEncodingName.toUpperCase(Locale.ENGLISH)); + } + + /** + * Character sets that we can't convert ourselves. + * + * @param javaEncodingName + */ + final public static boolean isMultibyteCharset(String javaEncodingName) { + return MULTIBYTE_ENCODINGS.contains(javaEncodingName.toUpperCase(Locale.ENGLISH)); + } + + public static int getMblen(String charsetName) { + if (charsetName != null) { + MysqlCharset cs = CHARSET_NAME_TO_CHARSET.get(charsetName); + if (cs != null) { + return cs.mblen; + } + } + return 0; + } +} + +class MysqlCharset { + public final String charsetName; + public final int mblen; + public final int priority; + public final List javaEncodingsUc = new ArrayList(); + + public int major = 4; + public int minor = 1; + public int subminor = 0; + + /** + * Constructs MysqlCharset object + * + * @param charsetName + * MySQL charset name + * @param mblen + * Max number of bytes per character + * @param priority + * MysqlCharset with highest lever of this param will be used for Java encoding --> Mysql charsets conversion. + * @param javaEncodings + * List of Java encodings corresponding to this MySQL charset; the first name in list is the default for mysql --> java data conversion + */ + public MysqlCharset(String charsetName, int mblen, int priority, String[] javaEncodings) { + this.charsetName = charsetName; + this.mblen = mblen; + this.priority = priority; + + for (int i = 0; i < javaEncodings.length; i++) { + String encoding = javaEncodings[i]; + try { + Charset cs = Charset.forName(encoding); + addEncodingMapping(cs.name()); + + Set als = cs.aliases(); + Iterator ali = als.iterator(); + while (ali.hasNext()) { + addEncodingMapping(ali.next()); + } + } catch (Exception e) { + // if there is no support of this charset in JVM it's still possible to use our converter for 1-byte charsets + if (mblen == 1) { + addEncodingMapping(encoding); + } + } + } + + if (this.javaEncodingsUc.size() == 0) { + if (mblen > 1) { + addEncodingMapping("UTF-8"); + } else { + addEncodingMapping("Cp1252"); + } + } + } + + private void addEncodingMapping(String encoding) { + String encodingUc = encoding.toUpperCase(Locale.ENGLISH); + + if (!this.javaEncodingsUc.contains(encodingUc)) { + this.javaEncodingsUc.add(encodingUc); + } + } + + public MysqlCharset(String charsetName, int mblen, int priority, String[] javaEncodings, int major, int minor) { + this(charsetName, mblen, priority, javaEncodings); + this.major = major; + this.minor = minor; + } + + public MysqlCharset(String charsetName, int mblen, int priority, String[] javaEncodings, int major, int minor, int subminor) { + this(charsetName, mblen, priority, javaEncodings); + this.major = major; + this.minor = minor; + this.subminor = subminor; + } + + @Override + public String toString() { + StringBuilder asString = new StringBuilder(); + asString.append("["); + asString.append("charsetName="); + asString.append(this.charsetName); + asString.append(",mblen="); + asString.append(this.mblen); + // asString.append(",javaEncoding="); + // asString.append(this.javaEncodings.toString()); + asString.append("]"); + return asString.toString(); + } + + boolean isOkayForVersion(Connection conn) throws SQLException { + return conn.versionMeetsMinimum(this.major, this.minor, this.subminor); + } + + /** + * If javaEncoding parameter value is one of available java encodings for this charset + * then returns javaEncoding value as is. Otherwise returns first available java encoding name. + * + * @param javaEncoding + * @throws SQLException + */ + String getMatchingJavaEncoding(String javaEncoding) { + if (javaEncoding != null && this.javaEncodingsUc.contains(javaEncoding.toUpperCase(Locale.ENGLISH))) { + return javaEncoding; + } + return this.javaEncodingsUc.get(0); + } +} + +class Collation { + public final int index; + public final String collationName; + public final int priority; + public final MysqlCharset mysqlCharset; + + public Collation(int index, String collationName, int priority, String charsetName) { + this.index = index; + this.collationName = collationName; + this.priority = priority; + this.mysqlCharset = CharsetMapping.CHARSET_NAME_TO_CHARSET.get(charsetName); + } + + @Override + public String toString() { + StringBuilder asString = new StringBuilder(); + asString.append("["); + asString.append("index="); + asString.append(this.index); + asString.append(",collationName="); + asString.append(this.collationName); + asString.append(",charsetName="); + asString.append(this.mysqlCharset.charsetName); + asString.append(",javaCharsetName="); + asString.append(this.mysqlCharset.getMatchingJavaEncoding(null)); + asString.append("]"); + return asString.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Charsets.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Charsets.properties new file mode 100644 index 0000000..ee83a93 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Charsets.properties @@ -0,0 +1,102 @@ +# +# Charset Mappings +# +# Java Encoding MySQL Name (and version, '*' +# denotes preferred value) +# + +javaToMysqlMappings=\ + US-ASCII = usa7,\ + US-ASCII = ascii,\ + Big5 = big5,\ + GBK = gbk,\ + SJIS = sjis,\ + EUC_CN = gb2312,\ + EUC_JP = ujis,\ + EUC_JP_Solaris = >5.0.3 eucjpms,\ + EUC_KR = euc_kr,\ + EUC_KR = >4.1.0 euckr,\ + ISO8859_1 = *latin1,\ + ISO8859_1 = latin1_de,\ + ISO8859_1 = german1,\ + ISO8859_1 = danish,\ + ISO8859_2 = latin2,\ + ISO8859_2 = czech,\ + ISO8859_2 = hungarian,\ + ISO8859_2 = croat,\ + ISO8859_7 = greek,\ + ISO8859_7 = latin7,\ + ISO8859_8 = hebrew,\ + ISO8859_9 = latin5,\ + ISO8859_13 = latvian,\ + ISO8859_13 = latvian1,\ + ISO8859_13 = estonia,\ + Cp437 = *>4.1.0 cp850,\ + Cp437 = dos,\ + Cp850 = Cp850,\ + Cp852 = Cp852,\ + Cp866 = cp866,\ + KOI8_R = koi8_ru,\ + KOI8_R = >4.1.0 koi8r,\ + TIS620 = tis620,\ + Cp1250 = cp1250,\ + Cp1250 = win1250,\ + Cp1251 = *>4.1.0 cp1251,\ + Cp1251 = win1251,\ + Cp1251 = cp1251cias,\ + Cp1251 = cp1251csas,\ + Cp1256 = cp1256,\ + Cp1251 = win1251ukr,\ + Cp1257 = cp1257,\ + MacRoman = macroman,\ + MacCentralEurope = macce,\ + UTF-8 = utf8,\ + UnicodeBig = ucs2,\ + US-ASCII = binary,\ + Cp943 = sjis,\ + MS932 = sjis,\ + MS932 = >4.1.11 cp932,\ + WINDOWS-31J = sjis,\ + WINDOWS-31J = >4.1.11 cp932,\ + CP932 = sjis,\ + CP932 = *>4.1.11 cp932,\ + SHIFT_JIS = sjis,\ + ASCII = ascii,\ + LATIN5 = latin5,\ + LATIN7 = latin7,\ + HEBREW = hebrew,\ + GREEK = greek,\ + EUCKR = euckr,\ + GB2312 = gb2312,\ + LATIN2 = latin2 + +# +# List of multibyte character sets that can not +# use efficient charset conversion or escaping +# +# This map is made case-insensitive inside CharsetMapping +# +# Java Name MySQL Name (not currently used) + +multibyteCharsets=\ + Big5 = big5,\ + GBK = gbk,\ + SJIS = sjis,\ + EUC_CN = gb2312,\ + EUC_JP = ujis,\ + EUC_JP_Solaris = eucjpms,\ + EUC_KR = euc_kr,\ + EUC_KR = >4.1.0 euckr,\ + Cp943 = sjis,\ + Cp943 = cp943,\ + WINDOWS-31J = sjis,\ + WINDOWS-31J = cp932,\ + CP932 = cp932,\ + MS932 = sjis,\ + MS932 = cp932,\ + SHIFT_JIS = sjis,\ + EUCKR = euckr,\ + GB2312 = gb2312,\ + UTF-8 = utf8,\ + utf8 = utf8,\ + UnicodeBig = ucs2 \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Clob.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Clob.java new file mode 100644 index 0000000..0460e49 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Clob.java @@ -0,0 +1,286 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.sql.SQLException; + +/** + * Simplistic implementation of java.sql.Clob for MySQL Connector/J + */ +public class Clob implements java.sql.Clob, OutputStreamWatcher, WriterWatcher { + private String charData; + private ExceptionInterceptor exceptionInterceptor; + + Clob(ExceptionInterceptor exceptionInterceptor) { + this.charData = ""; + this.exceptionInterceptor = exceptionInterceptor; + } + + Clob(String charDataInit, ExceptionInterceptor exceptionInterceptor) { + this.charData = charDataInit; + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * @see java.sql.Clob#getAsciiStream() + */ + public InputStream getAsciiStream() throws SQLException { + if (this.charData != null) { + return new ByteArrayInputStream(StringUtils.getBytes(this.charData)); + } + + return null; + } + + /** + * @see java.sql.Clob#getCharacterStream() + */ + public Reader getCharacterStream() throws SQLException { + if (this.charData != null) { + return new StringReader(this.charData); + } + + return null; + } + + /** + * @see java.sql.Clob#getSubString(long, int) + */ + public String getSubString(long startPos, int length) throws SQLException { + if (startPos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.6"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + int adjustedStartPos = (int) startPos - 1; + int adjustedEndIndex = adjustedStartPos + length; + + if (this.charData != null) { + if (adjustedEndIndex > this.charData.length()) { + throw SQLError.createSQLException(Messages.getString("Clob.7"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + return this.charData.substring(adjustedStartPos, adjustedEndIndex); + } + + return null; + } + + /** + * @see java.sql.Clob#length() + */ + public long length() throws SQLException { + if (this.charData != null) { + return this.charData.length(); + } + + return 0; + } + + /** + * @see java.sql.Clob#position(Clob, long) + */ + public long position(java.sql.Clob arg0, long arg1) throws SQLException { + return position(arg0.getSubString(1L, (int) arg0.length()), arg1); + } + + /** + * @see java.sql.Clob#position(String, long) + */ + public long position(String stringToFind, long startPos) throws SQLException { + if (startPos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.8") + startPos + Messages.getString("Clob.9"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + if (this.charData != null) { + if ((startPos - 1) > this.charData.length()) { + throw SQLError.createSQLException(Messages.getString("Clob.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + int pos = this.charData.indexOf(stringToFind, (int) (startPos - 1)); + + return (pos == -1) ? (-1) : (pos + 1); + } + + return -1; + } + + /** + * @see java.sql.Clob#setAsciiStream(long) + */ + public OutputStream setAsciiStream(long indexToWriteAt) throws SQLException { + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.0"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableOutputStream bytesOut = new WatchableOutputStream(); + bytesOut.setWatcher(this); + + if (indexToWriteAt > 0) { + bytesOut.write(StringUtils.getBytes(this.charData), 0, (int) (indexToWriteAt - 1)); + } + + return bytesOut; + } + + /** + * @see java.sql.Clob#setCharacterStream(long) + */ + public Writer setCharacterStream(long indexToWriteAt) throws SQLException { + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.1"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableWriter writer = new WatchableWriter(); + writer.setWatcher(this); + + // + // Don't call write() if nothing to write... + // + if (indexToWriteAt > 1) { + writer.write(this.charData, 0, (int) (indexToWriteAt - 1)); + } + + return writer; + } + + /** + * @see java.sql.Clob#setString(long, String) + */ + public int setString(long pos, String str) throws SQLException { + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.2"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (str == null) { + throw SQLError.createSQLException(Messages.getString("Clob.3"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + StringBuilder charBuf = new StringBuilder(this.charData); + + pos--; + + int strLength = str.length(); + + charBuf.replace((int) pos, (int) (pos + strLength), str); + + this.charData = charBuf.toString(); + + return strLength; + } + + /** + * @see java.sql.Clob#setString(long, String, int, int) + */ + public int setString(long pos, String str, int offset, int len) throws SQLException { + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.4"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (str == null) { + throw SQLError.createSQLException(Messages.getString("Clob.5"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + StringBuilder charBuf = new StringBuilder(this.charData); + + pos--; + + try { + String replaceString = str.substring(offset, offset + len); + + charBuf.replace((int) pos, (int) (pos + replaceString.length()), replaceString); + } catch (StringIndexOutOfBoundsException e) { + throw SQLError.createSQLException(e.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, e, this.exceptionInterceptor); + } + + this.charData = charBuf.toString(); + + return len; + } + + /** + * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[]) + */ + public void streamClosed(WatchableOutputStream out) { + int streamSize = out.size(); + + if (streamSize < this.charData.length()) { + try { + out.write(StringUtils.getBytes(this.charData, null, null, false, null, this.exceptionInterceptor), streamSize, + this.charData.length() - streamSize); + } catch (SQLException ex) { + // + } + } + + this.charData = StringUtils.toAsciiString(out.toByteArray()); + } + + /** + * @see java.sql.Clob#truncate(long) + */ + public void truncate(long length) throws SQLException { + if (length > this.charData.length()) { + throw SQLError.createSQLException( + Messages.getString("Clob.11") + this.charData.length() + Messages.getString("Clob.12") + length + Messages.getString("Clob.13"), + this.exceptionInterceptor); + } + + this.charData = this.charData.substring(0, (int) length); + } + + /** + * @see com.mysql.jdbc.WriterWatcher#writerClosed(char[]) + */ + public void writerClosed(char[] charDataBeingWritten) { + this.charData = new String(charDataBeingWritten); + } + + /** + * @see com.mysql.jdbc.WriterWatcher#writerClosed(char[]) + */ + public void writerClosed(WatchableWriter out) { + int dataLength = out.size(); + + if (dataLength < this.charData.length()) { + out.write(this.charData, dataLength, this.charData.length() - dataLength); + } + + this.charData = out.toString(); + } + + public void free() throws SQLException { + this.charData = null; + } + + public Reader getCharacterStream(long pos, long length) throws SQLException { + return new StringReader(getSubString(pos, (int) length)); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CommunicationsException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CommunicationsException.java new file mode 100644 index 0000000..5ab78dd --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CommunicationsException.java @@ -0,0 +1,70 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * An exception to represent communications errors with the database. + * + * Attempts to provide 'friendler' error messages to end-users, including last time a packet was sent to the database, what the client-timeout is set to, and + * whether the idle time has been exceeded. + */ +public class CommunicationsException extends SQLException implements StreamingNotifiable { + static final long serialVersionUID = 3193864990663398317L; + + private String exceptionMessage = null; + + public CommunicationsException(MySQLConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, Exception underlyingException) { + this.exceptionMessage = SQLError.createLinkFailureMessageBasedOnHeuristics(conn, lastPacketSentTimeMs, lastPacketReceivedTimeMs, underlyingException); + + if (underlyingException != null) { + initCause(underlyingException); + } + } + + /* + * @see java.lang.Throwable#getMessage() + */ + @Override + public String getMessage() { + return this.exceptionMessage; + } + + /* + * @see java.sql.SQLException#getSQLState() + */ + @Override + public String getSQLState() { + return SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE; + } + + /* + * @see com.mysql.jdbc.StreamingNotifiable#setWasStreamingResults() + */ + public void setWasStreamingResults() { + // replace exception message + this.exceptionMessage = Messages.getString("CommunicationsException.ClientWasStreaming"); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CompressedInputStream.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CompressedInputStream.java new file mode 100644 index 0000000..377e28f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/CompressedInputStream.java @@ -0,0 +1,304 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.log.NullLogger; + +/** + * Used to de-compress packets from the MySQL server when protocol-level compression is turned on. + */ +class CompressedInputStream extends InputStream { + /** The packet data after it has been un-compressed */ + private byte[] buffer; + + /** The stream we are reading from the server */ + private InputStream in; + + /** The ZIP inflater used to un-compress packets */ + private Inflater inflater; + + /** Connection property reference */ + private ConnectionPropertiesImpl.BooleanConnectionProperty traceProtocol; + + /** Connection logger */ + private Log log; + + /** + * The buffer to read packet headers into + */ + private byte[] packetHeaderBuffer = new byte[7]; + + /** The position we are reading from */ + private int pos = 0; + + /** + * Creates a new CompressedInputStream that reads the given stream from the + * server. + * + * @param conn + * @param streamFromServer + */ + public CompressedInputStream(Connection conn, InputStream streamFromServer) { + this.traceProtocol = ((ConnectionPropertiesImpl) conn).traceProtocol; + try { + this.log = conn.getLog(); + } catch (SQLException e) { + this.log = new NullLogger(null); + } + + this.in = streamFromServer; + this.inflater = new Inflater(); + } + + /** + * @see java.io.InputStream#available() + */ + @Override + public int available() throws IOException { + if (this.buffer == null) { + return this.in.available(); + } + + return this.buffer.length - this.pos + this.in.available(); + } + + /** + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + this.in.close(); + this.buffer = null; + this.inflater.end(); + this.inflater = null; + this.traceProtocol = null; + this.log = null; + } + + /** + * Retrieves and un-compressed (if necessary) the next packet from the + * server. + * + * @throws IOException + * if an I/O error occurs + */ + private void getNextPacketFromServer() throws IOException { + byte[] uncompressedData = null; + + int lengthRead = readFully(this.packetHeaderBuffer, 0, 7); + + if (lengthRead < 7) { + throw new IOException("Unexpected end of input stream"); + } + + int compressedPacketLength = ((this.packetHeaderBuffer[0] & 0xff)) + (((this.packetHeaderBuffer[1] & 0xff)) << 8) + + (((this.packetHeaderBuffer[2] & 0xff)) << 16); + + int uncompressedLength = ((this.packetHeaderBuffer[4] & 0xff)) + (((this.packetHeaderBuffer[5] & 0xff)) << 8) + + (((this.packetHeaderBuffer[6] & 0xff)) << 16); + + boolean doTrace = this.traceProtocol.getValueAsBoolean(); + + if (doTrace) { + this.log.logTrace("Reading compressed packet of length " + compressedPacketLength + " uncompressed to " + uncompressedLength); + } + + if (uncompressedLength > 0) { + uncompressedData = new byte[uncompressedLength]; + + byte[] compressedBuffer = new byte[compressedPacketLength]; + + readFully(compressedBuffer, 0, compressedPacketLength); + + this.inflater.reset(); + + this.inflater.setInput(compressedBuffer); + + try { + this.inflater.inflate(uncompressedData); + } catch (DataFormatException dfe) { + throw new IOException("Error while uncompressing packet from server."); + } + + } else { + if (doTrace) { + this.log.logTrace("Packet didn't meet compression threshold, not uncompressing..."); + } + + // + // Read data, note this this code is reached when using compressed packets that have not been compressed, as well + // + uncompressedLength = compressedPacketLength; + uncompressedData = new byte[uncompressedLength]; + readFully(uncompressedData, 0, uncompressedLength); + } + + if (doTrace) { + if (uncompressedLength > 1024) { + this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(uncompressedData, 256)); + byte[] tempData = new byte[256]; + System.arraycopy(uncompressedData, uncompressedLength - 256, tempData, 0, 256); + this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(tempData, 256)); + this.log.logTrace("Large packet dump truncated. Showing first and last 256 bytes."); + } else { + this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(uncompressedData, uncompressedLength)); + } + } + + if ((this.buffer != null) && (this.pos < this.buffer.length)) { + if (doTrace) { + this.log.logTrace("Combining remaining packet with new: "); + } + + int remaining = this.buffer.length - this.pos; + byte[] newBuffer = new byte[remaining + uncompressedData.length]; + + System.arraycopy(this.buffer, this.pos, newBuffer, 0, remaining); + System.arraycopy(uncompressedData, 0, newBuffer, remaining, uncompressedData.length); + + uncompressedData = newBuffer; + } + + this.pos = 0; + this.buffer = uncompressedData; + + return; + } + + /** + * Determines if another packet needs to be read from the server to be able + * to read numBytes from the stream. + * + * @param numBytes + * the number of bytes to be read + * + * @throws IOException + * if an I/O error occors. + */ + private void getNextPacketIfRequired(int numBytes) throws IOException { + if ((this.buffer == null) || ((this.pos + numBytes) > this.buffer.length)) { + getNextPacketFromServer(); + } + } + + /** + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException { + try { + getNextPacketIfRequired(1); + } catch (IOException ioEx) { + return -1; + } + + return this.buffer[this.pos++] & 0xff; + } + + /** + * @see java.io.InputStream#read(byte) + */ + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * @see java.io.InputStream#read(byte, int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } + + if (len <= 0) { + return 0; + } + + try { + getNextPacketIfRequired(len); + } catch (IOException ioEx) { + return -1; + } + + int remainingBufferLength = this.buffer.length - this.pos; + int consummedBytesLength = Math.min(remainingBufferLength, len); + + System.arraycopy(this.buffer, this.pos, b, off, consummedBytesLength); + this.pos += consummedBytesLength; + + return consummedBytesLength; + } + + private final int readFully(byte[] b, int off, int len) throws IOException { + if (len < 0) { + throw new IndexOutOfBoundsException(); + } + + int n = 0; + + while (n < len) { + int count = this.in.read(b, off + n, len - n); + + if (count < 0) { + throw new EOFException(); + } + + n += count; + } + + return n; + } + + /** + * @see java.io.InputStream#skip(long) + */ + @Override + public long skip(long n) throws IOException { + long count = 0; + + for (long i = 0; i < n; i++) { + int bytesRead = read(); + + if (bytesRead == -1) { + break; + } + + count++; + } + + return count; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Connection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Connection.java new file mode 100644 index 0000000..789cd6d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Connection.java @@ -0,0 +1,428 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.Executor; + +import com.mysql.jdbc.log.Log; + +/** + * This interface contains methods that are considered the "vendor extension" to the JDBC API for MySQL's implementation of java.sql.Connection. + * + * For those looking further into the driver implementation, it is not an API that is used for plugability of implementations inside our driver + * (which is why there are still references to ConnectionImpl throughout the code). + */ +public interface Connection extends java.sql.Connection, ConnectionProperties { + + /** + * Changes the user on this connection by performing a re-authentication. If + * authentication fails, the connection will remain under the context of the + * current user. + * + * @param userName + * the username to authenticate with + * @param newPassword + * the password to authenticate with + * @throws SQLException + * if authentication fails, or some other error occurs while + * performing the command. + */ + public abstract void changeUser(String userName, String newPassword) throws SQLException; + + @Deprecated + public abstract void clearHasTriedMaster(); + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int, int) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int, int, int) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + public abstract java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException; + + /** + * Returns the number of statements active on this connection, which + * haven't been .close()d. + */ + public abstract int getActiveStatementCount(); + + /** + * Reports how long this connection has been idle. + * This time (reported in milliseconds) is updated once a query has + * completed. + * + * @return number of ms that this connection has been idle, 0 if the driver + * is busy retrieving results. + */ + public abstract long getIdleFor(); + + /** + * Returns the log mechanism that should be used to log information from/for + * this Connection. + * + * @return the Log instance to use for logging messages. + * @throws SQLException + * if an error occurs + */ + public abstract Log getLog() throws SQLException; + + /** + * Returns the server's character set + * + * @return the server's character set. + * @deprecated replaced by Connection.getServerCharset() + */ + @Deprecated + public abstract String getServerCharacterEncoding(); + + /** + * Returns the server's character set + * + * @return the server's character set. + */ + public abstract String getServerCharset(); + + /** + * Returns the TimeZone that represents the configured + * timezone for the server. + */ + public abstract TimeZone getServerTimezoneTZ(); + + /** + * Returns the comment that will be prepended to all statements + * sent to the server. + * + * @return the comment that will be prepended to all statements + * sent to the server. + */ + public abstract String getStatementComment(); + + /** + * Has this connection tried to execute a query on the "master" + * server (first host in a multiple host list). + */ + @Deprecated + public abstract boolean hasTriedMaster(); + + /** + * Is this connection currently a participant in an XA transaction? + */ + public abstract boolean isInGlobalTx(); + + /** + * Set the state of being in a global (XA) transaction. + * + * @param flag + */ + public void setInGlobalTx(boolean flag); + + /** + * Is this connection connected to the first host in the list if + * there is a list of servers in the URL? + * + * @return true if this connection is connected to the first in + * the list. + */ + public abstract boolean isMasterConnection(); + + /** + * Is the server in a sql_mode that doesn't allow us to use \\ to escape + * things? + * + * @return Returns the noBackslashEscapes. + */ + public abstract boolean isNoBackslashEscapesSet(); + + /** + * Does this connection have the same resource name as the given + * connection (for XA)? + * + * @param c + */ + public abstract boolean isSameResource(Connection c); + + /** + * Is the server configured to use lower-case table names only? + * + * @return true if lower_case_table_names is 'on' + */ + public abstract boolean lowerCaseTableNames(); + + /** + * Does the server this connection is connected to + * support unicode? + */ + public abstract boolean parserKnowsUnicode(); + + /** + * Detect if the connection is still good by sending a ping command + * to the server. + * + * @throws SQLException + * if the ping fails + */ + public abstract void ping() throws SQLException; + + /** + * Resets the server-side state of this connection. Doesn't work for MySQL + * versions older than 4.0.6 or if isParanoid() is set (it will become a + * no-op in these cases). Usually only used from connection pooling code. + * + * @throws SQLException + * if the operation fails while resetting server state. + */ + public abstract void resetServerState() throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int, int) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int, int, int) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + public abstract java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException; + + /** + * @param failedOver + * The failedOver to set. + */ + public abstract void setFailedOver(boolean flag); + + /** + * @param preferSlaveDuringFailover + * The preferSlaveDuringFailover to set. + */ + @Deprecated + public abstract void setPreferSlaveDuringFailover(boolean flag); + + /** + * Sets the comment that will be prepended to all statements + * sent to the server. Do not use slash-star or star-slash tokens + * in the comment as these will be added by the driver itself. + * + * @param comment + * the comment that will be prepended to all statements + * sent to the server. + */ + public abstract void setStatementComment(String comment); + + /** + * Used by MiniAdmin to shutdown a MySQL server + * + * @throws SQLException + * if the command can not be issued. + */ + public abstract void shutdownServer() throws SQLException; + + /** + * Does the server this connection is connected to + * support isolation levels? + */ + public abstract boolean supportsIsolationLevel(); + + /** + * Does the server this connection is connected to + * support quoted identifiers? + */ + public abstract boolean supportsQuotedIdentifiers(); + + /** + * Does the server this connection is connected to + * support transactions? + */ + public abstract boolean supportsTransactions(); + + /** + * Does the server this connection is connected to + * meet or exceed the given version? + */ + public abstract boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException; + + public abstract void reportQueryTime(long millisOrNanos); + + public abstract boolean isAbonormallyLongQuery(long millisOrNanos); + + public abstract void initializeExtension(Extension ex) throws SQLException; + + /** + * Returns the -session- value of 'auto_increment_increment' from the server if it exists, + * or '1' if not. + */ + public abstract int getAutoIncrementIncrement(); + + /** + * Does this connection have the same properties as another? + */ + public boolean hasSameProperties(Connection c); + + /** + * Returns the parsed and passed in properties for this connection. + */ + public Properties getProperties(); + + public String getHost(); + + public void setProxy(MySQLConnection proxy); + + /** + * Is the server this connection is connected to "local" (i.e. same host) as the application? + */ + public boolean isServerLocal() throws SQLException; + + int getSessionMaxRows(); + + void setSessionMaxRows(int max) throws SQLException; + + // JDBC-4.1 + // until we flip catalog/schema, this is a no-op + void setSchema(String schema) throws SQLException; + + String getSchema() throws SQLException; + + // JDBC-4.1 + void abort(Executor executor) throws SQLException; + + // JDBC-4.1 + void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException; + + int getNetworkTimeout() throws SQLException; + + // ************************** + // moved from MySQLConnection + // ************************** + + void abortInternal() throws SQLException; + + void checkClosed() throws SQLException; + + Object getConnectionMutex(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java new file mode 100644 index 0000000..4778737 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java @@ -0,0 +1,62 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Thrown when a client requests a connection-level feature that isn't available for this particular distribution of Connector/J (currently only used by code + * that is export-controlled). + */ +public class ConnectionFeatureNotAvailableException extends CommunicationsException { + + static final long serialVersionUID = -5065030488729238287L; + + /** + * @param conn + * @param lastPacketSentTimeMs + * @param underlyingException + */ + public ConnectionFeatureNotAvailableException(MySQLConnection conn, long lastPacketSentTimeMs, Exception underlyingException) { + super(conn, lastPacketSentTimeMs, 0, underlyingException); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Throwable#getMessage() + */ + @Override + public String getMessage() { + return "Feature not available in this distribution of Connector/J"; + } + + /* + * (non-Javadoc) + * + * @see java.sql.SQLException#getSQLState() + */ + @Override + public String getSQLState() { + return SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroup.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroup.java new file mode 100644 index 0000000..61ebe39 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroup.java @@ -0,0 +1,240 @@ +/* + Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ConnectionGroup { + private String groupName; + private long connections = 0; + private long activeConnections = 0; + private HashMap connectionProxies = new HashMap(); + private Set hostList = new HashSet(); + private boolean isInitialized = false; + private long closedProxyTotalPhysicalConnections = 0; + private long closedProxyTotalTransactions = 0; + private int activeHosts = 0; + private Set closedHosts = new HashSet(); + + ConnectionGroup(String groupName) { + this.groupName = groupName; + } + + public long registerConnectionProxy(LoadBalancedConnectionProxy proxy, List localHostList) { + long currentConnectionId; + + synchronized (this) { + if (!this.isInitialized) { + this.hostList.addAll(localHostList); + this.isInitialized = true; + this.activeHosts = localHostList.size(); + } + currentConnectionId = ++this.connections; + this.connectionProxies.put(Long.valueOf(currentConnectionId), proxy); + } + this.activeConnections++; + + return currentConnectionId; + } + + public String getGroupName() { + return this.groupName; + } + + public Collection getInitialHosts() { + return this.hostList; + } + + public int getActiveHostCount() { + return this.activeHosts; + } + + public Collection getClosedHosts() { + return this.closedHosts; + } + + public long getTotalLogicalConnectionCount() { + return this.connections; + } + + public long getActiveLogicalConnectionCount() { + return this.activeConnections; + } + + public long getActivePhysicalConnectionCount() { + long result = 0; + Map proxyMap = new HashMap(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + result += proxy.getActivePhysicalConnectionCount(); + } + return result; + } + + public long getTotalPhysicalConnectionCount() { + long allConnections = this.closedProxyTotalPhysicalConnections; + Map proxyMap = new HashMap(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + allConnections += proxy.getTotalPhysicalConnectionCount(); + } + return allConnections; + } + + public long getTotalTransactionCount() { + // need to account for closed connection proxies + long transactions = this.closedProxyTotalTransactions; + Map proxyMap = new HashMap(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + transactions += proxy.getTransactionCount(); + } + return transactions; + } + + public void closeConnectionProxy(LoadBalancedConnectionProxy proxy) { + this.activeConnections--; + this.connectionProxies.remove(Long.valueOf(proxy.getConnectionGroupProxyID())); + this.closedProxyTotalPhysicalConnections += proxy.getTotalPhysicalConnectionCount(); + this.closedProxyTotalTransactions += proxy.getTransactionCount(); + } + + /** + * Remove the given host (host:port pair) from this Connection Group. + * + * @param hostPortPair + * The host:port pair to remove. + * @throws SQLException + */ + public void removeHost(String hostPortPair) throws SQLException { + removeHost(hostPortPair, false); + } + + /** + * Remove the given host (host:port pair) from this Connection Group. + * + * @param hostPortPair + * The host:port pair to remove. + * @param removeExisting + * Whether affects existing load-balanced connections or only new ones. + * @throws SQLException + */ + public void removeHost(String hostPortPair, boolean removeExisting) throws SQLException { + this.removeHost(hostPortPair, removeExisting, true); + } + + /** + * Remove the given host (host:port pair) from this Connection Group and, consequently, from all the load-balanced connections it holds. + * + * @param hostPortPair + * The host:port pair to remove. + * @param removeExisting + * Whether affects existing load-balanced connections or only new ones. + * @param waitForGracefulFailover + * If true instructs the load-balanced connections to fail-over the underlying active connection before removing this host, otherwise remove + * immediately. + * @throws SQLException + */ + public synchronized void removeHost(String hostPortPair, boolean removeExisting, boolean waitForGracefulFailover) throws SQLException { + if (this.activeHosts == 1) { + throw SQLError.createSQLException("Cannot remove host, only one configured host active.", null); + } + + if (this.hostList.remove(hostPortPair)) { + this.activeHosts--; + } else { + throw SQLError.createSQLException("Host is not configured: " + hostPortPair, null); + } + + if (removeExisting) { + // make a local copy to keep synchronization overhead to minimum + Map proxyMap = new HashMap(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + if (waitForGracefulFailover) { + proxy.removeHostWhenNotInUse(hostPortPair); + } else { + proxy.removeHost(hostPortPair); + } + } + } + this.closedHosts.add(hostPortPair); + } + + /** + * Add the given host (host:port pair) to this Connection Group. + * + * @param hostPortPair + * The host:port pair to add. + * @throws SQLException + */ + public void addHost(String hostPortPair) { + addHost(hostPortPair, false); + } + + /** + * Add the given host (host:port pair) to this Connection Group and, consequently, to all the load-balanced connections it holds. + * + * @param hostPortPair + * The host:port pair to add. + * @param forExisting + * Whether affects existing load-balanced connections or only new ones. + */ + public void addHost(String hostPortPair, boolean forExisting) { + synchronized (this) { + if (this.hostList.add(hostPortPair)) { + this.activeHosts++; + } + } + // all new connections will have this host + if (!forExisting) { + return; + } + + // make a local copy to keep synchronization overhead to minimum + Map proxyMap = new HashMap(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + proxy.addHost(hostPortPair); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroupManager.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroupManager.java new file mode 100644 index 0000000..2f01533 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionGroupManager.java @@ -0,0 +1,206 @@ +/* + Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.mysql.jdbc.jmx.LoadBalanceConnectionGroupManager; + +public class ConnectionGroupManager { + + private static HashMap GROUP_MAP = new HashMap(); + + private static LoadBalanceConnectionGroupManager mbean = new LoadBalanceConnectionGroupManager(); + + private static boolean hasRegisteredJmx = false; + + public static synchronized ConnectionGroup getConnectionGroupInstance(String groupName) { + if (GROUP_MAP.containsKey(groupName)) { + return GROUP_MAP.get(groupName); + } + ConnectionGroup group = new ConnectionGroup(groupName); + GROUP_MAP.put(groupName, group); + return group; + } + + public static void registerJmx() throws SQLException { + if (hasRegisteredJmx) { + return; + } + + mbean.registerJmx(); + hasRegisteredJmx = true; + } + + public static ConnectionGroup getConnectionGroup(String groupName) { + return GROUP_MAP.get(groupName); + } + + private static Collection getGroupsMatching(String group) { + if (group == null || group.equals("")) { + Set s = new HashSet(); + + s.addAll(GROUP_MAP.values()); + return s; + } + Set s = new HashSet(); + ConnectionGroup o = GROUP_MAP.get(group); + if (o != null) { + s.add(o); + } + return s; + + } + + public static void addHost(String group, String hostPortPair, boolean forExisting) { + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + cg.addHost(hostPortPair, forExisting); + } + } + + public static int getActiveHostCount(String group) { + Set active = new HashSet(); + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + active.addAll(cg.getInitialHosts()); + } + return active.size(); + } + + public static long getActiveLogicalConnectionCount(String group) { + int count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getActiveLogicalConnectionCount(); + } + return count; + } + + public static long getActivePhysicalConnectionCount(String group) { + int count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getActivePhysicalConnectionCount(); + } + return count; + } + + public static int getTotalHostCount(String group) { + Collection s = getGroupsMatching(group); + Set hosts = new HashSet(); + for (ConnectionGroup cg : s) { + hosts.addAll(cg.getInitialHosts()); + hosts.addAll(cg.getClosedHosts()); + } + return hosts.size(); + } + + public static long getTotalLogicalConnectionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalLogicalConnectionCount(); + } + return count; + } + + public static long getTotalPhysicalConnectionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalPhysicalConnectionCount(); + } + return count; + } + + public static long getTotalTransactionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalTransactionCount(); + } + return count; + } + + public static void removeHost(String group, String hostPortPair) throws SQLException { + removeHost(group, hostPortPair, false); + } + + public static void removeHost(String group, String host, boolean removeExisting) throws SQLException { + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + cg.removeHost(host, removeExisting); + } + } + + public static String getActiveHostLists(String group) { + Collection s = getGroupsMatching(group); + Map hosts = new HashMap(); + for (ConnectionGroup cg : s) { + + Collection l = cg.getInitialHosts(); + for (String host : l) { + Integer o = hosts.get(host); + if (o == null) { + o = Integer.valueOf(1); + } else { + o = Integer.valueOf(o.intValue() + 1); + } + hosts.put(host, o); + + } + } + + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (String host : hosts.keySet()) { + sb.append(sep); + sb.append(host); + sb.append('('); + sb.append(hosts.get(host)); + sb.append(')'); + sep = ","; + } + return sb.toString(); + } + + public static String getRegisteredConnectionGroups() { + Collection s = getGroupsMatching(null); + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (ConnectionGroup cg : s) { + String group = cg.getGroupName(); + sb.append(sep); + sb.append(group); + sep = ","; + } + return sb.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionImpl.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionImpl.java new file mode 100644 index 0000000..9debec4 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionImpl.java @@ -0,0 +1,5553 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.sql.Blob; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.SQLPermission; +import java.sql.SQLWarning; +import java.sql.Savepoint; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import java.util.Stack; +import java.util.TimeZone; +import java.util.Timer; +import java.util.TreeMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; + +import com.mysql.jdbc.PreparedStatement.ParseInfo; +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.log.LogFactory; +import com.mysql.jdbc.log.LogUtils; +import com.mysql.jdbc.log.NullLogger; +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.profiler.ProfilerEventHandler; +import com.mysql.jdbc.util.LRUCache; + +/** + * A Connection represents a session with a specific database. Within the context of a Connection, SQL statements are executed and results are returned. + * + *

    + * A Connection's database is able to provide information describing its tables, its supported SQL grammar, its stored procedures, the capabilities of this + * connection, etc. This information is obtained with the getMetaData method. + *

    + */ +public class ConnectionImpl extends ConnectionPropertiesImpl implements MySQLConnection { + + private static final long serialVersionUID = 2877471301981509474L; + + private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout"); + + private static final SQLPermission ABORT_PERM = new SQLPermission("abort"); + + public static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results"; + + public String getHost() { + return this.host; + } + + public String getHostPortPair() { + return this.hostPortPair != null ? this.hostPortPair : this.host + ":" + this.port; + } + + private MySQLConnection proxy = null; + private InvocationHandler realProxy = null; + + public boolean isProxySet() { + return this.proxy != null; + } + + public void setProxy(MySQLConnection proxy) { + this.proxy = proxy; + this.realProxy = this.proxy instanceof MultiHostMySQLConnection ? ((MultiHostMySQLConnection) proxy).getThisAsProxy() : null; + } + + // this connection has to be proxied when using multi-host settings so that statements get routed to the right physical connection + // (works as "logical" connection) + private MySQLConnection getProxy() { + return (this.proxy != null) ? this.proxy : (MySQLConnection) this; + } + + /** + * @deprecated replaced by getMultiHostSafeProxy() + */ + @Deprecated + public MySQLConnection getLoadBalanceSafeProxy() { + return getMultiHostSafeProxy(); + } + + public MySQLConnection getMultiHostSafeProxy() { + return this.getProxy(); + } + + public Object getConnectionMutex() { + return (this.realProxy != null) ? this.realProxy : getProxy(); + } + + public class ExceptionInterceptorChain implements ExceptionInterceptor { + private List interceptors; + + ExceptionInterceptorChain(String interceptorClasses) throws SQLException { + this.interceptors = Util.loadExtensions(ConnectionImpl.this, ConnectionImpl.this.props, interceptorClasses, "Connection.BadExceptionInterceptor", + this); + } + + void addRingZero(ExceptionInterceptor interceptor) throws SQLException { + this.interceptors.add(0, interceptor); + } + + public SQLException interceptException(SQLException sqlEx, Connection conn) { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + sqlEx = ((ExceptionInterceptor) iter.next()).interceptException(sqlEx, ConnectionImpl.this); + } + } + + return sqlEx; + } + + public void destroy() { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + ((ExceptionInterceptor) iter.next()).destroy(); + } + } + + } + + public void init(Connection conn, Properties properties) throws SQLException { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + ((ExceptionInterceptor) iter.next()).init(conn, properties); + } + } + } + + public List getInterceptors() { + return this.interceptors; + } + + } + + /** + * Used as a key for caching callable statements which (may) depend on + * current catalog...In 5.0.x, they don't (currently), but stored procedure + * names soon will, so current catalog is a (hidden) component of the name. + */ + static class CompoundCacheKey { + String componentOne; + + String componentTwo; + + int hashCode; + + CompoundCacheKey(String partOne, String partTwo) { + this.componentOne = partOne; + this.componentTwo = partTwo; + + // Handle first component (in most cases, currentCatalog being NULL.... + this.hashCode = (((this.componentOne != null) ? this.componentOne : "") + this.componentTwo).hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof CompoundCacheKey) { + CompoundCacheKey another = (CompoundCacheKey) obj; + + boolean firstPartEqual = false; + + if (this.componentOne == null) { + firstPartEqual = (another.componentOne == null); + } else { + firstPartEqual = this.componentOne.equals(another.componentOne); + } + + return (firstPartEqual && this.componentTwo.equals(another.componentTwo)); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return this.hashCode; + } + } + + /** + * Marker for character set converter not being available (not written, + * multibyte, etc) Used to prevent multiple instantiation requests. + */ + private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object(); + + /** + * The mapping between MySQL charset names and Java charset names. + * Initialized by loadCharacterSetMapping() + */ + public static Map charsetMap; + + /** Default logger class name */ + protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger"; + + private final static int HISTOGRAM_BUCKETS = 20; + + /** Logger instance name */ + private static final String LOGGER_INSTANCE_NAME = "MySQL"; + + /** + * Map mysql transaction isolation level name to + * java.sql.Connection.TRANSACTION_XXX + */ + private static Map mapTransIsolationNameToValue = null; + + /** Null logger shared by all connections at startup */ + private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME); + + protected static Map roundRobinStatsMap; + + /** + * Actual collation index to collation name map for given server URLs. + */ + private static final Map> dynamicIndexToCollationMapByUrl = new HashMap>(); + + /** + * Actual collation index to mysql charset name map for given server URLs. + */ + private static final Map> dynamicIndexToCharsetMapByUrl = new HashMap>(); + + /** + * Actual collation index to mysql charset name map of user defined charsets for given server URLs. + */ + private static final Map> customIndexToCharsetMapByUrl = new HashMap>(); + + /** + * Actual mysql charset name to mblen map of user defined charsets for given server URLs. + */ + private static final Map> customCharsetToMblenMapByUrl = new HashMap>(); + + private CacheAdapter> serverConfigCache; + + private long queryTimeCount; + private double queryTimeSum; + private double queryTimeSumSquares; + private double queryTimeMean; + + private transient Timer cancelTimer; + + private List connectionLifecycleInterceptors; + + private static final Constructor JDBC_4_CONNECTION_CTOR; + + private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY; + + private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY; + + static { + mapTransIsolationNameToValue = new HashMap(8); + mapTransIsolationNameToValue.put("READ-UNCOMMITED", TRANSACTION_READ_UNCOMMITTED); + mapTransIsolationNameToValue.put("READ-UNCOMMITTED", TRANSACTION_READ_UNCOMMITTED); + mapTransIsolationNameToValue.put("READ-COMMITTED", TRANSACTION_READ_COMMITTED); + mapTransIsolationNameToValue.put("REPEATABLE-READ", TRANSACTION_REPEATABLE_READ); + mapTransIsolationNameToValue.put("SERIALIZABLE", TRANSACTION_SERIALIZABLE); + + if (Util.isJdbc4()) { + try { + JDBC_4_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4Connection") + .getConstructor(new Class[] { String.class, Integer.TYPE, Properties.class, String.class, String.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_CONNECTION_CTOR = null; + } + } + + protected static SQLException appendMessageToException(SQLException sqlEx, String messageToAppend, ExceptionInterceptor interceptor) { + String origMessage = sqlEx.getMessage(); + String sqlState = sqlEx.getSQLState(); + int vendorErrorCode = sqlEx.getErrorCode(); + + StringBuilder messageBuf = new StringBuilder(origMessage.length() + messageToAppend.length()); + messageBuf.append(origMessage); + messageBuf.append(messageToAppend); + + SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf.toString(), sqlState, vendorErrorCode, interceptor); + + // + // Try and maintain the original stack trace, only works on JDK-1.4 and newer + // + + try { + // Have to do this with reflection, otherwise older JVMs croak + Method getStackTraceMethod = null; + Method setStackTraceMethod = null; + Object theStackTraceAsObject = null; + + Class stackTraceElementClass = Class.forName("java.lang.StackTraceElement"); + Class stackTraceElementArrayClass = Array.newInstance(stackTraceElementClass, new int[] { 0 }).getClass(); + + getStackTraceMethod = Throwable.class.getMethod("getStackTrace", new Class[] {}); + + setStackTraceMethod = Throwable.class.getMethod("setStackTrace", new Class[] { stackTraceElementArrayClass }); + + if (getStackTraceMethod != null && setStackTraceMethod != null) { + theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx, new Object[0]); + setStackTraceMethod.invoke(sqlExceptionWithNewMessage, new Object[] { theStackTraceAsObject }); + } + } catch (NoClassDefFoundError noClassDefFound) { + + } catch (NoSuchMethodException noSuchMethodEx) { + + } catch (Throwable catchAll) { + + } + + return sqlExceptionWithNewMessage; + } + + public Timer getCancelTimer() { + synchronized (getConnectionMutex()) { + if (this.cancelTimer == null) { + boolean createdNamedTimer = false; + + // Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named timer on older VMs. + try { + Constructor ctr = Timer.class.getConstructor(new Class[] { String.class, Boolean.TYPE }); + + this.cancelTimer = ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE }); + createdNamedTimer = true; + } catch (Throwable t) { + createdNamedTimer = false; + } + + if (!createdNamedTimer) { + this.cancelTimer = new Timer(true); + } + } + + return this.cancelTimer; + } + } + + /** + * Creates a connection instance -- We need to provide factory-style methods + * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise + * the class verifier complains when it tries to load JDBC4-only interface + * classes that are present in JDBC4 method signatures. + */ + + protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) + throws SQLException { + if (!Util.isJdbc4()) { + return new ConnectionImpl(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url); + } + + return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR, + new Object[] { hostToConnectTo, Integer.valueOf(portToConnectTo), info, databaseToConnectTo, url }, null); + } + + private static final Random random = new Random(); + + /** + * @param url + * @param hostList + */ + protected static synchronized int getNextRoundRobinHostIndex(String url, List hostList) { + // we really do "random" here, because you don't get even distribution when this is coupled with connection pools + + int indexRange = hostList.size(); + + int index = random.nextInt(indexRange); + + return index; + } + + private static boolean nullSafeCompare(String s1, String s2) { + if (s1 == null && s2 == null) { + return true; + } + + if (s1 == null && s2 != null) { + return false; + } + + return s1 != null && s1.equals(s2); + } + + /** Are we in autoCommit mode? */ + private boolean autoCommit = true; + + /** A cache of SQL to parsed prepared statement parameters. */ + private CacheAdapter cachedPreparedStatementParams; + + /** + * For servers > 4.1.0, what character set is the metadata returned in? + */ + private String characterSetMetadata = null; + + /** + * The character set we want results and result metadata returned in (null == + * results in any charset, metadata in UTF-8). + */ + private String characterSetResultsOnServer = null; + + /** + * Holds cached mappings to charset converters to avoid static + * synchronization and at the same time save memory (each charset converter + * takes approx 65K of static data). + */ + private Map charsetConverterMap = new HashMap(CharsetMapping.getNumberOfCharsetsConfigured()); + + /** The point in time when this connection was created */ + private long connectionCreationTimeMillis = 0; + + /** ID used when profiling */ + private long connectionId; + + /** The database we're currently using (called Catalog in JDBC terms). */ + private String database = null; + + /** Internal DBMD to use for various database-version specific features */ + private DatabaseMetaData dbmd = null; + + private TimeZone defaultTimeZone; + + /** The event sink to use for profiling */ + private ProfilerEventHandler eventSink; + + /** Why was this connection implicitly closed, if known? (for diagnostics) */ + private Throwable forceClosedReason; + + /** Does the server support isolation levels? */ + private boolean hasIsolationLevels = false; + + /** Does this version of MySQL support quoted identifiers? */ + private boolean hasQuotedIdentifiers = false; + + /** The hostname we're connected to */ + private String host = null; + + /** + * We need this 'bootstrapped', because 4.1 and newer will send fields back + * with this even before we fill this dynamically from the server. + */ + public Map indexToMysqlCharset = new HashMap(); + + public Map indexToCustomMysqlCharset = null; //new HashMap(); + + private Map mysqlCharsetToCustomMblen = null; //new HashMap(); + + /** The I/O abstraction interface (network conn to MySQL server */ + private transient MysqlIO io = null; + + private boolean isClientTzUTC = false; + + /** Has this connection been closed? */ + private boolean isClosed = true; + + /** Is this connection associated with a global tx? */ + private boolean isInGlobalTx = false; + + /** Is this connection running inside a JDK-1.3 VM? */ + private boolean isRunningOnJDK13 = false; + + /** isolation level */ + private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED; + + private boolean isServerTzUTC = false; + + /** When did the last query finish? */ + private long lastQueryFinishedTime = 0; + + /** The logger we're going to use */ + private transient Log log = NULL_LOGGER; + + /** + * If gathering metrics, what was the execution time of the longest query so + * far ? + */ + private long longestQueryTimeMs = 0; + + /** Is the server configured to use lower-case table names only? */ + private boolean lowerCaseTableNames = false; + + /** When did the master fail? */ + // private long masterFailTimeMillis = 0L; + + private long maximumNumberTablesAccessed = 0; + + /** The max-rows setting for current session */ + private int sessionMaxRows = -1; + + /** When was the last time we reported metrics? */ + private long metricsLastReportedMs; + + private long minimumNumberTablesAccessed = Long.MAX_VALUE; + + /** The JDBC URL we're using */ + private String myURL = null; + + /** Does this connection need to be tested? */ + private boolean needsPing = false; + + private int netBufferLength = 16384; + + private boolean noBackslashEscapes = false; + + private long numberOfPreparedExecutes = 0; + + private long numberOfPrepares = 0; + + private long numberOfQueriesIssued = 0; + + private long numberOfResultSetsCreated = 0; + + private long[] numTablesMetricsHistBreakpoints; + + private int[] numTablesMetricsHistCounts; + + private long[] oldHistBreakpoints = null; + + private int[] oldHistCounts = null; + + /** + * An array of currently open statements. + * Copy-on-write used here to avoid ConcurrentModificationException when statements unregister themselves while we iterate over the list. + */ + private final CopyOnWriteArrayList openStatements = new CopyOnWriteArrayList(); + + private LRUCache parsedCallableStatementCache; + + private boolean parserKnowsUnicode = false; + + /** The password we used */ + private String password = null; + + private long[] perfMetricsHistBreakpoints; + + private int[] perfMetricsHistCounts; + + /** Point of origin where this Connection was created */ + private String pointOfOrigin; + + /** The port number we're connected to (defaults to 3306) */ + private int port = 3306; + + /** Properties for this connection specified by user */ + protected Properties props = null; + + /** Should we retrieve 'info' messages from the server? */ + private boolean readInfoMsg = false; + + /** Are we in read-only mode? */ + private boolean readOnly = false; + + /** Cache of ResultSet metadata */ + protected LRUCache resultSetMetadataCache; + + /** The timezone of the server */ + private TimeZone serverTimezoneTZ = null; + + /** The map of server variables that we retrieve at connection init. */ + private Map serverVariables = null; + + private long shortestQueryTimeMs = Long.MAX_VALUE; + + private double totalQueryTimeMs = 0; + + /** Are transactions supported by the MySQL server we are connected to? */ + private boolean transactionsSupported = false; + + /** + * The type map for UDTs (not implemented, but used by some third-party + * vendors, most notably IBM WebSphere) + */ + private Map> typeMap; + + /** Has ANSI_QUOTES been enabled on the server? */ + private boolean useAnsiQuotes = false; + + /** The user we're connected as */ + private String user = null; + + /** + * Should we use server-side prepared statements? (auto-detected, but can be + * disabled by user) + */ + private boolean useServerPreparedStmts = false; + + private LRUCache serverSideStatementCheckCache; + private LRUCache serverSideStatementCache; + private Calendar sessionCalendar; + + private Calendar utcCalendar; + + private String origHostToConnectTo; + + // we don't want to be able to publicly clone this... + + private int origPortToConnectTo; + + private String origDatabaseToConnectTo; + + private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server + + private boolean usePlatformCharsetConverters; + + /* + * For testing failover scenarios + */ + private boolean hasTriedMasterFlag = false; + + /** + * The comment (if any) that we'll prepend to all statements + * sent to the server (to show up in "SHOW PROCESSLIST") + */ + private String statementComment = null; + + private boolean storesLowerCaseTableName; + + private List statementInterceptors; + + /** + * If a CharsetEncoder is required for escaping. Needed for SJIS and related + * problems with \u00A5. + */ + private boolean requiresEscapingEncoder; + + private String hostPortPair; + + /** + * ' + * For the delegate only + */ + protected ConnectionImpl() { + } + + /** + * Creates a connection to a MySQL Server. + * + * @param hostToConnectTo + * the hostname of the database server + * @param portToConnectTo + * the port number the server is listening on + * @param info + * a Properties[] list holding the user and password + * @param databaseToConnectTo + * the database to connect to + * @param url + * the URL of the connection + * @param d + * the Driver instantation of the connection + * @exception SQLException + * if a database access error occurs + */ + public ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException { + + this.connectionCreationTimeMillis = System.currentTimeMillis(); + + if (databaseToConnectTo == null) { + databaseToConnectTo = ""; + } + + // Stash away for later, used to clone this connection for Statement.cancel and Statement.setQueryTimeout(). + // + + this.origHostToConnectTo = hostToConnectTo; + this.origPortToConnectTo = portToConnectTo; + this.origDatabaseToConnectTo = databaseToConnectTo; + + try { + Blob.class.getMethod("truncate", new Class[] { Long.TYPE }); + + this.isRunningOnJDK13 = false; + } catch (NoSuchMethodException nsme) { + this.isRunningOnJDK13 = true; + } + + this.sessionCalendar = new GregorianCalendar(); + this.utcCalendar = new GregorianCalendar(); + this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT")); + + // + // Normally, this code would be in initializeDriverProperties, but we need to do this as early as possible, so we can start logging to the 'correct' + // place as early as possible...this.log points to 'NullLogger' for every connection at startup to avoid NPEs and the overhead of checking for NULL at + // every logging call. + // + // We will reset this to the configured logger during properties initialization. + // + this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor()); + + if (NonRegisteringDriver.isHostPropertiesList(hostToConnectTo)) { + Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostToConnectTo); + + Enumeration propertyNames = hostSpecificProps.propertyNames(); + + while (propertyNames.hasMoreElements()) { + String propertyName = propertyNames.nextElement().toString(); + String propertyValue = hostSpecificProps.getProperty(propertyName); + + info.setProperty(propertyName, propertyValue); + } + } else { + + if (hostToConnectTo == null) { + this.host = "localhost"; + this.hostPortPair = this.host + ":" + portToConnectTo; + } else { + this.host = hostToConnectTo; + + if (hostToConnectTo.indexOf(":") == -1) { + this.hostPortPair = this.host + ":" + portToConnectTo; + } else { + this.hostPortPair = this.host; + } + } + } + + this.port = portToConnectTo; + + this.database = databaseToConnectTo; + this.myURL = url; + this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + this.password = info.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + + if ((this.user == null) || this.user.equals("")) { + this.user = ""; + } + + if (this.password == null) { + this.password = ""; + } + + this.props = info; + + initializeDriverProperties(info); + + // We store this per-connection, due to static synchronization issues in Java's built-in TimeZone class... + this.defaultTimeZone = TimeUtil.getDefaultTimeZone(getCacheDefaultTimezone()); + + this.isClientTzUTC = !this.defaultTimeZone.useDaylightTime() && this.defaultTimeZone.getRawOffset() == 0; + + if (getUseUsageAdvisor()) { + this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable()); + } else { + this.pointOfOrigin = ""; + } + + try { + this.dbmd = getMetaData(false, false); + initializeSafeStatementInterceptors(); + createNewIO(false); + unSafeStatementInterceptors(); + } catch (SQLException ex) { + cleanup(ex); + + // don't clobber SQL exceptions + throw ex; + } catch (Exception ex) { + cleanup(ex); + + StringBuilder mesg = new StringBuilder(128); + + if (!getParanoid()) { + mesg.append("Cannot connect to MySQL server on "); + mesg.append(this.host); + mesg.append(":"); + mesg.append(this.port); + mesg.append(".\n\n"); + mesg.append("Make sure that there is a MySQL server "); + mesg.append("running on the machine/port you are trying "); + mesg.append("to connect to and that the machine this software is running on "); + mesg.append("is able to connect to this host/port (i.e. not firewalled). "); + mesg.append("Also make sure that the server has not been started with the --skip-networking "); + mesg.append("flag.\n\n"); + } else { + mesg.append("Unable to connect to database."); + } + + SQLException sqlEx = SQLError.createSQLException(mesg.toString(), SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, getExceptionInterceptor()); + + sqlEx.initCause(ex); + + throw sqlEx; + } + + NonRegisteringDriver.trackConnection(this); + } + + public void unSafeStatementInterceptors() throws SQLException { + + ArrayList unSafedStatementInterceptors = new ArrayList(this.statementInterceptors.size()); + + for (int i = 0; i < this.statementInterceptors.size(); i++) { + NoSubInterceptorWrapper wrappedInterceptor = (NoSubInterceptorWrapper) this.statementInterceptors.get(i); + + unSafedStatementInterceptors.add(wrappedInterceptor.getUnderlyingInterceptor()); + } + + this.statementInterceptors = unSafedStatementInterceptors; + + if (this.io != null) { + this.io.setStatementInterceptors(this.statementInterceptors); + } + } + + public void initializeSafeStatementInterceptors() throws SQLException { + this.isClosed = false; + + List unwrappedInterceptors = Util.loadExtensions(this, this.props, getStatementInterceptors(), "MysqlIo.BadStatementInterceptor", + getExceptionInterceptor()); + + this.statementInterceptors = new ArrayList(unwrappedInterceptors.size()); + + for (int i = 0; i < unwrappedInterceptors.size(); i++) { + Extension interceptor = unwrappedInterceptors.get(i); + + // adapt older versions of statement interceptors, handle the case where something wants v2 functionality but wants to run with an older driver + if (interceptor instanceof StatementInterceptor) { + if (ReflectiveStatementInterceptorAdapter.getV2PostProcessMethod(interceptor.getClass()) != null) { + this.statementInterceptors.add(new NoSubInterceptorWrapper(new ReflectiveStatementInterceptorAdapter((StatementInterceptor) interceptor))); + } else { + this.statementInterceptors.add(new NoSubInterceptorWrapper(new V1toV2StatementInterceptorAdapter((StatementInterceptor) interceptor))); + } + } else { + this.statementInterceptors.add(new NoSubInterceptorWrapper((StatementInterceptorV2) interceptor)); + } + } + + } + + public List getStatementInterceptorsInstances() { + return this.statementInterceptors; + } + + private void addToHistogram(int[] histogramCounts, long[] histogramBreakpoints, long value, int numberOfTimes, long currentLowerBound, + long currentUpperBound) { + if (histogramCounts == null) { + createInitialHistogram(histogramBreakpoints, currentLowerBound, currentUpperBound); + } else { + for (int i = 0; i < HISTOGRAM_BUCKETS; i++) { + if (histogramBreakpoints[i] >= value) { + histogramCounts[i] += numberOfTimes; + + break; + } + } + } + } + + private void addToPerformanceHistogram(long value, int numberOfTimes) { + checkAndCreatePerformanceHistogram(); + + addToHistogram(this.perfMetricsHistCounts, this.perfMetricsHistBreakpoints, value, numberOfTimes, + this.shortestQueryTimeMs == Long.MAX_VALUE ? 0 : this.shortestQueryTimeMs, this.longestQueryTimeMs); + } + + private void addToTablesAccessedHistogram(long value, int numberOfTimes) { + checkAndCreateTablesAccessedHistogram(); + + addToHistogram(this.numTablesMetricsHistCounts, this.numTablesMetricsHistBreakpoints, value, numberOfTimes, + this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0 : this.minimumNumberTablesAccessed, this.maximumNumberTablesAccessed); + } + + /** + * Builds the map needed for 4.1.0 and newer servers that maps field-level + * charset/collation info to a java character encoding name. + * + * @throws SQLException + */ + private void buildCollationMapping() throws SQLException { + + Map indexToCharset = null; + Map sortedCollationMap = null; + Map customCharset = null; + Map customMblen = null; + + if (getCacheServerConfiguration()) { + synchronized (dynamicIndexToCharsetMapByUrl) { + indexToCharset = dynamicIndexToCharsetMapByUrl.get(getURL()); + sortedCollationMap = dynamicIndexToCollationMapByUrl.get(getURL()); + customCharset = customIndexToCharsetMapByUrl.get(getURL()); + customMblen = customCharsetToMblenMapByUrl.get(getURL()); + } + } + + if (indexToCharset == null) { + indexToCharset = new HashMap(); + + if (versionMeetsMinimum(4, 1, 0) && getDetectCustomCollations()) { + + java.sql.Statement stmt = null; + java.sql.ResultSet results = null; + + try { + sortedCollationMap = new TreeMap(); + customCharset = new HashMap(); + customMblen = new HashMap(); + + stmt = getMetadataSafeStatement(); + + try { + results = stmt.executeQuery("SHOW COLLATION"); + if (versionMeetsMinimum(5, 0, 0)) { + Util.resultSetToMap(sortedCollationMap, results, 3, 2); + } else { + while (results.next()) { + sortedCollationMap.put(results.getLong(3), results.getString(2)); + } + } + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + + for (Iterator> indexIter = sortedCollationMap.entrySet().iterator(); indexIter.hasNext();) { + Map.Entry indexEntry = indexIter.next(); + + int collationIndex = indexEntry.getKey().intValue(); + String charsetName = indexEntry.getValue(); + + indexToCharset.put(collationIndex, charsetName); + + // if no static map for charsetIndex or server has a different mapping then our static map, adding it to custom map + if (collationIndex >= CharsetMapping.MAP_SIZE + || !charsetName.equals(CharsetMapping.getMysqlCharsetNameForCollationIndex(collationIndex))) { + customCharset.put(collationIndex, charsetName); + } + + // if no static map for charsetName adding to custom map + if (!CharsetMapping.CHARSET_NAME_TO_CHARSET.containsKey(charsetName)) { + customMblen.put(charsetName, null); + } + } + + // if there is a number of custom charsets we should execute SHOW CHARACTER SET to know theirs mblen + if (customMblen.size() > 0) { + try { + results = stmt.executeQuery("SHOW CHARACTER SET"); + while (results.next()) { + String charsetName = results.getString("Charset"); + if (customMblen.containsKey(charsetName)) { + customMblen.put(charsetName, results.getInt("Maxlen")); + } + } + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + } + + if (getCacheServerConfiguration()) { + synchronized (dynamicIndexToCharsetMapByUrl) { + dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset); + dynamicIndexToCollationMapByUrl.put(getURL(), sortedCollationMap); + customIndexToCharsetMapByUrl.put(getURL(), customCharset); + customCharsetToMblenMapByUrl.put(getURL(), customMblen); + } + } + + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } finally { + if (results != null) { + try { + results.close(); + } catch (java.sql.SQLException sqlE) { + // ignore + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (java.sql.SQLException sqlE) { + // ignore + } + } + } + } else { + for (int i = 1; i < CharsetMapping.MAP_SIZE; i++) { + indexToCharset.put(i, CharsetMapping.getMysqlCharsetNameForCollationIndex(i)); + } + if (getCacheServerConfiguration()) { + synchronized (dynamicIndexToCharsetMapByUrl) { + dynamicIndexToCharsetMapByUrl.put(getURL(), indexToCharset); + } + } + } + + } + + this.indexToMysqlCharset = Collections.unmodifiableMap(indexToCharset); + if (customCharset != null) { + this.indexToCustomMysqlCharset = Collections.unmodifiableMap(customCharset); + } + if (customMblen != null) { + this.mysqlCharsetToCustomMblen = Collections.unmodifiableMap(customMblen); + } + } + + private boolean canHandleAsServerPreparedStatement(String sql) throws SQLException { + if (sql == null || sql.length() == 0) { + return true; + } + + if (!this.useServerPreparedStmts) { + return false; + } + + if (getCachePreparedStatements()) { + synchronized (this.serverSideStatementCheckCache) { + Boolean flag = (Boolean) this.serverSideStatementCheckCache.get(sql); + + if (flag != null) { + return flag.booleanValue(); + } + + boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql); + + if (sql.length() < getPreparedStatementCacheSqlLimit()) { + this.serverSideStatementCheckCache.put(sql, canHandle ? Boolean.TRUE : Boolean.FALSE); + } + + return canHandle; + } + } + + return canHandleAsServerPreparedStatementNoCache(sql); + } + + private boolean canHandleAsServerPreparedStatementNoCache(String sql) throws SQLException { + + // Can't use server-side prepare for CALL + if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) { + return false; + } + + boolean canHandleAsStatement = true; + + if (!versionMeetsMinimum(5, 0, 7) && (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT") + || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "DELETE") || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "INSERT") + || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "UPDATE") || StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "REPLACE"))) { + + // check for limit ?[,?] + + /* + * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM + */ + + int currentPos = 0; + int statementLength = sql.length(); + int lastPosToLook = statementLength - 7; // "LIMIT ".length() + boolean allowBackslashEscapes = !this.noBackslashEscapes; + String quoteChar = this.useAnsiQuotes ? "\"" : "'"; + boolean foundLimitWithPlaceholder = false; + + while (currentPos < lastPosToLook) { + int limitStart = StringUtils.indexOfIgnoreCase(currentPos, sql, "LIMIT ", quoteChar, quoteChar, + allowBackslashEscapes ? StringUtils.SEARCH_MODE__ALL : StringUtils.SEARCH_MODE__MRK_COM_WS); + + if (limitStart == -1) { + break; + } + + currentPos = limitStart + 7; + + while (currentPos < statementLength) { + char c = sql.charAt(currentPos); + + // + // Have we reached the end of what can be in a LIMIT clause? + // + + if (!Character.isDigit(c) && !Character.isWhitespace(c) && c != ',' && c != '?') { + break; + } + + if (c == '?') { + foundLimitWithPlaceholder = true; + break; + } + + currentPos++; + } + } + + canHandleAsStatement = !foundLimitWithPlaceholder; + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "XA ")) { + canHandleAsStatement = false; + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) { + canHandleAsStatement = false; + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) { + canHandleAsStatement = false; + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) { + canHandleAsStatement = false; + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SHOW WARNINGS") && versionMeetsMinimum(5, 7, 2)) { + canHandleAsStatement = false; + } + + return canHandleAsStatement; + } + + /** + * Changes the user on this connection by performing a re-authentication. If + * authentication fails, the connection will remain under the context of the + * current user. + * + * @param userName + * the username to authenticate with + * @param newPassword + * the password to authenticate with + * @throws SQLException + * if authentication fails, or some other error occurs while + * performing the command. + */ + public void changeUser(String userName, String newPassword) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if ((userName == null) || userName.equals("")) { + userName = ""; + } + + if (newPassword == null) { + newPassword = ""; + } + + // reset maxRows to default value + this.sessionMaxRows = -1; + + try { + this.io.changeUser(userName, newPassword, this.database); + } catch (SQLException ex) { + if (versionMeetsMinimum(5, 6, 13) && "28000".equals(ex.getSQLState())) { + cleanup(ex); + } + throw ex; + } + this.user = userName; + this.password = newPassword; + + if (versionMeetsMinimum(4, 1, 0)) { + configureClientCharacterSet(true); + } + + setSessionVariables(); + + setupServerForTruncationChecks(); + } + } + + private boolean characterSetNamesMatches(String mysqlEncodingName) { + // set names is equivalent to character_set_client ..._results and ..._connection, but we set _results later, so don't check it here. + return (mysqlEncodingName != null && mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_client")) + && mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_connection"))); + } + + private void checkAndCreatePerformanceHistogram() { + if (this.perfMetricsHistCounts == null) { + this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS]; + } + + if (this.perfMetricsHistBreakpoints == null) { + this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS]; + } + } + + private void checkAndCreateTablesAccessedHistogram() { + if (this.numTablesMetricsHistCounts == null) { + this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS]; + } + + if (this.numTablesMetricsHistBreakpoints == null) { + this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS]; + } + } + + public void checkClosed() throws SQLException { + if (this.isClosed) { + throwConnectionClosedException(); + } + } + + public void throwConnectionClosedException() throws SQLException { + SQLException ex = SQLError.createSQLException("No operations allowed after connection closed.", SQLError.SQL_STATE_CONNECTION_NOT_OPEN, + getExceptionInterceptor()); + + if (this.forceClosedReason != null) { + ex.initCause(this.forceClosedReason); + } + + throw ex; + } + + /** + * If useUnicode flag is set and explicit client character encoding isn't + * specified then assign encoding from server if any. + * + * @throws SQLException + */ + private void checkServerEncoding() throws SQLException { + if (getUseUnicode() && (getEncoding() != null)) { + // spec'd by client, don't map + return; + } + + String serverCharset = this.serverVariables.get("character_set"); + + if (serverCharset == null) { + // must be 4.1.1 or newer? + serverCharset = this.serverVariables.get("character_set_server"); + } + + String mappedServerEncoding = null; + + if (serverCharset != null) { + try { + mappedServerEncoding = CharsetMapping.getJavaEncodingForMysqlCharset(serverCharset); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + } + + // + // First check if we can do the encoding ourselves + // + if (!getUseUnicode() && (mappedServerEncoding != null)) { + SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding); + + if (converter != null) { // we know how to convert this ourselves + setUseUnicode(true); // force the issue + setEncoding(mappedServerEncoding); + + return; + } + } + + // + // Now, try and find a Java I/O converter that can do the encoding for us + // + if (serverCharset != null) { + if (mappedServerEncoding == null) { + // We don't have a mapping for it, so try and canonicalize the name.... + if (Character.isLowerCase(serverCharset.charAt(0))) { + char[] ach = serverCharset.toCharArray(); + ach[0] = Character.toUpperCase(serverCharset.charAt(0)); + setEncoding(new String(ach)); + } + } + + if (mappedServerEncoding == null) { + throw SQLError.createSQLException( + "Unknown character encoding on server '" + serverCharset + "', use 'characterEncoding=' property " + " to provide correct mapping", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor()); + } + + // + // Attempt to use the encoding, and bail out if it can't be used + // + try { + StringUtils.getBytes("abc", mappedServerEncoding); + setEncoding(mappedServerEncoding); + setUseUnicode(true); + } catch (UnsupportedEncodingException UE) { + throw SQLError.createSQLException("The driver can not map the character encoding '" + getEncoding() + "' that your server is using " + + "to a character encoding your JVM understands. You can specify this mapping manually by adding \"useUnicode=true\" " + + "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" to your JDBC URL.", "0S100", getExceptionInterceptor()); + } + } + } + + /** + * Set transaction isolation level to the value received from server if any. + * Is called by connectionInit(...) + * + * @throws SQLException + */ + private void checkTransactionIsolationLevel() throws SQLException { + String txIsolationName = null; + + if (versionMeetsMinimum(4, 0, 3)) { + txIsolationName = "tx_isolation"; + } else { + txIsolationName = "transaction_isolation"; + } + + String s = this.serverVariables.get(txIsolationName); + + if (s != null) { + Integer intTI = mapTransIsolationNameToValue.get(s); + + if (intTI != null) { + this.isolationLevel = intTI.intValue(); + } + } + } + + /** + * Clobbers the physical network connection and marks + * this connection as closed. + * + * @throws SQLException + */ + public void abortInternal() throws SQLException { + if (this.io != null) { + // checking this.io != null isn't enough if connection is used concurrently (the usual situation + // with application servers which have additional thread management), this.io can become null + // at any moment after this check, causing a race condition and NPEs on next calls; + // but we may ignore them because at this stage null this.io means that we successfully closed all resources by other thread. + try { + this.io.forceClose(); + this.io.releaseResources(); + } catch (Throwable t) { + // can't do anything about it, and we're forcibly aborting + } + this.io = null; + } + + this.isClosed = true; + } + + /** + * Destroys this connection and any underlying resources + * + * @param fromWhere + * @param whyCleanedUp + */ + private void cleanup(Throwable whyCleanedUp) { + try { + if (this.io != null) { + if (isClosed()) { + this.io.forceClose(); + } else { + realClose(false, false, false, whyCleanedUp); + } + } + } catch (SQLException sqlEx) { + // ignore, we're going away. + } + + this.isClosed = true; + } + + @Deprecated + public void clearHasTriedMaster() { + this.hasTriedMasterFlag = false; + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this connection. + * + * @exception SQLException + * if a database access error occurs + */ + public void clearWarnings() throws SQLException { + // firstWarning = null; + } + + /** + * @param sql + * @throws SQLException + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException { + return clientPrepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * @see Connection#prepareStatement(String, int) + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + java.sql.PreparedStatement pStmt = clientPrepareStatement(sql); + + ((com.mysql.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * @param sql + * @param resultSetType + * @param resultSetConcurrency + * @throws SQLException + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, boolean processEscapeCodesIfNeeded) + throws SQLException { + checkClosed(); + + String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql; + + PreparedStatement pStmt = null; + + if (getCachePreparedStatements()) { + PreparedStatement.ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql); + + if (pStmtInfo == null) { + pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database); + + this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo()); + } else { + pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, pStmtInfo); + } + } else { + pStmt = com.mysql.jdbc.PreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database); + } + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + + return pStmt; + } + + /** + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + + PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + /** + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true); + } + + // --------------------------JDBC 2.0----------------------------- + + /** + * In some cases, it is desirable to immediately release a Connection's + * database and JDBC resources instead of waiting for them to be + * automatically released (cant think why off the top of my head) Note: + * A Connection is automatically closed when it is garbage collected. + * Certain fatal errors also result in a closed connection. + * + * @exception SQLException + * if a database access error occurs + */ + public void close() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + @Override + void forEach(Extension each) throws SQLException { + ((ConnectionLifecycleInterceptor) each).close(); + } + }.doForAll(); + } + + realClose(true, true, false, null); + } + } + + /** + * Closes all currently open statements. + * + * @throws SQLException + */ + private void closeAllOpenStatements() throws SQLException { + SQLException postponedException = null; + + for (Statement stmt : this.openStatements) { + try { + ((StatementImpl) stmt).realClose(false, true); + } catch (SQLException sqlEx) { + postponedException = sqlEx; // throw it later, cleanup all statements first + } + } + + if (postponedException != null) { + throw postponedException; + } + } + + private void closeStatement(java.sql.Statement stmt) { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + + stmt = null; + } + } + + /** + * The method commit() makes all changes made since the previous + * commit/rollback permanent and releases any database locks currently held + * by the Connection. This method should only be used when auto-commit has + * been disabled. + *

    + * Note: MySQL does not support transactions, so this method is a no-op. + *

    + * + * @exception SQLException + * if a database access error occurs + * @see setAutoCommit + */ + public void commit() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + if (!((ConnectionLifecycleInterceptor) each).commit()) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + // no-op if _relaxAutoCommit == true + if (this.autoCommit && !getRelaxAutoCommit()) { + throw SQLError.createSQLException("Can't call commit when autocommit=true", getExceptionInterceptor()); + } else if (this.transactionsSupported) { + if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) { + if (!this.io.inTransactionOnServer()) { + return; // effectively a no-op + } + } + + execSQL(null, "commit", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } + } catch (SQLException sqlException) { + if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) { + throw SQLError.createSQLException("Communications link failure during commit(). Transaction resolution unknown.", + SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor()); + } + + throw sqlException; + } finally { + this.needsPing = this.getReconnectAtTxEnd(); + } + } + return; + } + + /** + * Configures client-side properties for character set information. + * + * @throws SQLException + * if unable to configure the specified character set. + */ + private void configureCharsetProperties() throws SQLException { + if (getEncoding() != null) { + // Attempt to use the encoding, and bail out if it can't be used + try { + String testString = "abc"; + StringUtils.getBytes(testString, getEncoding()); + } catch (UnsupportedEncodingException UE) { + // Try the MySQL character encoding, then.... + String oldEncoding = getEncoding(); + + try { + setEncoding(CharsetMapping.getJavaEncodingForMysqlCharset(oldEncoding)); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + if (getEncoding() == null) { + throw SQLError.createSQLException("Java does not support the MySQL character encoding '" + oldEncoding + "'.", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor()); + } + + try { + String testString = "abc"; + StringUtils.getBytes(testString, getEncoding()); + } catch (UnsupportedEncodingException encodingEx) { + throw SQLError.createSQLException("Unsupported character encoding '" + getEncoding() + "'.", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor()); + } + } + } + } + + /** + * Sets up client character set for MySQL-4.1 and newer if the user This + * must be done before any further communication with the server! + * + * @return true if this routine actually configured the client character + * set, or false if the driver needs to use 'older' methods to + * detect the character set, as it is connected to a MySQL server + * older than 4.1.0 + * @throws SQLException + * if an exception happens while sending 'SET NAMES' to the + * server, or the server sends character set information that + * the client doesn't know about. + */ + private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException { + String realJavaEncoding = getEncoding(); + boolean characterSetAlreadyConfigured = false; + + try { + if (versionMeetsMinimum(4, 1, 0)) { + characterSetAlreadyConfigured = true; + + setUseUnicode(true); + + configureCharsetProperties(); + realJavaEncoding = getEncoding(); // we need to do this again to grab this for versions > 4.1.0 + + try { + + // Fault injection for testing server character set indices + + if (this.props != null && this.props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) { + this.io.serverCharsetIndex = Integer.parseInt(this.props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex")); + } + + String serverEncodingToSet = CharsetMapping.getJavaEncodingForCollationIndex(this.io.serverCharsetIndex); + + if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) { + if (realJavaEncoding != null) { + // user knows best, try it + setEncoding(realJavaEncoding); + } else { + throw SQLError.createSQLException( + "Unknown initial character set index '" + this.io.serverCharsetIndex + + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + // "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1 + if (versionMeetsMinimum(4, 1, 0) && "ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) { + serverEncodingToSet = "Cp1252"; + } + if ("UnicodeBig".equalsIgnoreCase(serverEncodingToSet) || "UTF-16".equalsIgnoreCase(serverEncodingToSet) + || "UTF-16LE".equalsIgnoreCase(serverEncodingToSet) || "UTF-32".equalsIgnoreCase(serverEncodingToSet)) { + serverEncodingToSet = "UTF-8"; + } + + setEncoding(serverEncodingToSet); + + } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) { + if (realJavaEncoding != null) { + // user knows best, try it + setEncoding(realJavaEncoding); + } else { + throw SQLError.createSQLException( + "Unknown initial character set index '" + this.io.serverCharsetIndex + + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + if (getEncoding() == null) { + // punt? + setEncoding("ISO8859_1"); + } + + // + // Has the user has 'forced' the character encoding via driver properties? + // + if (getUseUnicode()) { + if (realJavaEncoding != null) { + + // + // Now, inform the server what character set we will be using from now-on... + // + if (realJavaEncoding.equalsIgnoreCase("UTF-8") || realJavaEncoding.equalsIgnoreCase("UTF8")) { + // charset names are case-sensitive + + boolean utf8mb4Supported = versionMeetsMinimum(5, 5, 2); + boolean useutf8mb4 = utf8mb4Supported && (CharsetMapping.UTF8MB4_INDEXES.contains(this.io.serverCharsetIndex)); + + if (!getUseOldUTF8Behavior()) { + if (dontCheckServerMatch || !characterSetNamesMatches("utf8") || (utf8mb4Supported && !characterSetNamesMatches("utf8mb4"))) { + execSQL(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8"), -1, null, DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + this.serverVariables.put("character_set_client", useutf8mb4 ? "utf8mb4" : "utf8"); + this.serverVariables.put("character_set_connection", useutf8mb4 ? "utf8mb4" : "utf8"); + } + } else { + execSQL(null, "SET NAMES latin1", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, + false); + this.serverVariables.put("character_set_client", "latin1"); + this.serverVariables.put("character_set_connection", "latin1"); + } + + setEncoding(realJavaEncoding); + } /* not utf-8 */else { + String mysqlCharsetName = CharsetMapping.getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), this); + + /* + * if ("koi8_ru".equals(mysqlEncodingName)) { // + * This has a _different_ name in 4.1... + * mysqlEncodingName = "ko18r"; } else if + * ("euc_kr".equals(mysqlEncodingName)) { // + * Different name in 4.1 mysqlEncodingName = + * "euckr"; } + */ + + if (mysqlCharsetName != null) { + + if (dontCheckServerMatch || !characterSetNamesMatches(mysqlCharsetName)) { + execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, + this.database, null, false); + this.serverVariables.put("character_set_client", mysqlCharsetName); + this.serverVariables.put("character_set_connection", mysqlCharsetName); + } + } + + // Switch driver's encoding now, since the server knows what we're sending... + // + setEncoding(realJavaEncoding); + } + } else if (getEncoding() != null) { + // Tell the server we'll use the server default charset to send our queries from now on.... + String mysqlCharsetName = getServerCharset(); + + if (getUseOldUTF8Behavior()) { + mysqlCharsetName = "latin1"; + } + + boolean ucs2 = false; + if ("ucs2".equalsIgnoreCase(mysqlCharsetName) || "utf16".equalsIgnoreCase(mysqlCharsetName) + || "utf16le".equalsIgnoreCase(mysqlCharsetName) || "utf32".equalsIgnoreCase(mysqlCharsetName)) { + mysqlCharsetName = "utf8"; + ucs2 = true; + if (getCharacterSetResults() == null) { + setCharacterSetResults("UTF-8"); + } + } + + if (dontCheckServerMatch || !characterSetNamesMatches(mysqlCharsetName) || ucs2) { + try { + execSQL(null, "SET NAMES " + mysqlCharsetName, -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, + this.database, null, false); + this.serverVariables.put("character_set_client", mysqlCharsetName); + this.serverVariables.put("character_set_connection", mysqlCharsetName); + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + } + + realJavaEncoding = getEncoding(); + } + + } + + // + // We know how to deal with any charset coming back from the database, so tell the server not to do conversion if the user hasn't 'forced' a + // result-set character set + // + + String onServer = null; + boolean isNullOnServer = false; + + if (this.serverVariables != null) { + onServer = this.serverVariables.get("character_set_results"); + + isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0; + } + + if (getCharacterSetResults() == null) { + + // + // Only send if needed, if we're caching server variables we -have- to send, because we don't know what it was before we cached them. + // + if (!isNullOnServer) { + try { + execSQL(null, "SET character_set_results = NULL", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, + this.database, null, false); + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null); + } else { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } else { + + if (getUseOldUTF8Behavior()) { + try { + execSQL(null, "SET NAMES latin1", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, + false); + this.serverVariables.put("character_set_client", "latin1"); + this.serverVariables.put("character_set_connection", "latin1"); + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + } + String charsetResults = getCharacterSetResults(); + String mysqlEncodingName = null; + + if ("UTF-8".equalsIgnoreCase(charsetResults) || "UTF8".equalsIgnoreCase(charsetResults)) { + mysqlEncodingName = "utf8"; + } else if ("null".equalsIgnoreCase(charsetResults)) { + mysqlEncodingName = "NULL"; + } else { + mysqlEncodingName = CharsetMapping.getMysqlCharsetForJavaEncoding(charsetResults.toUpperCase(Locale.ENGLISH), this); + } + + // + // Only change the value if needed + // + + if (mysqlEncodingName == null) { + throw SQLError.createSQLException("Can't map " + charsetResults + " given for characterSetResults to a supported MySQL encoding.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (!mysqlEncodingName.equalsIgnoreCase(this.serverVariables.get("character_set_results"))) { + StringBuilder setBuf = new StringBuilder("SET character_set_results = ".length() + mysqlEncodingName.length()); + setBuf.append("SET character_set_results = ").append(mysqlEncodingName); + + try { + execSQL(null, setBuf.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, + false); + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, mysqlEncodingName); + + // We have to set errorMessageEncoding according to new value of charsetResults for server version 5.5 and higher + if (versionMeetsMinimum(5, 5, 0)) { + this.errorMessageEncoding = charsetResults; + } + + } else { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } + + if (getConnectionCollation() != null) { + StringBuilder setBuf = new StringBuilder("SET collation_connection = ".length() + getConnectionCollation().length()); + setBuf.append("SET collation_connection = ").append(getConnectionCollation()); + + try { + execSQL(null, setBuf.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + } + } else { + // Use what the server has specified + realJavaEncoding = getEncoding(); // so we don't get + // swapped out in the finally block.... + } + } finally { + // Failsafe, make sure that the driver's notion of character encoding matches what the user has specified. + // + setEncoding(realJavaEncoding); + } + + /** + * Check if we need a CharsetEncoder for escaping codepoints that are + * transformed to backslash (0x5c) in the connection encoding. + */ + try { + CharsetEncoder enc = Charset.forName(getEncoding()).newEncoder(); + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + + cbuf.put("\u00a5"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + this.requiresEscapingEncoder = true; + } else { + cbuf.clear(); + bbuf.clear(); + + cbuf.put("\u20a9"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + this.requiresEscapingEncoder = true; + } + } + } catch (java.nio.charset.UnsupportedCharsetException ucex) { + // fallback to String API - for Java 1.4 + try { + byte bbuf[] = StringUtils.getBytes("\u00a5", getEncoding()); + if (bbuf[0] == '\\') { + this.requiresEscapingEncoder = true; + } else { + bbuf = StringUtils.getBytes("\u20a9", getEncoding()); + if (bbuf[0] == '\\') { + this.requiresEscapingEncoder = true; + } + } + } catch (UnsupportedEncodingException ueex) { + throw SQLError.createSQLException("Unable to use encoding: " + getEncoding(), SQLError.SQL_STATE_GENERAL_ERROR, ueex, + getExceptionInterceptor()); + } + } + + return characterSetAlreadyConfigured; + } + + /** + * Configures the client's timezone if required. + * + * @throws SQLException + * if the timezone the server is configured to use can't be + * mapped to a Java timezone. + */ + private void configureTimezone() throws SQLException { + String configuredTimeZoneOnServer = this.serverVariables.get("timezone"); + + if (configuredTimeZoneOnServer == null) { + configuredTimeZoneOnServer = this.serverVariables.get("time_zone"); + + if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) { + configuredTimeZoneOnServer = this.serverVariables.get("system_time_zone"); + } + } + + String canonicalTimezone = getServerTimezone(); + + if ((getUseTimezone() || !getUseLegacyDatetimeCode()) && configuredTimeZoneOnServer != null) { + // user can override this with driver properties, so don't detect if that's the case + if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) { + try { + canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor()); + } catch (IllegalArgumentException iae) { + throw SQLError.createSQLException(iae.getMessage(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + } + + if (canonicalTimezone != null && canonicalTimezone.length() > 0) { + this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone); + + // + // The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this... + // + if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverTimezoneTZ.getID().equals("GMT")) { + throw SQLError.createSQLException("No timezone mapping entry for '" + canonicalTimezone + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + this.isServerTzUTC = !this.serverTimezoneTZ.useDaylightTime() && this.serverTimezoneTZ.getRawOffset() == 0; + } + } + + private void createInitialHistogram(long[] breakpoints, long lowerBound, long upperBound) { + + double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25; + + if (bucketSize < 1) { + bucketSize = 1; + } + + for (int i = 0; i < HISTOGRAM_BUCKETS; i++) { + breakpoints[i] = lowerBound; + lowerBound += bucketSize; + } + } + + /** + * Creates an IO channel to the server + * + * @param isForReconnect + * is this request for a re-connect + * @return a new MysqlIO instance connected to a server + * @throws SQLException + * if a database access error occurs + * @throws CommunicationsException + */ + public void createNewIO(boolean isForReconnect) throws SQLException { + synchronized (getConnectionMutex()) { + // Synchronization Not needed for *new* connections, but defintely for connections going through fail-over, since we might get the new connection up + // and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to + // re-prepare them... + + Properties mergedProps = exposeAsProperties(this.props); + + if (!getHighAvailability()) { + connectOneTryOnly(isForReconnect, mergedProps); + + return; + } + + connectWithRetries(isForReconnect, mergedProps); + } + } + + private void connectWithRetries(boolean isForReconnect, Properties mergedProps) throws SQLException { + double timeout = getInitialTimeout(); + boolean connectionGood = false; + + Exception connectionException = null; + + for (int attemptCount = 0; (attemptCount < getMaxReconnects()) && !connectionGood; attemptCount++) { + try { + if (this.io != null) { + this.io.forceClose(); + } + + coreConnect(mergedProps); + pingInternal(false, 0); + + boolean oldAutoCommit; + int oldIsolationLevel; + boolean oldReadOnly; + String oldCatalog; + + synchronized (getConnectionMutex()) { + this.connectionId = this.io.getThreadId(); + this.isClosed = false; + + // save state from old connection + oldAutoCommit = getAutoCommit(); + oldIsolationLevel = this.isolationLevel; + oldReadOnly = isReadOnly(false); + oldCatalog = getCatalog(); + + this.io.setStatementInterceptors(this.statementInterceptors); + } + + // Server properties might be different from previous connection, so initialize again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + + if (this.hasIsolationLevels) { + setTransactionIsolation(oldIsolationLevel); + } + + setCatalog(oldCatalog); + setReadOnly(oldReadOnly); + } + + connectionGood = true; + + break; + } catch (Exception EEE) { + connectionException = EEE; + connectionGood = false; + } + + if (connectionGood) { + break; + } + + if (attemptCount > 0) { + try { + Thread.sleep((long) timeout * 1000); + } catch (InterruptedException IE) { + // ignore + } + } + } // end attempts for a single host + + if (!connectionGood) { + // We've really failed! + SQLException chainedEx = SQLError.createSQLException( + Messages.getString("Connection.UnableToConnectWithRetries", new Object[] { Integer.valueOf(getMaxReconnects()) }), + SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + chainedEx.initCause(connectionException); + + throw chainedEx; + } + + if (getParanoid() && !getHighAvailability()) { + this.password = null; + this.user = null; + } + + if (isForReconnect) { + // + // Retrieve any 'lost' prepared statements if re-connecting + // + Iterator statementIter = this.openStatements.iterator(); + + // + // We build a list of these outside the map of open statements, because in the process of re-preparing, we might end up having to close a prepared + // statement, thus removing it from the map, and generating a ConcurrentModificationException + // + Stack serverPreparedStatements = null; + + while (statementIter.hasNext()) { + Statement statementObj = statementIter.next(); + + if (statementObj instanceof ServerPreparedStatement) { + if (serverPreparedStatements == null) { + serverPreparedStatements = new Stack(); + } + + serverPreparedStatements.add(statementObj); + } + } + + if (serverPreparedStatements != null) { + while (!serverPreparedStatements.isEmpty()) { + ((ServerPreparedStatement) serverPreparedStatements.pop()).rePrepare(); + } + } + } + } + + private void coreConnect(Properties mergedProps) throws SQLException, IOException { + int newPort = 3306; + String newHost = "localhost"; + + String protocol = mergedProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY); + + if (protocol != null) { + // "new" style URL + + if ("tcp".equalsIgnoreCase(protocol)) { + newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY)); + newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306")); + } else if ("pipe".equalsIgnoreCase(protocol)) { + setSocketFactoryClassName(NamedPipeSocketFactory.class.getName()); + + String path = mergedProps.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY); + + if (path != null) { + mergedProps.setProperty(NamedPipeSocketFactory.NAMED_PIPE_PROP_NAME, path); + } + } else { + // normalize for all unknown protocols + newHost = normalizeHost(mergedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY)); + newPort = parsePortNumber(mergedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306")); + } + } else { + + String[] parsedHostPortPair = NonRegisteringDriver.parseHostPortPair(this.hostPortPair); + newHost = parsedHostPortPair[NonRegisteringDriver.HOST_NAME_INDEX]; + + newHost = normalizeHost(newHost); + + if (parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) { + newPort = parsePortNumber(parsedHostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]); + } + } + + this.port = newPort; + this.host = newHost; + + // reset max-rows to default value + this.sessionMaxRows = -1; + + this.io = new MysqlIO(newHost, newPort, mergedProps, getSocketFactoryClassName(), getProxy(), getSocketTimeout(), + this.largeRowSizeThreshold.getValueAsInt()); + this.io.doHandshake(this.user, this.password, this.database); + if (versionMeetsMinimum(5, 5, 0)) { + // error messages are returned according to character_set_results which, at this point, is set from the response packet + this.errorMessageEncoding = this.io.getEncodingForHandshake(); + } + } + + private String normalizeHost(String hostname) { + if (hostname == null || StringUtils.isEmptyOrWhitespaceOnly(hostname)) { + return "localhost"; + } + + return hostname; + } + + private int parsePortNumber(String portAsString) throws SQLException { + int portNumber = 3306; + try { + portNumber = Integer.parseInt(portAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException("Illegal connection port value '" + portAsString + "'", SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, + getExceptionInterceptor()); + } + return portNumber; + } + + private void connectOneTryOnly(boolean isForReconnect, Properties mergedProps) throws SQLException { + Exception connectionNotEstablishedBecause = null; + + try { + + coreConnect(mergedProps); + this.connectionId = this.io.getThreadId(); + this.isClosed = false; + + // save state from old connection + boolean oldAutoCommit = getAutoCommit(); + int oldIsolationLevel = this.isolationLevel; + boolean oldReadOnly = isReadOnly(false); + String oldCatalog = getCatalog(); + + this.io.setStatementInterceptors(this.statementInterceptors); + + // Server properties might be different from previous connection, so initialize again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + + if (this.hasIsolationLevels) { + setTransactionIsolation(oldIsolationLevel); + } + + setCatalog(oldCatalog); + + setReadOnly(oldReadOnly); + } + return; + + } catch (Exception EEE) { + + if (EEE instanceof SQLException && ((SQLException) EEE).getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD + && !getDisconnectOnExpiredPasswords()) { + return; + } + + if (this.io != null) { + this.io.forceClose(); + } + + connectionNotEstablishedBecause = EEE; + + if (EEE instanceof SQLException) { + throw (SQLException) EEE; + } + + SQLException chainedEx = SQLError.createSQLException(Messages.getString("Connection.UnableToConnect"), + SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + chainedEx.initCause(connectionNotEstablishedBecause); + + throw chainedEx; + } + } + + private void createPreparedStatementCaches() throws SQLException { + synchronized (getConnectionMutex()) { + int cacheSize = getPreparedStatementCacheSize(); + + try { + Class factoryClass; + + factoryClass = Class.forName(getParseInfoCacheFactory()); + + @SuppressWarnings("unchecked") + CacheAdapterFactory cacheFactory = ((CacheAdapterFactory) factoryClass.newInstance()); + + this.cachedPreparedStatementParams = cacheFactory.getInstance(this, this.myURL, getPreparedStatementCacheSize(), + getPreparedStatementCacheSqlLimit(), this.props); + + } catch (ClassNotFoundException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantFindCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } catch (InstantiationException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantLoadCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } catch (IllegalAccessException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantLoadCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + + if (getUseServerPreparedStmts()) { + this.serverSideStatementCheckCache = new LRUCache(cacheSize); + + this.serverSideStatementCache = new LRUCache(cacheSize) { + + private static final long serialVersionUID = 7692318650375988114L; + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + if (this.maxElements <= 1) { + return false; + } + + boolean removeIt = super.removeEldestEntry(eldest); + + if (removeIt) { + ServerPreparedStatement ps = (ServerPreparedStatement) eldest.getValue(); + ps.isCached = false; + ps.setClosed(false); + + try { + ps.close(); + } catch (SQLException sqlEx) { + // punt + } + } + + return removeIt; + } + }; + } + } + } + + /** + * SQL statements without parameters are normally executed using Statement + * objects. If the same SQL statement is executed many times, it is more + * efficient to use a PreparedStatement + * + * @return a new Statement object + * @throws SQLException + * passed through from the constructor + */ + public java.sql.Statement createStatement() throws SQLException { + return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * JDBC 2.0 Same as createStatement() above, but allows the default result + * set type and result set concurrency type to be overridden. + * + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new Statement object + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + checkClosed(); + + StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database); + stmt.setResultSetType(resultSetType); + stmt.setResultSetConcurrency(resultSetConcurrency); + + return stmt; + } + + /** + * @see Connection#createStatement(int, int, int) + */ + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (getPedantic()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + return createStatement(resultSetType, resultSetConcurrency); + } + + public void dumpTestcaseQuery(String query) { + System.err.println(query); + } + + public Connection duplicate() throws SQLException { + return new ConnectionImpl(this.origHostToConnectTo, this.origPortToConnectTo, this.props, this.origDatabaseToConnectTo, this.myURL); + } + + /** + * Send a query to the server. Returns one of the ResultSet objects. This is + * synchronized, so Statement's queries will be serialized. + * + * @param callingStatement + * @param sql + * the SQL statement to be executed + * @param maxRows + * @param packet + * @param resultSetType + * @param resultSetConcurrency + * @param streamResults + * @param queryIsSelectOnly + * @param catalog + * @param unpackFields + * @return a ResultSet holding the results + * @exception SQLException + * if a database error occurs + */ + + // ResultSet execSQL(Statement callingStatement, String sql, + // int maxRowsToRetreive, String catalog) throws SQLException { + // return execSQL(callingStatement, sql, maxRowsToRetreive, null, + // java.sql.ResultSet.TYPE_FORWARD_ONLY, + // DEFAULT_RESULT_SET_CONCURRENCY, catalog); + // } + // ResultSet execSQL(Statement callingStatement, String sql, int maxRows, + // int resultSetType, int resultSetConcurrency, boolean streamResults, + // boolean queryIsSelectOnly, String catalog, boolean unpackFields) throws + // SQLException { + // return execSQL(callingStatement, sql, maxRows, null, resultSetType, + // resultSetConcurrency, streamResults, queryIsSelectOnly, catalog, + // unpackFields); + // } + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata) throws SQLException { + return execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog, cachedMetadata, false); + } + + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException { + synchronized (getConnectionMutex()) { + // + // Fall-back if the master is back online if we've issued queriesBeforeRetryMaster queries since we failed over + // + + long queryStartTime = 0; + + int endOfQueryPacketPosition = 0; + + if (packet != null) { + endOfQueryPacketPosition = packet.getPosition(); + } + + if (getGatherPerformanceMetrics()) { + queryStartTime = System.currentTimeMillis(); + } + + this.lastQueryFinishedTime = 0; // we're busy! + + if ((getHighAvailability()) && (this.autoCommit || getAutoReconnectForPools()) && this.needsPing && !isBatch) { + try { + pingInternal(false, 0); + + this.needsPing = false; + } catch (Exception Ex) { + createNewIO(true); + } + } + + try { + if (packet == null) { + String encoding = null; + + if (getUseUnicode()) { + encoding = getEncoding(); + } + + return this.io.sqlQueryDirect(callingStatement, sql, encoding, null, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata); + } + + return this.io.sqlQueryDirect(callingStatement, null, null, packet, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata); + } catch (java.sql.SQLException sqlE) { + // don't clobber SQL exceptions + + if (getDumpQueriesOnException()) { + String extractedSql = extractSqlFromPacket(sql, packet, endOfQueryPacketPosition); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + + sqlE = appendMessageToException(sqlE, messageBuf.toString(), getExceptionInterceptor()); + } + + if ((getHighAvailability())) { + this.needsPing = true; + } else { + String sqlState = sqlE.getSQLState(); + + if ((sqlState != null) && sqlState.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) { + cleanup(sqlE); + } + } + + throw sqlE; + } catch (Exception ex) { + if (getHighAvailability()) { + this.needsPing = true; + } else if (ex instanceof IOException) { + cleanup(ex); + } + + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.UnexpectedException"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } finally { + if (getMaintainTimeStats()) { + this.lastQueryFinishedTime = System.currentTimeMillis(); + } + + if (getGatherPerformanceMetrics()) { + long queryTime = System.currentTimeMillis() - queryStartTime; + + registerQueryExecutionTime(queryTime); + } + } + } + } + + public String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket, int endOfQueryPacketPosition) throws SQLException { + + String extractedSql = null; + + if (possibleSqlQuery != null) { + if (possibleSqlQuery.length() > getMaxQuerySizeToLog()) { + StringBuilder truncatedQueryBuf = new StringBuilder(possibleSqlQuery.substring(0, getMaxQuerySizeToLog())); + truncatedQueryBuf.append(Messages.getString("MysqlIO.25")); + extractedSql = truncatedQueryBuf.toString(); + } else { + extractedSql = possibleSqlQuery; + } + } + + if (extractedSql == null) { + // This is probably from a client-side prepared statement + + int extractPosition = endOfQueryPacketPosition; + + boolean truncated = false; + + if (endOfQueryPacketPosition > getMaxQuerySizeToLog()) { + extractPosition = getMaxQuerySizeToLog(); + truncated = true; + } + + extractedSql = StringUtils.toString(queryPacket.getByteBuffer(), 5, (extractPosition - 5)); + + if (truncated) { + extractedSql += Messages.getString("MysqlIO.25"); + } + } + + return extractedSql; + + } + + public StringBuilder generateConnectionCommentBlock(StringBuilder buf) { + buf.append("/* conn id "); + buf.append(getId()); + buf.append(" clock: "); + buf.append(System.currentTimeMillis()); + buf.append(" */ "); + + return buf; + } + + public int getActiveStatementCount() { + return this.openStatements.size(); + } + + /** + * Gets the current auto-commit state + * + * @return Current state of auto-commit + * @exception SQLException + * if an error occurs + * @see setAutoCommit + */ + public boolean getAutoCommit() throws SQLException { + synchronized (getConnectionMutex()) { + return this.autoCommit; + } + } + + /** + * Optimization to only use one calendar per-session, or calculate it for + * each call, depending on user configuration + */ + public Calendar getCalendarInstanceForSessionOrNew() { + if (getDynamicCalendars()) { + return Calendar.getInstance(); + } + + return getSessionLockedCalendar(); + } + + /** + * Return the connections current catalog name, or null if no catalog name + * is set, or we dont support catalogs. + *

    + * Note: MySQL's notion of catalogs are individual databases. + *

    + * + * @return the current catalog name or null + * @exception SQLException + * if a database access error occurs + */ + public String getCatalog() throws SQLException { + synchronized (getConnectionMutex()) { + return this.database; + } + } + + /** + * @return Returns the characterSetMetadata. + */ + public String getCharacterSetMetadata() { + synchronized (getConnectionMutex()) { + return this.characterSetMetadata; + } + } + + /** + * Returns the locally mapped instance of a charset converter (to avoid + * overhead of static synchronization). + * + * @param javaEncodingName + * the encoding name to retrieve + * @return a character converter, or null if one couldn't be mapped. + */ + public SingleByteCharsetConverter getCharsetConverter(String javaEncodingName) throws SQLException { + if (javaEncodingName == null) { + return null; + } + + if (this.usePlatformCharsetConverters) { + return null; // we'll use Java's built-in routines for this they're finally fast enough + } + + SingleByteCharsetConverter converter = null; + + synchronized (this.charsetConverterMap) { + Object asObject = this.charsetConverterMap.get(javaEncodingName); + + if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) { + return null; + } + + converter = (SingleByteCharsetConverter) asObject; + + if (converter == null) { + try { + converter = SingleByteCharsetConverter.getInstance(javaEncodingName, this); + + if (converter == null) { + this.charsetConverterMap.put(javaEncodingName, CHARSET_CONVERTER_NOT_AVAILABLE_MARKER); + } else { + this.charsetConverterMap.put(javaEncodingName, converter); + } + } catch (UnsupportedEncodingException unsupEncEx) { + this.charsetConverterMap.put(javaEncodingName, CHARSET_CONVERTER_NOT_AVAILABLE_MARKER); + + converter = null; + } + } + } + + return converter; + } + + /** + * @deprecated replaced by getEncodingForIndex(int charsetIndex) + */ + @Deprecated + public String getCharsetNameForIndex(int charsetIndex) throws SQLException { + return getEncodingForIndex(charsetIndex); + } + + /** + * Returns the Java character encoding name for the given MySQL server + * charset index + * + * @param charsetIndex + * @return the Java character encoding name for the given MySQL server + * charset index + * @throws SQLException + * if the character set index isn't known by the driver + */ + public String getEncodingForIndex(int charsetIndex) throws SQLException { + String javaEncoding = null; + + if (getUseOldUTF8Behavior()) { + return getEncoding(); + } + + if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { + try { + if (this.indexToMysqlCharset.size() > 0) { + javaEncoding = CharsetMapping.getJavaEncodingForMysqlCharset(this.indexToMysqlCharset.get(charsetIndex), getEncoding()); + } + // checking against static maps if no custom charset found + if (javaEncoding == null) { + javaEncoding = CharsetMapping.getJavaEncodingForCollationIndex(charsetIndex, getEncoding()); + } + + } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) { + throw SQLError.createSQLException("Unknown character set index for field '" + charsetIndex + "' received from server.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + // Punt + if (javaEncoding == null) { + javaEncoding = getEncoding(); + } + } else { + javaEncoding = getEncoding(); + } + + return javaEncoding; + } + + /** + * @return Returns the defaultTimeZone. + */ + public TimeZone getDefaultTimeZone() { + // If default time zone is cached then there is no need to get a new instance of it, just use the previous one. + return getCacheDefaultTimezone() ? this.defaultTimeZone : TimeUtil.getDefaultTimeZone(false); + } + + public String getErrorMessageEncoding() { + return this.errorMessageEncoding; + } + + /** + * @see Connection#getHoldability() + */ + public int getHoldability() throws SQLException { + return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT; + } + + public long getId() { + return this.connectionId; + } + + /** + * NOT JDBC-Compliant, but clients can use this method to determine how long + * this connection has been idle. This time (reported in milliseconds) is + * updated once a query has completed. + * + * @return number of ms that this connection has been idle, 0 if the driver + * is busy retrieving results. + */ + public long getIdleFor() { + synchronized (getConnectionMutex()) { + if (this.lastQueryFinishedTime == 0) { + return 0; + } + + long now = System.currentTimeMillis(); + long idleTime = now - this.lastQueryFinishedTime; + + return idleTime; + } + } + + /** + * Returns the IO channel to the server + * + * @return the IO channel to the server + * @throws SQLException + * if the connection is closed. + */ + public MysqlIO getIO() throws SQLException { + if ((this.io == null) || this.isClosed) { + throw SQLError.createSQLException("Operation not allowed on closed connection", SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor()); + } + + return this.io; + } + + /** + * Returns the log mechanism that should be used to log information from/for + * this Connection. + * + * @return the Log instance to use for logging messages. + * @throws SQLException + * if an error occurs + */ + public Log getLog() throws SQLException { + return this.log; + } + + public int getMaxBytesPerChar(String javaCharsetName) throws SQLException { + return getMaxBytesPerChar(null, javaCharsetName); + } + + public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) throws SQLException { + + String charset = null; + int res = 1; + + try { + // if we can get it by charsetIndex just doing it + + // getting charset name from dynamic maps in connection; we do it before checking against static maps because custom charset on server can be mapped + // to index from our static map key's diapason + if (this.indexToCustomMysqlCharset != null) { + charset = this.indexToCustomMysqlCharset.get(charsetIndex); + } + // checking against static maps if no custom charset found + if (charset == null) { + charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(charsetIndex); + } + + // if we didn't find charset name by index + if (charset == null) { + charset = CharsetMapping.getMysqlCharsetForJavaEncoding(javaCharsetName, this); + } + + // checking against dynamic maps in connection + Integer mblen = null; + if (this.mysqlCharsetToCustomMblen != null) { + mblen = this.mysqlCharsetToCustomMblen.get(charset); + } + + // checking against static maps + if (mblen == null) { + mblen = CharsetMapping.getMblen(charset); + } + + if (mblen != null) { + res = mblen.intValue(); + } + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + return res; + } + + /** + * A connection's database is able to provide information describing its + * tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made available + * through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException + * if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException { + return getMetaData(true, true); + } + + private java.sql.DatabaseMetaData getMetaData(boolean checkClosed, boolean checkForInfoSchema) throws SQLException { + if (checkClosed) { + checkClosed(); + } + + return com.mysql.jdbc.DatabaseMetaData.getInstance(getMultiHostSafeProxy(), this.database, checkForInfoSchema); + } + + public java.sql.Statement getMetadataSafeStatement() throws SQLException { + java.sql.Statement stmt = createStatement(); + + if (stmt.getMaxRows() != 0) { + stmt.setMaxRows(0); + } + + stmt.setEscapeProcessing(false); + + if (stmt.getFetchSize() != 0) { + stmt.setFetchSize(0); + } + + return stmt; + } + + /** + * Returns the packet buffer size the MySQL server reported upon connection + */ + public int getNetBufferLength() { + return this.netBufferLength; + } + + /** + * @deprecated replaced by getServerCharset() + */ + @Deprecated + public String getServerCharacterEncoding() { + return getServerCharset(); + } + + /** + * Returns the server's character set + * + * @return the server's character set. + */ + public String getServerCharset() { + if (this.io.versionMeetsMinimum(4, 1, 0)) { + String charset = null; + if (this.indexToCustomMysqlCharset != null) { + charset = this.indexToCustomMysqlCharset.get(this.io.serverCharsetIndex); + } + if (charset == null) { + charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(this.io.serverCharsetIndex); + } + return charset != null ? charset : this.serverVariables.get("character_set_server"); + } + return this.serverVariables.get("character_set"); + } + + public int getServerMajorVersion() { + return this.io.getServerMajorVersion(); + } + + public int getServerMinorVersion() { + return this.io.getServerMinorVersion(); + } + + public int getServerSubMinorVersion() { + return this.io.getServerSubMinorVersion(); + } + + public TimeZone getServerTimezoneTZ() { + return this.serverTimezoneTZ; + } + + public String getServerVariable(String variableName) { + if (this.serverVariables != null) { + return this.serverVariables.get(variableName); + } + + return null; + } + + public String getServerVersion() { + return this.io.getServerVersion(); + } + + public Calendar getSessionLockedCalendar() { + + return this.sessionCalendar; + } + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_ mode value + * @exception SQLException + * if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException { + + synchronized (getConnectionMutex()) { + if (this.hasIsolationLevels && !getUseLocalSessionState()) { + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + stmt = getMetadataSafeStatement(); + + String query = null; + + int offset = 0; + + if (versionMeetsMinimum(4, 0, 3)) { + query = "SELECT @@session.tx_isolation"; + offset = 1; + } else { + query = "SHOW VARIABLES LIKE 'transaction_isolation'"; + offset = 2; + } + + rs = stmt.executeQuery(query); + + if (rs.next()) { + String s = rs.getString(offset); + + if (s != null) { + Integer intTI = mapTransIsolationNameToValue.get(s); + + if (intTI != null) { + return intTI.intValue(); + } + } + + throw SQLError.createSQLException("Could not map transaction isolation '" + s + " to a valid JDBC level.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + throw SQLError.createSQLException("Could not retrieve transaction isolation level from server", SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + // ignore + } + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ex) { + // ignore + } + + stmt = null; + } + } + } + + return this.isolationLevel; + } + } + + /** + * JDBC 2.0 Get the type-map object associated with this connection. By + * default, the map returned is empty. + * + * @return the type map + * @throws SQLException + * if a database error occurs + */ + public java.util.Map> getTypeMap() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.typeMap == null) { + this.typeMap = new HashMap>(); + } + + return this.typeMap; + } + } + + public String getURL() { + return this.myURL; + } + + public String getUser() { + return this.user; + } + + public Calendar getUtcCalendar() { + return this.utcCalendar; + } + + /** + * The first warning reported by calls on this Connection is returned. + * Note: Sebsequent warnings will be changed to this + * java.sql.SQLWarning + * + * @return the first java.sql.SQLWarning or null + * @exception SQLException + * if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException { + return null; + } + + public boolean hasSameProperties(Connection c) { + return this.props.equals(c.getProperties()); + } + + public Properties getProperties() { + return this.props; + } + + @Deprecated + public boolean hasTriedMaster() { + return this.hasTriedMasterFlag; + } + + public void incrementNumberOfPreparedExecutes() { + if (getGatherPerformanceMetrics()) { + this.numberOfPreparedExecutes++; + + // We need to increment this, because server-side prepared statements bypass any execution by the connection itself... + this.numberOfQueriesIssued++; + } + } + + public void incrementNumberOfPrepares() { + if (getGatherPerformanceMetrics()) { + this.numberOfPrepares++; + } + } + + public void incrementNumberOfResultSetsCreated() { + if (getGatherPerformanceMetrics()) { + this.numberOfResultSetsCreated++; + } + } + + /** + * Initializes driver properties that come from URL or properties passed to + * the driver manager. + * + * @param info + * @throws SQLException + */ + private void initializeDriverProperties(Properties info) throws SQLException { + initializeProperties(info); + + String exceptionInterceptorClasses = getExceptionInterceptors(); + + if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) { + this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses); + } + + this.usePlatformCharsetConverters = getUseJvmCharsetConverters(); + + this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME, getExceptionInterceptor()); + + if (getProfileSql() || getUseUsageAdvisor()) { + this.eventSink = ProfilerEventHandlerFactory.getInstance(getMultiHostSafeProxy()); + } + + if (getCachePreparedStatements()) { + createPreparedStatementCaches(); + } + + if (getNoDatetimeStringSync() && getUseTimezone()) { + throw SQLError.createSQLException("Can't enable noDatetimeStringSync and useTimezone configuration properties at the same time", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor()); + } + + if (getCacheCallableStatements()) { + this.parsedCallableStatementCache = new LRUCache(getCallableStatementCacheSize()); + } + + if (getAllowMultiQueries()) { + setCacheResultSetMetadata(false); // we don't handle this yet + } + + if (getCacheResultSetMetadata()) { + this.resultSetMetadataCache = new LRUCache(getMetadataCacheSize()); + } + + if (getSocksProxyHost() != null) { + setSocketFactoryClassName("com.mysql.jdbc.SocksProxySocketFactory"); + } + } + + /** + * Sets varying properties that depend on server information. Called once we + * have connected to the server. + * + * @param info + * @throws SQLException + */ + private void initializePropsFromServer() throws SQLException { + String connectionInterceptorClasses = getConnectionLifecycleInterceptors(); + + this.connectionLifecycleInterceptors = null; + + if (connectionInterceptorClasses != null) { + this.connectionLifecycleInterceptors = Util.loadExtensions(this, this.props, connectionInterceptorClasses, "Connection.badLifecycleInterceptor", + getExceptionInterceptor()); + } + + setSessionVariables(); + + // + // the "boolean" type didn't come along until MySQL-4.1 + // + + if (!versionMeetsMinimum(4, 1, 0)) { + setTransformedBitIsBoolean(false); + } + + this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0); + + // + // Users can turn off detection of server-side prepared statements + // + if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) { + this.useServerPreparedStmts = true; + + if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) { + this.useServerPreparedStmts = false; // 4.1.2+ style prepared + // statements + // don't work on these versions + } + } + + // + // If version is greater than 3.21.22 get the server variables. + if (versionMeetsMinimum(3, 21, 22)) { + loadServerVariables(); + + if (versionMeetsMinimum(5, 0, 2)) { + this.autoIncrementIncrement = getServerVariableAsInt("auto_increment_increment", 1); + } else { + this.autoIncrementIncrement = 1; + } + + buildCollationMapping(); + + LicenseConfiguration.checkLicenseType(this.serverVariables); + + String lowerCaseTables = this.serverVariables.get("lower_case_table_names"); + + this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables) || "1".equalsIgnoreCase(lowerCaseTables) || "2".equalsIgnoreCase(lowerCaseTables); + + this.storesLowerCaseTableName = "1".equalsIgnoreCase(lowerCaseTables) || "on".equalsIgnoreCase(lowerCaseTables); + + configureTimezone(); + + if (this.serverVariables.containsKey("max_allowed_packet")) { + int serverMaxAllowedPacket = getServerVariableAsInt("max_allowed_packet", -1); + // use server value if maxAllowedPacket hasn't been given, or max_allowed_packet is smaller + if (serverMaxAllowedPacket != -1 && (serverMaxAllowedPacket < getMaxAllowedPacket() || getMaxAllowedPacket() <= 0)) { + setMaxAllowedPacket(serverMaxAllowedPacket); + } else if (serverMaxAllowedPacket == -1 && getMaxAllowedPacket() == -1) { + setMaxAllowedPacket(65535); + } + + if (getUseServerPrepStmts()) { + int preferredBlobSendChunkSize = getBlobSendChunkSize(); + + // LONG_DATA and MySQLIO packet header size + int packetHeaderSize = ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE + 11; + int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, getMaxAllowedPacket()) - packetHeaderSize; + + if (allowedBlobSendChunkSize <= 0) { + throw SQLError.createSQLException( + "Connection setting too low for 'maxAllowedPacket'. " + + "When 'useServerPrepStmts=true', 'maxAllowedPacket' must be higher than " + packetHeaderSize + + ". Check also 'max_allowed_packet' in MySQL configuration files.", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, getExceptionInterceptor()); + } + + setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize)); + } + } + + if (this.serverVariables.containsKey("net_buffer_length")) { + this.netBufferLength = getServerVariableAsInt("net_buffer_length", 16 * 1024); + } + + checkTransactionIsolationLevel(); + + if (!versionMeetsMinimum(4, 1, 0)) { + checkServerEncoding(); + } + + this.io.checkForCharsetMismatch(); + + if (this.serverVariables.containsKey("sql_mode")) { + String sqlModeAsString = this.serverVariables.get("sql_mode"); + if (StringUtils.isStrictlyNumeric(sqlModeAsString)) { + // Old MySQL servers used to have sql_mode as a numeric value. + this.useAnsiQuotes = (Integer.parseInt(sqlModeAsString) & 4) > 0; + } else if (sqlModeAsString != null) { + this.useAnsiQuotes = sqlModeAsString.indexOf("ANSI_QUOTES") != -1; + this.noBackslashEscapes = sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1; + } + } + } + + boolean overrideDefaultAutocommit = isAutoCommitNonDefaultOnServer(); + + configureClientCharacterSet(false); + + try { + this.errorMessageEncoding = CharsetMapping.getCharacterEncodingForErrorMessages(this); + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + if (versionMeetsMinimum(3, 23, 15)) { + this.transactionsSupported = true; + + if (!overrideDefaultAutocommit) { + try { + setAutoCommit(true); // to override anything the server is set to...reqd by JDBC spec. + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + } + } else { + this.transactionsSupported = false; + } + + if (versionMeetsMinimum(3, 23, 36)) { + this.hasIsolationLevels = true; + } else { + this.hasIsolationLevels = false; + } + + this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6); + + this.io.resetMaxBuf(); + + // + // If we're using MySQL 4.1.0 or newer, we need to figure out what character set metadata will be returned in, and then map that to a Java encoding name + // + // We've already set it, and it might be different than what was originally on the server, which is why we use the "special" key to retrieve it + if (this.io.versionMeetsMinimum(4, 1, 0)) { + String characterSetResultsOnServerMysql = this.serverVariables.get(JDBC_LOCAL_CHARACTER_SET_RESULTS); + + if (characterSetResultsOnServerMysql == null || StringUtils.startsWithIgnoreCaseAndWs(characterSetResultsOnServerMysql, "NULL") + || characterSetResultsOnServerMysql.length() == 0) { + String defaultMetadataCharsetMysql = this.serverVariables.get("character_set_system"); + String defaultMetadataCharset = null; + + if (defaultMetadataCharsetMysql != null) { + defaultMetadataCharset = CharsetMapping.getJavaEncodingForMysqlCharset(defaultMetadataCharsetMysql); + } else { + defaultMetadataCharset = "UTF-8"; + } + + this.characterSetMetadata = defaultMetadataCharset; + } else { + this.characterSetResultsOnServer = CharsetMapping.getJavaEncodingForMysqlCharset(characterSetResultsOnServerMysql); + this.characterSetMetadata = this.characterSetResultsOnServer; + } + } else { + this.characterSetMetadata = getEncoding(); + } + + // + // Query cache is broken wrt. multi-statements before MySQL-4.1.10 + // + + if (versionMeetsMinimum(4, 1, 0) && !this.versionMeetsMinimum(4, 1, 10) && getAllowMultiQueries()) { + if (isQueryCacheEnabled()) { + setAllowMultiQueries(false); + } + } + + if (versionMeetsMinimum(5, 0, 0) && (getUseLocalTransactionState() || getElideSetAutoCommits()) && isQueryCacheEnabled() + && !versionMeetsMinimum(5, 1, 32)) { + // Can't trust the server status flag on the wire if query cache is enabled, due to Bug#36326 + setUseLocalTransactionState(false); + setElideSetAutoCommits(false); + } + + // + // Server can do this more efficiently for us + // + + setupServerForTruncationChecks(); + } + + private boolean isQueryCacheEnabled() { + return "ON".equalsIgnoreCase(this.serverVariables.get("query_cache_type")) && !"0".equalsIgnoreCase(this.serverVariables.get("query_cache_size")); + } + + private int getServerVariableAsInt(String variableName, int fallbackValue) throws SQLException { + try { + return Integer.parseInt(this.serverVariables.get(variableName)); + } catch (NumberFormatException nfe) { + getLog().logWarn(Messages.getString("Connection.BadValueInServerVariables", + new Object[] { variableName, this.serverVariables.get(variableName), Integer.valueOf(fallbackValue) })); + + return fallbackValue; + } + } + + /** + * Has the default autocommit value of 0 been changed on the server + * via init_connect? + * + * @return true if autocommit is not the default of '0' on the server. + * + * @throws SQLException + */ + private boolean isAutoCommitNonDefaultOnServer() throws SQLException { + boolean overrideDefaultAutocommit = false; + + String initConnectValue = this.serverVariables.get("init_connect"); + + if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null && initConnectValue.length() > 0) { + if (!getElideSetAutoCommits()) { + // auto-commit might have changed + java.sql.ResultSet rs = null; + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + rs = stmt.executeQuery("SELECT @@session.autocommit"); + + if (rs.next()) { + this.autoCommit = rs.getBoolean(1); + if (this.autoCommit != true) { + overrideDefaultAutocommit = true; + } + } + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + } + } + } else { + if (this.getIO().isSetNeededForAutoCommitMode(true)) { + // we're not in standard autocommit=true mode + this.autoCommit = false; + overrideDefaultAutocommit = true; + } + } + } + + return overrideDefaultAutocommit; + } + + public boolean isClientTzUTC() { + return this.isClientTzUTC; + } + + public boolean isClosed() { + return this.isClosed; + } + + public boolean isCursorFetchEnabled() throws SQLException { + return (versionMeetsMinimum(5, 0, 2) && getUseCursorFetch()); + } + + public boolean isInGlobalTx() { + return this.isInGlobalTx; + } + + /** + * Is this connection connected to the first host in the list if + * there is a list of servers in the URL? + * + * @return true if this connection is connected to the first in + * the list. + */ + public boolean isMasterConnection() { + return false; // handled higher up + } + + /** + * Is the server in a sql_mode that doesn't allow us to use \\ to escape + * things? + * + * @return Returns the noBackslashEscapes. + */ + public boolean isNoBackslashEscapesSet() { + return this.noBackslashEscapes; + } + + public boolean isReadInfoMsgEnabled() { + return this.readInfoMsg; + } + + /** + * Tests to see if the connection is in Read Only Mode. Note that prior to 5.6, + * we cannot really put the database in read only mode, but we pretend we can by + * returning the value of the readOnly flag + * + * @return true if the connection is read only + * @exception SQLException + * if a database access error occurs + */ + public boolean isReadOnly() throws SQLException { + return isReadOnly(true); + } + + /** + * Tests to see if the connection is in Read Only Mode. Note that prior to 5.6, + * we cannot really put the database in read only mode, but we pretend we can by + * returning the value of the readOnly flag + * + * @param useSessionStatus + * in some cases, for example when restoring connection with autoReconnect=true, + * we can rely only on saved readOnly state, so use useSessionStatus=false in that case + * + * @return true if the connection is read only + * @exception SQLException + * if a database access error occurs + */ + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + if (useSessionStatus && !this.isClosed && versionMeetsMinimum(5, 6, 5) && !getUseLocalSessionState() && getReadOnlyPropagatesToServer()) { + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + try { + stmt = getMetadataSafeStatement(); + + rs = stmt.executeQuery("select @@session.tx_read_only"); + if (rs.next()) { + return rs.getInt(1) != 0; // mysql has a habit of tri+ state booleans + } + } catch (SQLException ex1) { + if (ex1.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw SQLError.createSQLException("Could not retrieve transation read-only status server", SQLError.SQL_STATE_GENERAL_ERROR, ex1, + getExceptionInterceptor()); + } + } + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + // ignore + } + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ex) { + // ignore + } + + stmt = null; + } + } + } + + return this.readOnly; + } + + public boolean isRunningOnJDK13() { + return this.isRunningOnJDK13; + } + + public boolean isSameResource(Connection otherConnection) { + synchronized (getConnectionMutex()) { + if (otherConnection == null) { + return false; + } + + boolean directCompare = true; + + String otherHost = ((ConnectionImpl) otherConnection).origHostToConnectTo; + String otherOrigDatabase = ((ConnectionImpl) otherConnection).origDatabaseToConnectTo; + String otherCurrentCatalog = ((ConnectionImpl) otherConnection).database; + + if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) { + directCompare = false; + } else if (otherHost != null && otherHost.indexOf(',') == -1 && otherHost.indexOf(':') == -1) { + // need to check port numbers + directCompare = (((ConnectionImpl) otherConnection).origPortToConnectTo == this.origPortToConnectTo); + } + + if (directCompare) { + if (!nullSafeCompare(otherOrigDatabase, this.origDatabaseToConnectTo) || !nullSafeCompare(otherCurrentCatalog, this.database)) { + directCompare = false; + } + } + + if (directCompare) { + return true; + } + + // Has the user explicitly set a resourceId? + String otherResourceId = ((ConnectionImpl) otherConnection).getResourceId(); + String myResourceId = getResourceId(); + + if (otherResourceId != null || myResourceId != null) { + directCompare = nullSafeCompare(otherResourceId, myResourceId); + + if (directCompare) { + return true; + } + } + + return false; + } + } + + public boolean isServerTzUTC() { + return this.isServerTzUTC; + } + + private void createConfigCacheIfNeeded() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.serverConfigCache != null) { + return; + } + + try { + Class factoryClass; + + factoryClass = Class.forName(getServerConfigCacheFactory()); + + @SuppressWarnings("unchecked") + CacheAdapterFactory> cacheFactory = ((CacheAdapterFactory>) factoryClass.newInstance()); + + this.serverConfigCache = cacheFactory.getInstance(this, this.myURL, Integer.MAX_VALUE, Integer.MAX_VALUE, this.props); + + ExceptionInterceptor evictOnCommsError = new ExceptionInterceptor() { + + public void init(Connection conn, Properties config) throws SQLException { + } + + public void destroy() { + } + + @SuppressWarnings("synthetic-access") + public SQLException interceptException(SQLException sqlEx, Connection conn) { + if (sqlEx.getSQLState() != null && sqlEx.getSQLState().startsWith("08")) { + ConnectionImpl.this.serverConfigCache.invalidate(getURL()); + } + return null; + } + }; + + if (this.exceptionInterceptor == null) { + this.exceptionInterceptor = evictOnCommsError; + } else { + ((ExceptionInterceptorChain) this.exceptionInterceptor).addRingZero(evictOnCommsError); + } + } catch (ClassNotFoundException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantFindCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } catch (InstantiationException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantLoadCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } catch (IllegalAccessException e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.CantLoadCacheFactory", new Object[] { getParseInfoCacheFactory(), "parseInfoCacheFactory" }), + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + } + } + + private final static String SERVER_VERSION_STRING_VAR_NAME = "server_version_string"; + + /** + * Loads the result of 'SHOW VARIABLES' into the serverVariables field so + * that the driver can configure itself. + * + * @throws SQLException + * if the 'SHOW VARIABLES' query fails for any reason. + */ + private void loadServerVariables() throws SQLException { + + if (getCacheServerConfiguration()) { + createConfigCacheIfNeeded(); + + Map cachedVariableMap = this.serverConfigCache.get(getURL()); + + if (cachedVariableMap != null) { + String cachedServerVersion = cachedVariableMap.get(SERVER_VERSION_STRING_VAR_NAME); + + if (cachedServerVersion != null && this.io.getServerVersion() != null && cachedServerVersion.equals(this.io.getServerVersion())) { + this.serverVariables = cachedVariableMap; + + return; + } + + this.serverConfigCache.invalidate(getURL()); + } + } + + java.sql.Statement stmt = null; + java.sql.ResultSet results = null; + + try { + stmt = getMetadataSafeStatement(); + + String version = this.dbmd.getDriverVersion(); + + if (version != null && version.indexOf('*') != -1) { + StringBuilder buf = new StringBuilder(version.length() + 10); + + for (int i = 0; i < version.length(); i++) { + char c = version.charAt(i); + + if (c == '*') { + buf.append("[star]"); + } else { + buf.append(c); + } + } + + version = buf.toString(); + } + + String versionComment = (this.getParanoid() || version == null) ? "" : "/* " + version + " */"; + + this.serverVariables = new HashMap(); + + try { + if (versionMeetsMinimum(5, 1, 0)) { + StringBuilder queryBuf = new StringBuilder(versionComment).append("SELECT"); + queryBuf.append(" @@session.auto_increment_increment AS auto_increment_increment"); + queryBuf.append(", @@character_set_client AS character_set_client"); + queryBuf.append(", @@character_set_connection AS character_set_connection"); + queryBuf.append(", @@character_set_results AS character_set_results"); + queryBuf.append(", @@character_set_server AS character_set_server"); + queryBuf.append(", @@init_connect AS init_connect"); + queryBuf.append(", @@interactive_timeout AS interactive_timeout"); + if (!versionMeetsMinimum(5, 5, 0)) { + queryBuf.append(", @@language AS language"); + } + queryBuf.append(", @@license AS license"); + queryBuf.append(", @@lower_case_table_names AS lower_case_table_names"); + queryBuf.append(", @@max_allowed_packet AS max_allowed_packet"); + queryBuf.append(", @@net_buffer_length AS net_buffer_length"); + queryBuf.append(", @@net_write_timeout AS net_write_timeout"); + queryBuf.append(", @@query_cache_size AS query_cache_size"); + queryBuf.append(", @@query_cache_type AS query_cache_type"); + queryBuf.append(", @@sql_mode AS sql_mode"); + queryBuf.append(", @@system_time_zone AS system_time_zone"); + queryBuf.append(", @@time_zone AS time_zone"); + queryBuf.append(", @@tx_isolation AS tx_isolation"); + queryBuf.append(", @@wait_timeout AS wait_timeout"); + + results = stmt.executeQuery(queryBuf.toString()); + if (results.next()) { + ResultSetMetaData rsmd = results.getMetaData(); + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + this.serverVariables.put(rsmd.getColumnLabel(i), results.getString(i)); + } + } + } else { + results = stmt.executeQuery(versionComment + "SHOW VARIABLES"); + while (results.next()) { + this.serverVariables.put(results.getString(1), results.getString(2)); + } + } + + results.close(); + results = null; + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || getDisconnectOnExpiredPasswords()) { + throw ex; + } + } + + if (getCacheServerConfiguration()) { + this.serverVariables.put(SERVER_VERSION_STRING_VAR_NAME, this.io.getServerVersion()); + + this.serverConfigCache.put(getURL(), this.serverVariables); + + } + } catch (SQLException e) { + throw e; + } finally { + if (results != null) { + try { + results.close(); + } catch (SQLException sqlE) { + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlE) { + } + } + } + } + + private int autoIncrementIncrement = 0; + + public int getAutoIncrementIncrement() { + return this.autoIncrementIncrement; + } + + /** + * Is the server configured to use lower-case table names only? + * + * @return true if lower_case_table_names is 'on' + */ + public boolean lowerCaseTableNames() { + return this.lowerCaseTableNames; + } + + /** + * A driver may convert the JDBC sql grammar into its system's native SQL + * grammar prior to sending it; nativeSQL returns the native form of the + * statement that the driver would have sent. + * + * @param sql + * a SQL statement that may contain one or more '?' parameter + * placeholders + * @return the native form of this statement + * @exception SQLException + * if a database access error occurs + */ + public String nativeSQL(String sql) throws SQLException { + if (sql == null) { + return null; + } + + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, serverSupportsConvertFn(), getMultiHostSafeProxy()); + + if (escapedSqlResult instanceof String) { + return (String) escapedSqlResult; + } + + return ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + + private CallableStatement parseCallableStatement(String sql) throws SQLException { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, serverSupportsConvertFn(), getMultiHostSafeProxy()); + + boolean isFunctionCall = false; + String parsedSql = null; + + if (escapedSqlResult instanceof EscapeProcessorResult) { + parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql; + isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction; + } else { + parsedSql = (String) escapedSqlResult; + isFunctionCall = false; + } + + return CallableStatement.getInstance(getMultiHostSafeProxy(), parsedSql, this.database, isFunctionCall); + } + + public boolean parserKnowsUnicode() { + return this.parserKnowsUnicode; + } + + /** + * Detect if the connection is still good + * + * @throws SQLException + * if the ping fails + */ + public void ping() throws SQLException { + pingInternal(true, 0); + } + + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + if (checkForClosedConnection) { + checkClosed(); + } + + long pingMillisLifetime = getSelfDestructOnPingSecondsLifetime(); + int pingMaxOperations = getSelfDestructOnPingMaxOperations(); + + if ((pingMillisLifetime > 0 && (System.currentTimeMillis() - this.connectionCreationTimeMillis) > pingMillisLifetime) + || (pingMaxOperations > 0 && pingMaxOperations <= this.io.getCommandCount())) { + + close(); + + throw SQLError.createSQLException(Messages.getString("Connection.exceededConnectionLifetime"), SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE, + getExceptionInterceptor()); + } + // Need MySQL-3.22.1, but who uses anything older!? + this.io.sendCommand(MysqlDefs.PING, null, null, false, null, timeoutMillis); + } + + /** + * @param sql + * @throws SQLException + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException { + + return prepareCall(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * JDBC 2.0 Same as prepareCall() above, but allows the default result set + * type and result set concurrency type to be overridden. + * + * @param sql + * the SQL representing the callable statement + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new CallableStatement object containing the pre-compiled SQL + * statement + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + if (versionMeetsMinimum(5, 0, 0)) { + CallableStatement cStmt = null; + + if (!getCacheCallableStatements()) { + + cStmt = parseCallableStatement(sql); + } else { + synchronized (this.parsedCallableStatementCache) { + CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql); + + CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache + .get(key); + + if (cachedParamInfo != null) { + cStmt = CallableStatement.getInstance(getMultiHostSafeProxy(), cachedParamInfo); + } else { + cStmt = parseCallableStatement(sql); + + synchronized (cStmt) { + cachedParamInfo = cStmt.paramInfo; + } + + this.parsedCallableStatementCache.put(key, cachedParamInfo); + } + } + } + + cStmt.setResultSetType(resultSetType); + cStmt.setResultSetConcurrency(resultSetConcurrency); + + return cStmt; + } + + throw SQLError.createSQLException("Callable statements not supported.", SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + + /** + * @see Connection#prepareCall(String, int, int, int) + */ + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (getPedantic()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + CallableStatement cStmt = (com.mysql.jdbc.CallableStatement) prepareCall(sql, resultSetType, resultSetConcurrency); + + return cStmt; + } + + /** + * A SQL statement with or without IN parameters can be pre-compiled and + * stored in a PreparedStatement object. This object can then be used to + * efficiently execute this statement multiple times. + *

    + * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation if the driver supports precompilation. In + * this case, the statement is not sent to the database until the PreparedStatement is executed. This has no direct effect on users; however it does affect + * which method throws certain java.sql.SQLExceptions + *

    + *

    + * MySQL does not support precompilation of statements, so they are handled by the driver. + *

    + * + * @param sql + * a SQL statement that may contain one or more '?' IN parameter + * placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException + * if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { + return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * @see Connection#prepareStatement(String, int) + */ + public java.sql.PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((com.mysql.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * JDBC 2.0 Same as prepareStatement() above, but allows the default result + * set type and result set concurrency type to be overridden. + * + * @param sql + * the SQL query containing place holders + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new PreparedStatement object containing the pre-compiled SQL + * statement + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + // + // FIXME: Create warnings if can't create results of the given type or concurrency + // + PreparedStatement pStmt = null; + + boolean canServerPrepare = true; + + String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql; + + if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) { + canServerPrepare = canHandleAsServerPreparedStatement(nativeSql); + } + + if (this.useServerPreparedStmts && canServerPrepare) { + if (this.getCachePreparedStatements()) { + synchronized (this.serverSideStatementCache) { + pStmt = (com.mysql.jdbc.ServerPreparedStatement) this.serverSideStatementCache.remove(sql); + + if (pStmt != null) { + ((com.mysql.jdbc.ServerPreparedStatement) pStmt).setClosed(false); + pStmt.clearParameters(); + } + + if (pStmt == null) { + try { + pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, + resultSetConcurrency); + if (sql.length() < getPreparedStatementCacheSqlLimit()) { + ((com.mysql.jdbc.ServerPreparedStatement) pStmt).isCached = true; + } + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + } catch (SQLException sqlEx) { + // Punt, if necessary + if (getEmulateUnsupportedPstmts()) { + pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + + if (sql.length() < getPreparedStatementCacheSqlLimit()) { + this.serverSideStatementCheckCache.put(sql, Boolean.FALSE); + } + } else { + throw sqlEx; + } + } + } + } + } else { + try { + pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, resultSetConcurrency); + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + } catch (SQLException sqlEx) { + // Punt, if necessary + if (getEmulateUnsupportedPstmts()) { + pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + } else { + throw sqlEx; + } + } + } + } else { + pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + } + + return pStmt; + } + } + + /** + * @see Connection#prepareStatement(String, int, int, int) + */ + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (getPedantic()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + return prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + /** + * @see Connection#prepareStatement(String, int[]) + */ + public java.sql.PreparedStatement prepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((com.mysql.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + /** + * @see Connection#prepareStatement(String, String[]) + */ + public java.sql.PreparedStatement prepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((com.mysql.jdbc.PreparedStatement) pStmt).setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + /** + * Closes connection and frees resources. + * + * @param calledExplicitly + * is this being called from close() + * @param issueRollback + * should a rollback() be issued? + * @throws SQLException + * if an error occurs + */ + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + SQLException sqlEx = null; + + if (this.isClosed()) { + return; + } + + this.forceClosedReason = reason; + + try { + if (!skipLocalTeardown) { + if (!getAutoCommit() && issueRollback) { + try { + rollback(); + } catch (SQLException ex) { + sqlEx = ex; + } + } + + reportMetrics(); + + if (getUseUsageAdvisor()) { + if (!calledExplicitly) { + String message = "Connection implicitly closed by Driver. You should call Connection.close() from your code to free resources more efficiently and avoid resource leaks."; + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", this.getCatalog(), this.getId(), -1, -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + + long connectionLifeTime = System.currentTimeMillis() - this.connectionCreationTimeMillis; + + if (connectionLifeTime < 500) { + String message = "Connection lifetime of < .5 seconds. You might be un-necessarily creating short-lived connections and should investigate connection pooling to be more efficient."; + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", this.getCatalog(), this.getId(), -1, -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + try { + closeAllOpenStatements(); + } catch (SQLException ex) { + sqlEx = ex; + } + + if (this.io != null) { + try { + this.io.quit(); + } catch (Exception e) { + } + + } + } else { + this.io.forceClose(); + } + + if (this.statementInterceptors != null) { + for (int i = 0; i < this.statementInterceptors.size(); i++) { + this.statementInterceptors.get(i).destroy(); + } + } + + if (this.exceptionInterceptor != null) { + this.exceptionInterceptor.destroy(); + } + } finally { + this.openStatements.clear(); + if (this.io != null) { + this.io.releaseResources(); + this.io = null; + } + this.statementInterceptors = null; + this.exceptionInterceptor = null; + ProfilerEventHandlerFactory.removeInstance(this); + + synchronized (getConnectionMutex()) { + if (this.cancelTimer != null) { + this.cancelTimer.cancel(); + } + } + + this.isClosed = true; + } + + if (sqlEx != null) { + throw sqlEx; + } + + } + + public void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + synchronized (getConnectionMutex()) { + if (getCachePreparedStatements() && pstmt.isPoolable()) { + synchronized (this.serverSideStatementCache) { + this.serverSideStatementCache.put(pstmt.originalSql, pstmt); + } + } + } + } + + public void decachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + synchronized (getConnectionMutex()) { + if (getCachePreparedStatements() && pstmt.isPoolable()) { + synchronized (this.serverSideStatementCache) { + this.serverSideStatementCache.remove(pstmt.originalSql); + } + } + } + } + + /** + * @param queryTimeMs + */ + public void registerQueryExecutionTime(long queryTimeMs) { + if (queryTimeMs > this.longestQueryTimeMs) { + this.longestQueryTimeMs = queryTimeMs; + + repartitionPerformanceHistogram(); + } + + addToPerformanceHistogram(queryTimeMs, 1); + + if (queryTimeMs < this.shortestQueryTimeMs) { + this.shortestQueryTimeMs = (queryTimeMs == 0) ? 1 : queryTimeMs; + } + + this.numberOfQueriesIssued++; + + this.totalQueryTimeMs += queryTimeMs; + } + + /** + * Register a Statement instance as open. + * + * @param stmt + * the Statement instance to remove + */ + public void registerStatement(Statement stmt) { + this.openStatements.addIfAbsent(stmt); + } + + /** + * @see Connection#releaseSavepoint(Savepoint) + */ + public void releaseSavepoint(Savepoint arg0) throws SQLException { + // this is a no-op + } + + private void repartitionHistogram(int[] histCounts, long[] histBreakpoints, long currentLowerBound, long currentUpperBound) { + + if (this.oldHistCounts == null) { + this.oldHistCounts = new int[histCounts.length]; + this.oldHistBreakpoints = new long[histBreakpoints.length]; + } + + System.arraycopy(histCounts, 0, this.oldHistCounts, 0, histCounts.length); + + System.arraycopy(histBreakpoints, 0, this.oldHistBreakpoints, 0, histBreakpoints.length); + + createInitialHistogram(histBreakpoints, currentLowerBound, currentUpperBound); + + for (int i = 0; i < HISTOGRAM_BUCKETS; i++) { + addToHistogram(histCounts, histBreakpoints, this.oldHistBreakpoints[i], this.oldHistCounts[i], currentLowerBound, currentUpperBound); + } + } + + private void repartitionPerformanceHistogram() { + checkAndCreatePerformanceHistogram(); + + repartitionHistogram(this.perfMetricsHistCounts, this.perfMetricsHistBreakpoints, + this.shortestQueryTimeMs == Long.MAX_VALUE ? 0 : this.shortestQueryTimeMs, this.longestQueryTimeMs); + } + + private void repartitionTablesAccessedHistogram() { + checkAndCreateTablesAccessedHistogram(); + + repartitionHistogram(this.numTablesMetricsHistCounts, this.numTablesMetricsHistBreakpoints, + this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0 : this.minimumNumberTablesAccessed, this.maximumNumberTablesAccessed); + } + + private void reportMetrics() { + if (getGatherPerformanceMetrics()) { + StringBuilder logMessage = new StringBuilder(256); + + logMessage.append("** Performance Metrics Report **\n"); + logMessage.append("\nLongest reported query: " + this.longestQueryTimeMs + " ms"); + logMessage.append("\nShortest reported query: " + this.shortestQueryTimeMs + " ms"); + logMessage.append("\nAverage query execution time: " + (this.totalQueryTimeMs / this.numberOfQueriesIssued) + " ms"); + logMessage.append("\nNumber of statements executed: " + this.numberOfQueriesIssued); + logMessage.append("\nNumber of result sets created: " + this.numberOfResultSetsCreated); + logMessage.append("\nNumber of statements prepared: " + this.numberOfPrepares); + logMessage.append("\nNumber of prepared statement executions: " + this.numberOfPreparedExecutes); + + if (this.perfMetricsHistBreakpoints != null) { + logMessage.append("\n\n\tTiming Histogram:\n"); + int maxNumPoints = 20; + int highestCount = Integer.MIN_VALUE; + + for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) { + if (this.perfMetricsHistCounts[i] > highestCount) { + highestCount = this.perfMetricsHistCounts[i]; + } + } + + if (highestCount == 0) { + highestCount = 1; // avoid DIV/0 + } + + for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) { + + if (i == 0) { + logMessage.append("\n\tless than " + this.perfMetricsHistBreakpoints[i + 1] + " ms: \t" + this.perfMetricsHistCounts[i]); + } else { + logMessage.append("\n\tbetween " + this.perfMetricsHistBreakpoints[i] + " and " + this.perfMetricsHistBreakpoints[i + 1] + " ms: \t" + + this.perfMetricsHistCounts[i]); + } + + logMessage.append("\t"); + + int numPointsToGraph = (int) (maxNumPoints * ((double) this.perfMetricsHistCounts[i] / (double) highestCount)); + + for (int j = 0; j < numPointsToGraph; j++) { + logMessage.append("*"); + } + + if (this.longestQueryTimeMs < this.perfMetricsHistCounts[i + 1]) { + break; + } + } + + if (this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.longestQueryTimeMs) { + logMessage.append("\n\tbetween "); + logMessage.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]); + logMessage.append(" and "); + logMessage.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]); + logMessage.append(" ms: \t"); + logMessage.append(this.perfMetricsHistCounts[HISTOGRAM_BUCKETS - 1]); + } + } + + if (this.numTablesMetricsHistBreakpoints != null) { + logMessage.append("\n\n\tTable Join Histogram:\n"); + int maxNumPoints = 20; + int highestCount = Integer.MIN_VALUE; + + for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) { + if (this.numTablesMetricsHistCounts[i] > highestCount) { + highestCount = this.numTablesMetricsHistCounts[i]; + } + } + + if (highestCount == 0) { + highestCount = 1; // avoid DIV/0 + } + + for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) { + + if (i == 0) { + logMessage.append("\n\t" + this.numTablesMetricsHistBreakpoints[i + 1] + " tables or less: \t\t" + this.numTablesMetricsHistCounts[i]); + } else { + logMessage.append("\n\tbetween " + this.numTablesMetricsHistBreakpoints[i] + " and " + this.numTablesMetricsHistBreakpoints[i + 1] + + " tables: \t" + this.numTablesMetricsHistCounts[i]); + } + + logMessage.append("\t"); + + int numPointsToGraph = (int) (maxNumPoints * ((double) this.numTablesMetricsHistCounts[i] / (double) highestCount)); + + for (int j = 0; j < numPointsToGraph; j++) { + logMessage.append("*"); + } + + if (this.maximumNumberTablesAccessed < this.numTablesMetricsHistBreakpoints[i + 1]) { + break; + } + } + + if (this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.maximumNumberTablesAccessed) { + logMessage.append("\n\tbetween "); + logMessage.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]); + logMessage.append(" and "); + logMessage.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]); + logMessage.append(" tables: "); + logMessage.append(this.numTablesMetricsHistCounts[HISTOGRAM_BUCKETS - 1]); + } + } + + this.log.logInfo(logMessage); + + this.metricsLastReportedMs = System.currentTimeMillis(); + } + } + + /** + * Reports currently collected metrics if this feature is enabled and the + * timeout has passed. + */ + protected void reportMetricsIfNeeded() { + if (getGatherPerformanceMetrics()) { + if ((System.currentTimeMillis() - this.metricsLastReportedMs) > getReportMetricsIntervalMillis()) { + reportMetrics(); + } + } + } + + public void reportNumberOfTablesAccessed(int numTablesAccessed) { + if (numTablesAccessed < this.minimumNumberTablesAccessed) { + this.minimumNumberTablesAccessed = numTablesAccessed; + } + + if (numTablesAccessed > this.maximumNumberTablesAccessed) { + this.maximumNumberTablesAccessed = numTablesAccessed; + + repartitionTablesAccessedHistogram(); + } + + addToTablesAccessedHistogram(numTablesAccessed, 1); + } + + /** + * Resets the server-side state of this connection. Doesn't work for MySQL + * versions older than 4.0.6 or if isParanoid() is set (it will become a + * no-op in these cases). Usually only used from connection pooling code. + * + * @throws SQLException + * if the operation fails while resetting server state. + */ + public void resetServerState() throws SQLException { + if (!getParanoid() && ((this.io != null) && versionMeetsMinimum(4, 0, 6))) { + changeUser(this.user, this.password); + } + } + + /** + * The method rollback() drops all changes made since the previous + * commit/rollback and releases any database locks currently held by the + * Connection. + * + * @exception SQLException + * if a database access error occurs + * @see commit + */ + public void rollback() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + if (!((ConnectionLifecycleInterceptor) each).rollback()) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + // no-op if _relaxAutoCommit == true + if (this.autoCommit && !getRelaxAutoCommit()) { + throw SQLError.createSQLException("Can't call rollback when autocommit=true", SQLError.SQL_STATE_CONNECTION_NOT_OPEN, + getExceptionInterceptor()); + } else if (this.transactionsSupported) { + try { + rollbackNoChecks(); + } catch (SQLException sqlEx) { + // We ignore non-transactional tables if told to do so + if (getIgnoreNonTxTables() && (sqlEx.getErrorCode() == SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) { + return; + } + throw sqlEx; + + } + } + } catch (SQLException sqlException) { + if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) { + throw SQLError.createSQLException("Communications link failure during rollback(). Transaction resolution unknown.", + SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor()); + } + + throw sqlException; + } finally { + this.needsPing = this.getReconnectAtTxEnd(); + } + } + } + + /** + * @see Connection#rollback(Savepoint) + */ + public void rollback(final Savepoint savepoint) throws SQLException { + + synchronized (getConnectionMutex()) { + if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + if (!((ConnectionLifecycleInterceptor) each).rollback(savepoint)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + StringBuilder rollbackQuery = new StringBuilder("ROLLBACK TO SAVEPOINT "); + rollbackQuery.append('`'); + rollbackQuery.append(savepoint.getSavepointName()); + rollbackQuery.append('`'); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + stmt.executeUpdate(rollbackQuery.toString()); + } catch (SQLException sqlEx) { + int errno = sqlEx.getErrorCode(); + + if (errno == 1181) { + String msg = sqlEx.getMessage(); + + if (msg != null) { + int indexOfError153 = msg.indexOf("153"); + + if (indexOfError153 != -1) { + throw SQLError.createSQLException("Savepoint '" + savepoint.getSavepointName() + "' does not exist", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, errno, getExceptionInterceptor()); + } + } + } + + // We ignore non-transactional tables if told to do so + if (getIgnoreNonTxTables() && (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) { + throw sqlEx; + } + + if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + throw SQLError.createSQLException("Communications link failure during rollback(). Transaction resolution unknown.", + SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, getExceptionInterceptor()); + } + + throw sqlEx; + } finally { + closeStatement(stmt); + } + } finally { + this.needsPing = this.getReconnectAtTxEnd(); + } + } else { + throw SQLError.createSQLFeatureNotSupportedException(); + } + } + } + + private void rollbackNoChecks() throws SQLException { + if (getUseLocalTransactionState() && versionMeetsMinimum(5, 0, 0)) { + if (!this.io.inTransactionOnServer()) { + return; // effectively a no-op + } + } + + execSQL(null, "rollback", -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } + + /** + * @see java.sql.Connection#prepareStatement(String) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException { + + String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql; + + return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * @see java.sql.Connection#prepareStatement(String, int) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql; + + PreparedStatement pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY); + + pStmt.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * @see java.sql.Connection#prepareStatement(String, int, int) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql; + + return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), resultSetType, resultSetConcurrency); + } + + /** + * @see java.sql.Connection#prepareStatement(String, int, int, int) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + if (getPedantic()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + return serverPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + /** + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + + PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + /** + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + public boolean serverSupportsConvertFn() throws SQLException { + return versionMeetsMinimum(4, 0, 2); + } + + /** + * If a connection is in auto-commit mode, than all its SQL statements will + * be executed and committed as individual transactions. Otherwise, its SQL + * statements are grouped into transactions that are terminated by either + * commit() or rollback(). By default, new connections are in auto- commit + * mode. The commit occurs when the statement completes or the next execute + * occurs, whichever comes first. In the case of statements returning a + * ResultSet, the statement completes when the last row of the ResultSet has + * been retrieved or the ResultSet has been closed. In advanced cases, a + * single statement may return multiple results as well as output parameter + * values. Here the commit occurs when all results and output param values + * have been retrieved. + *

    + * Note: MySQL does not support transactions, so this method is a no-op. + *

    + * + * @param autoCommitFlag + * - + * true enables auto-commit; false disables it + * @exception SQLException + * if a database access error occurs + */ + public void setAutoCommit(final boolean autoCommitFlag) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + if (!((ConnectionLifecycleInterceptor) each).setAutoCommit(autoCommitFlag)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + if (getAutoReconnectForPools()) { + setHighAvailability(true); + } + + try { + if (this.transactionsSupported) { + + boolean needsSetOnServer = true; + + if (this.getUseLocalSessionState() && this.autoCommit == autoCommitFlag) { + needsSetOnServer = false; + } else if (!this.getHighAvailability()) { + needsSetOnServer = this.getIO().isSetNeededForAutoCommitMode(autoCommitFlag); + } + + // this internal value must be set first as failover depends on it being set to true to fail over (which is done by most app servers and + // connection pools at the end of a transaction), and the driver issues an implicit set based on this value when it (re)-connects to a + // server so the value holds across connections + this.autoCommit = autoCommitFlag; + + if (needsSetOnServer) { + execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } + + } else { + if ((autoCommitFlag == false) && !getRelaxAutoCommit()) { + throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 do not support transactions", + SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor()); + } + + this.autoCommit = autoCommitFlag; + } + } finally { + if (this.getAutoReconnectForPools()) { + setHighAvailability(false); + } + } + + return; + } + } + + /** + * A sub-space of this Connection's database may be selected by setting a + * catalog name. If the driver does not support catalogs, it will silently + * ignore this request + *

    + * Note: MySQL's notion of catalogs are individual databases. + *

    + * + * @param catalog + * the database for this connection to use + * @throws SQLException + * if a database access error occurs + */ + public void setCatalog(final String catalog) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if (catalog == null) { + throw SQLError.createSQLException("Catalog can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + if (!((ConnectionLifecycleInterceptor) each).setCatalog(catalog)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + if (getUseLocalSessionState()) { + if (this.lowerCaseTableNames) { + if (this.database.equalsIgnoreCase(catalog)) { + return; + } + } else { + if (this.database.equals(catalog)) { + return; + } + } + } + + String quotedId = this.dbmd.getIdentifierQuoteString(); + + if ((quotedId == null) || quotedId.equals(" ")) { + quotedId = ""; + } + + StringBuilder query = new StringBuilder("USE "); + query.append(StringUtils.quoteIdentifier(catalog, quotedId, getPedantic())); + + execSQL(null, query.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + + this.database = catalog; + } + } + + /** + * @param failedOver + * The failedOver to set. + */ + public void setFailedOver(boolean flag) { + // handled higher up + } + + /** + * @see Connection#setHoldability(int) + */ + public void setHoldability(int arg0) throws SQLException { + // do nothing + } + + public void setInGlobalTx(boolean flag) { + this.isInGlobalTx = flag; + } + + // exposed for testing + /** + * @param preferSlaveDuringFailover + * The preferSlaveDuringFailover to set. + */ + @Deprecated + public void setPreferSlaveDuringFailover(boolean flag) { + // no-op, handled further up in the wrapper + } + + public void setReadInfoMsgEnabled(boolean flag) { + this.readInfoMsg = flag; + } + + /** + * You can put a connection in read-only mode as a hint to enable database + * optimizations Note: setReadOnly cannot be called while in the + * middle of a transaction + * + * @param readOnlyFlag + * - + * true enables read-only mode; false disables it + * @exception SQLException + * if a database access error occurs + */ + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + checkClosed(); + + setReadOnlyInternal(readOnlyFlag); + } + + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + // note this this is safe even inside a transaction + if (getReadOnlyPropagatesToServer() && versionMeetsMinimum(5, 6, 5)) { + if (!getUseLocalSessionState() || (readOnlyFlag != this.readOnly)) { + execSQL(null, "set session transaction " + (readOnlyFlag ? "read only" : "read write"), -1, null, DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } + } + + this.readOnly = readOnlyFlag; + } + + /** + * @see Connection#setSavepoint() + */ + public java.sql.Savepoint setSavepoint() throws SQLException { + MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor()); + + setSavepoint(savepoint); + + return savepoint; + } + + private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { + + synchronized (getConnectionMutex()) { + if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) { + checkClosed(); + + StringBuilder savePointQuery = new StringBuilder("SAVEPOINT "); + savePointQuery.append('`'); + savePointQuery.append(savepoint.getSavepointName()); + savePointQuery.append('`'); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + stmt.executeUpdate(savePointQuery.toString()); + } finally { + closeStatement(stmt); + } + } else { + throw SQLError.createSQLFeatureNotSupportedException(); + } + } + } + + /** + * @see Connection#setSavepoint(String) + */ + public java.sql.Savepoint setSavepoint(String name) throws SQLException { + synchronized (getConnectionMutex()) { + MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor()); + + setSavepoint(savepoint); + + return savepoint; + } + } + + private void setSessionVariables() throws SQLException { + if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) { + List variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'", false); + + int numVariablesToSet = variablesToSet.size(); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + for (int i = 0; i < numVariablesToSet; i++) { + String variableValuePair = variablesToSet.get(i); + + if (variableValuePair.startsWith("@")) { + stmt.executeUpdate("SET " + variableValuePair); + } else { + stmt.executeUpdate("SET SESSION " + variableValuePair); + } + } + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + } + + /** + * @param level + * @throws SQLException + */ + public void setTransactionIsolation(int level) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if (this.hasIsolationLevels) { + String sql = null; + + boolean shouldSendSet = false; + + if (getAlwaysSendSetIsolation()) { + shouldSendSet = true; + } else { + if (level != this.isolationLevel) { + shouldSendSet = true; + } + } + + if (getUseLocalSessionState()) { + shouldSendSet = this.isolationLevel != level; + } + + if (shouldSendSet) { + switch (level) { + case java.sql.Connection.TRANSACTION_NONE: + throw SQLError.createSQLException("Transaction isolation level NONE not supported by MySQL", getExceptionInterceptor()); + + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"; + + break; + + case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; + + break; + + case java.sql.Connection.TRANSACTION_REPEATABLE_READ: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ"; + + break; + + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE"; + + break; + + default: + throw SQLError.createSQLException("Unsupported transaction isolation level '" + level + "'", SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, + getExceptionInterceptor()); + } + + execSQL(null, sql, -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + + this.isolationLevel = level; + } + } else { + throw SQLError.createSQLException("Transaction Isolation Levels are not supported on MySQL versions older than 3.23.36.", + SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + } + } + + /** + * JDBC 2.0 Install a type-map object as the default type-map for this + * connection + * + * @param map + * the type mapping + * @throws SQLException + * if a database error occurs. + */ + public void setTypeMap(java.util.Map> map) throws SQLException { + synchronized (getConnectionMutex()) { + this.typeMap = map; + } + } + + private void setupServerForTruncationChecks() throws SQLException { + if (getJdbcCompliantTruncation()) { + if (versionMeetsMinimum(5, 0, 2)) { + String currentSqlMode = this.serverVariables.get("sql_mode"); + + boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1; + + if (currentSqlMode == null || currentSqlMode.length() == 0 || !strictTransTablesIsSet) { + StringBuilder commandBuf = new StringBuilder("SET sql_mode='"); + + if (currentSqlMode != null && currentSqlMode.length() > 0) { + commandBuf.append(currentSqlMode); + commandBuf.append(","); + } + + commandBuf.append("STRICT_TRANS_TABLES'"); + + execSQL(null, commandBuf.toString(), -1, null, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + + setJdbcCompliantTruncation(false); // server's handling this for us now + } else if (strictTransTablesIsSet) { + // We didn't set it, but someone did, so we piggy back on it + setJdbcCompliantTruncation(false); // server's handling this for us now + } + + } + } + } + + /** + * Used by MiniAdmin to shutdown a MySQL server + * + * @throws SQLException + * if the command can not be issued. + */ + public void shutdownServer() throws SQLException { + try { + this.io.sendCommand(MysqlDefs.SHUTDOWN, null, null, false, null, 0); + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.UnhandledExceptionDuringShutdown"), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + public boolean supportsIsolationLevel() { + return this.hasIsolationLevels; + } + + public boolean supportsQuotedIdentifiers() { + return this.hasQuotedIdentifiers; + } + + public boolean supportsTransactions() { + return this.transactionsSupported; + } + + /** + * Remove the given statement from the list of open statements + * + * @param stmt + * the Statement instance to remove + */ + public void unregisterStatement(Statement stmt) { + this.openStatements.remove(stmt); + } + + public boolean useAnsiQuotedIdentifiers() { + synchronized (getConnectionMutex()) { + return this.useAnsiQuotes; + } + } + + public boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException { + checkClosed(); + + return this.io.versionMeetsMinimum(major, minor, subminor); + } + + /** + * Returns cached metadata (or null if not cached) for the given query, + * which must match _exactly_. + * + * This method is synchronized by the caller on getMutex(), so if + * calling this method from internal code in the driver, make sure it's + * synchronized on the mutex that guards communication with the server. + * + * @param sql + * the query that is the key to the cache + * + * @return metadata cached for the given SQL, or none if it doesn't + * exist. + */ + public CachedResultSetMetaData getCachedMetaData(String sql) { + if (this.resultSetMetadataCache != null) { + synchronized (this.resultSetMetadataCache) { + return (CachedResultSetMetaData) this.resultSetMetadataCache.get(sql); + } + } + + return null; // no cache exists + } + + /** + * Caches CachedResultSetMetaData that has been placed in the cache using + * the given SQL as a key. + * + * This method is synchronized by the caller on getMutex(), so if + * calling this method from internal code in the driver, make sure it's + * synchronized on the mutex that guards communication with the server. + * + * @param sql + * the query that the metadata pertains too. + * @param cachedMetaData + * metadata (if it exists) to populate the cache. + * @param resultSet + * the result set to retreive metadata from, or apply to. + * + * @throws SQLException + */ + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + + if (cachedMetaData == null) { + + // read from results + cachedMetaData = new CachedResultSetMetaData(); + + // assume that users will use named-based lookups + resultSet.buildIndexMapping(); + resultSet.initializeWithMetadata(); + + if (resultSet instanceof UpdatableResultSet) { + ((UpdatableResultSet) resultSet).checkUpdatability(); + } + + resultSet.populateCachedMetaData(cachedMetaData); + + this.resultSetMetadataCache.put(sql, cachedMetaData); + } else { + resultSet.initializeFromCachedMetaData(cachedMetaData); + resultSet.initializeWithMetadata(); + + if (resultSet instanceof UpdatableResultSet) { + ((UpdatableResultSet) resultSet).checkUpdatability(); + } + } + } + + /** + * Returns the comment that will be prepended to all statements + * sent to the server. + * + * @return the comment that will be prepended to all statements + * sent to the server. + */ + public String getStatementComment() { + return this.statementComment; + } + + /** + * Sets the comment that will be prepended to all statements + * sent to the server. Do not use slash-star or star-slash tokens + * in the comment as these will be added by the driver itself. + * + * @param comment + * the comment that will be prepended to all statements + * sent to the server. + */ + public void setStatementComment(String comment) { + this.statementComment = comment; + } + + public void reportQueryTime(long millisOrNanos) { + synchronized (getConnectionMutex()) { + this.queryTimeCount++; + this.queryTimeSum += millisOrNanos; + this.queryTimeSumSquares += (millisOrNanos * millisOrNanos); + this.queryTimeMean = ((this.queryTimeMean * (this.queryTimeCount - 1)) + millisOrNanos) / this.queryTimeCount; + } + } + + public boolean isAbonormallyLongQuery(long millisOrNanos) { + synchronized (getConnectionMutex()) { + if (this.queryTimeCount < 15) { + return false; // need a minimum amount for this to make sense + } + + double stddev = Math.sqrt((this.queryTimeSumSquares - ((this.queryTimeSum * this.queryTimeSum) / this.queryTimeCount)) / (this.queryTimeCount - 1)); + + return millisOrNanos > (this.queryTimeMean + 5 * stddev); + } + } + + public void initializeExtension(Extension ex) throws SQLException { + ex.init(this, this.props); + } + + public void transactionBegun() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + ((ConnectionLifecycleInterceptor) each).transactionBegun(); + } + }; + + iter.doForAll(); + } + } + } + + public void transactionCompleted() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(Extension each) throws SQLException { + ((ConnectionLifecycleInterceptor) each).transactionCompleted(); + } + }; + + iter.doForAll(); + } + } + } + + public boolean storesLowerCaseTableName() { + return this.storesLowerCaseTableName; + } + + private ExceptionInterceptor exceptionInterceptor; + + @Override + public ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + public boolean getRequiresEscapingEncoder() { + return this.requiresEscapingEncoder; + } + + public boolean isServerLocal() throws SQLException { + synchronized (getConnectionMutex()) { + SocketFactory factory = getIO().socketFactory; + + if (factory instanceof SocketMetadata) { + return ((SocketMetadata) factory).isLocallyConnected(this); + } + getLog().logWarn(Messages.getString("Connection.NoMetadataOnSocketFactory")); + return false; + } + } + + /** + * Returns the sql select limit max-rows for this session. + */ + public int getSessionMaxRows() { + synchronized (getConnectionMutex()) { + return this.sessionMaxRows; + } + } + + /** + * Sets the sql select limit max-rows for this session if different from current. + * + * @param max + * the new max-rows value to set. + * @throws SQLException + * if a database error occurs issuing the statement that sets the limit. + */ + public void setSessionMaxRows(int max) throws SQLException { + synchronized (getConnectionMutex()) { + if (this.sessionMaxRows != max) { + this.sessionMaxRows = max; + execSQL(null, "SET SQL_SELECT_LIMIT=" + (this.sessionMaxRows == -1 ? "DEFAULT" : this.sessionMaxRows), -1, null, DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false); + } + } + } + + // JDBC-4.1 + // until we flip catalog/schema, this is a no-op + public void setSchema(String schema) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + } + } + + // JDBC-4.1 + public String getSchema() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + return null; + } + } + + /** + * Terminates an open connection. Calling abort results in: + *
      + *
    • The connection marked as closed + *
    • Closes any physical connection to the database + *
    • Releases resources used by the connection + *
    • Insures that any thread that is currently accessing the connection will either progress to completion or throw an SQLException. + *
    + *

    + * Calling abort marks the connection closed and releases any resources. Calling abort on a closed connection is a no-op. + *

    + * It is possible that the aborting and releasing of the resources that are held by the connection can take an extended period of time. When the + * abort method returns, the connection will have been marked as closed and the Executor that was passed as a parameter to abort + * may still be executing tasks to release resources. + *

    + * This method checks to see that there is an SQLPermission object before allowing the method to proceed. If a SecurityManager + * exists and its checkPermission method denies calling abort, this method throws a java.lang.SecurityException. + * + * @param executor + * The Executor implementation which will + * be used by abort. + * @throws java.sql.SQLException + * if a database access error occurs or + * the {@code executor} is {@code null}, + * @throws java.lang.SecurityException + * if a security manager exists and its checkPermission method denies calling abort + * @see SecurityManager#checkPermission + * @see Executor + * @since 1.7 + */ + public void abort(Executor executor) throws SQLException { + SecurityManager sec = System.getSecurityManager(); + + if (sec != null) { + sec.checkPermission(ABORT_PERM); + } + + if (executor == null) { + throw SQLError.createSQLException("Executor can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + executor.execute(new Runnable() { + + public void run() { + try { + abortInternal(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + }); + } + + // JDBC-4.1 + public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException { + synchronized (getConnectionMutex()) { + SecurityManager sec = System.getSecurityManager(); + + if (sec != null) { + sec.checkPermission(SET_NETWORK_TIMEOUT_PERM); + } + + if (executor == null) { + throw SQLError.createSQLException("Executor can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + checkClosed(); + final MysqlIO mysqlIo = this.io; + + executor.execute(new Runnable() { + + public void run() { + try { + setSocketTimeout(milliseconds); // for re-connects + mysqlIo.setSocketTimeout(milliseconds); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + }); + } + } + + // JDBC-4.1 + public int getNetworkTimeout() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + return getSocketTimeout(); + } + } + + public ProfilerEventHandler getProfilerEventHandlerInstance() { + return this.eventSink; + } + + public void setProfilerEventHandlerInstance(ProfilerEventHandler h) { + this.eventSink = h; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java new file mode 100644 index 0000000..5e99fde --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java @@ -0,0 +1,130 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.Savepoint; + +/** + * Implementors of this interface can be installed via the "connectionLifecycleInterceptors" configuration property and receive events and alter behavior of + * "lifecycle" methods on our connection implementation. + * + * The driver will create one instance of a given interceptor per-connection. + */ +public interface ConnectionLifecycleInterceptor extends Extension { + /** + * Called when an application calls Connection.close(), before the driver + * processes its own internal logic for close. + * + * @throws SQLException + */ + public abstract void close() throws SQLException; + + /** + * Called when an application calls Connection.commit(), before the + * driver processes its own internal logic for commit(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for commit(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for commit(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean commit() throws SQLException; + + /** + * Called when an application calls Connection.rollback(), before the + * driver processes its own internal logic for rollback(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean rollback() throws SQLException; + + /** + * Called when an application calls Connection.rollback(), before the + * driver processes its own internal logic for rollback(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean rollback(Savepoint s) throws SQLException; + + /** + * Called when an application calls Connection.setAutoCommit(), before the + * driver processes its own internal logic for setAutoCommit(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for setAutoCommit(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for setAutoCommit(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean setAutoCommit(boolean flag) throws SQLException; + + /** + * Called when an application calls Connection.setCatalog(), before the + * driver processes its own internal logic for setCatalog(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for setCatalog(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for setCatalog(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean setCatalog(String catalog) throws SQLException; + + /** + * Called when the driver has been told by the server that a transaction + * is now in progress (when one has not been currently in progress). + */ + public abstract boolean transactionBegun() throws SQLException; + + /** + * Called when the driver has been told by the server that a transaction + * has completed, and no transaction is currently in progress. + */ + public abstract boolean transactionCompleted() throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionProperties.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionProperties.java new file mode 100644 index 0000000..ed3fda9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionProperties.java @@ -0,0 +1,1428 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface ConnectionProperties { + + /** + * Returns a description of the connection properties as an XML document. + * + * @return the connection properties as an XML document. + * @throws SQLException + * if an error occurs. + */ + public String exposeAsXml() throws SQLException; + + public boolean getAllowLoadLocalInfile(); + + public boolean getAllowMultiQueries(); + + /** + * @return Returns the allowNanAndInf. + */ + public boolean getAllowNanAndInf(); + + /** + * @return Returns the allowUrlInLocalInfile. + */ + public boolean getAllowUrlInLocalInfile(); + + /** + * @return Returns the alwaysSendSetIsolation. + */ + public boolean getAlwaysSendSetIsolation(); + + /** + * @return Returns the autoDeserialize. + */ + public boolean getAutoDeserialize(); + + public boolean getAutoGenerateTestcaseScript(); + + public boolean getAutoReconnectForPools(); + + /** + * @return Returns the blobSendChunkSize. + */ + public int getBlobSendChunkSize(); + + /** + * @return Returns if cacheCallableStatements is enabled + */ + public boolean getCacheCallableStatements(); + + /** + * @return Returns the cachePreparedStatements. + */ + public boolean getCachePreparedStatements(); + + public boolean getCacheResultSetMetadata(); + + /** + * @return Returns the cacheServerConfiguration. + */ + public boolean getCacheServerConfiguration(); + + /** + * @return Returns the callableStatementCacheSize. + */ + public int getCallableStatementCacheSize(); + + public boolean getCapitalizeTypeNames(); + + /** + * @return Returns the characterSetResults. + */ + public String getCharacterSetResults(); + + /** + * @return Returns the clobberStreamingResults. + */ + public boolean getClobberStreamingResults(); + + public String getClobCharacterEncoding(); + + /** + * @return Returns the connectionCollation. + */ + public String getConnectionCollation(); + + public int getConnectTimeout(); + + public boolean getContinueBatchOnError(); + + public boolean getCreateDatabaseIfNotExist(); + + public int getDefaultFetchSize(); + + /** + * @return Returns the dontTrackOpenResources. + */ + public boolean getDontTrackOpenResources(); + + /** + * @return Returns the dumpQueriesOnException. + */ + public boolean getDumpQueriesOnException(); + + /** + * @return Returns the dynamicCalendars. + */ + public boolean getDynamicCalendars(); + + /** + * @return Returns the elideSetAutoCommits. + */ + public boolean getElideSetAutoCommits(); + + public boolean getEmptyStringsConvertToZero(); + + public boolean getEmulateLocators(); + + /** + * @return Returns the emulateUnsupportedPstmts. + */ + public boolean getEmulateUnsupportedPstmts(); + + /** + * @return Returns the enablePacketDebug. + */ + public boolean getEnablePacketDebug(); + + public String getEncoding(); + + /** + * @return Returns the explainSlowQueries. + */ + public boolean getExplainSlowQueries(); + + /** + * @return Returns the failOverReadOnly. + */ + public boolean getFailOverReadOnly(); + + /** + * @return Returns the gatherPerformanceMetrics. + */ + public boolean getGatherPerformanceMetrics(); + + /** + * @return Returns the holdResultsOpenOverStatementClose. + */ + public boolean getHoldResultsOpenOverStatementClose(); + + public boolean getIgnoreNonTxTables(); + + public int getInitialTimeout(); + + public boolean getInteractiveClient(); + + /** + * @return Returns the isInteractiveClient. + */ + public boolean getIsInteractiveClient(); + + /** + * @return Returns the jdbcCompliantTruncation. + */ + public boolean getJdbcCompliantTruncation(); + + /** + * @return Returns the dontTrackOpenResources. + */ + public int getLocatorFetchBufferSize(); + + public String getLogger(); + + /** + * @return Returns the loggerClassName. + */ + public String getLoggerClassName(); + + /** + * @return Returns the logSlowQueries. + */ + public boolean getLogSlowQueries(); + + public boolean getMaintainTimeStats(); + + /** + * @return Returns the maxQuerySizeToLog. + */ + public int getMaxQuerySizeToLog(); + + public int getMaxReconnects(); + + public int getMaxRows(); + + /** + * Returns the number of queries that metadata can be cached if caching is + * enabled. + * + * @return the number of queries to cache metadata for. + */ + public int getMetadataCacheSize(); + + /** + * @return Returns the noDatetimeStringSync. + */ + public boolean getNoDatetimeStringSync(); + + public boolean getNullCatalogMeansCurrent(); + + public boolean getNullNamePatternMatchesAll(); + + /** + * @return Returns the packetDebugBufferSize. + */ + public int getPacketDebugBufferSize(); + + public boolean getParanoid(); + + public boolean getPedantic(); + + /** + * @return Returns the preparedStatementCacheSize. + */ + public int getPreparedStatementCacheSize(); + + /** + * @return Returns the preparedStatementCacheSqlLimit. + */ + public int getPreparedStatementCacheSqlLimit(); + + public boolean getProfileSql(); + + /** + * @return Returns the profileSQL flag + */ + public boolean getProfileSQL(); + + /** + * @return Returns the propertiesTransform. + */ + public String getPropertiesTransform(); + + public int getQueriesBeforeRetryMaster(); + + public boolean getReconnectAtTxEnd(); + + public boolean getRelaxAutoCommit(); + + /** + * @return Returns the reportMetricsIntervalMillis. + */ + public int getReportMetricsIntervalMillis(); + + public boolean getRequireSSL(); + + /** + * @return Returns the rollbackOnPooledClose. + */ + public boolean getRollbackOnPooledClose(); + + /** + * Returns whether or not hosts will be picked in a round-robin fashion. + * + * @return Returns the roundRobinLoadBalance property. + */ + public boolean getRoundRobinLoadBalance(); + + /** + * @return Returns the runningCTS13. + */ + public boolean getRunningCTS13(); + + public int getSecondsBeforeRetryMaster(); + + /** + * Returns the 'serverTimezone' property. + * + * @return the configured server timezone property. + */ + public String getServerTimezone(); + + /** + * @return Returns the sessionVariables. + */ + public String getSessionVariables(); + + /** + * @return Returns the slowQueryThresholdMillis. + */ + public int getSlowQueryThresholdMillis(); + + public String getSocketFactoryClassName(); + + public int getSocketTimeout(); + + public boolean getStrictFloatingPoint(); + + public boolean getStrictUpdates(); + + /** + * @return Returns the tinyInt1isBit. + */ + public boolean getTinyInt1isBit(); + + /** + * @return Returns the logProtocol. + */ + public boolean getTraceProtocol(); + + public boolean getTransformedBitIsBoolean(); + + public boolean getUseCompression(); + + /** + * @return Returns the useFastIntParsing. + */ + public boolean getUseFastIntParsing(); + + public boolean getUseHostsInPrivileges(); + + public boolean getUseInformationSchema(); + + /** + * @return Returns the useLocalSessionState. + */ + public boolean getUseLocalSessionState(); + + /** + * @return Returns the useOldUTF8Behavior. + */ + public boolean getUseOldUTF8Behavior(); + + /** + * @return Returns the useOnlyServerErrorMessages. + */ + public boolean getUseOnlyServerErrorMessages(); + + /** + * @return Returns the useReadAheadInput. + */ + public boolean getUseReadAheadInput(); + + public boolean getUseServerPreparedStmts(); + + /** + * @return Returns the useSqlStateCodes state. + */ + public boolean getUseSqlStateCodes(); + + public boolean getUseSSL(); + + boolean isUseSSLExplicit(); + + public boolean getUseStreamLengthsInPrepStmts(); + + public boolean getUseTimezone(); + + public boolean getUseUltraDevWorkAround(); + + /** + * @return Returns the useUnbufferedInput. + */ + public boolean getUseUnbufferedInput(); + + public boolean getUseUnicode(); + + /** + * Returns whether or not the driver advises of proper usage. + * + * @return the value of useUsageAdvisor + */ + public boolean getUseUsageAdvisor(); + + public boolean getYearIsDateType(); + + /** + * @return Returns the zeroDateTimeBehavior. + */ + public String getZeroDateTimeBehavior(); + + public void setAllowLoadLocalInfile(boolean property); + + /** + * @param property + */ + public void setAllowMultiQueries(boolean property); + + /** + * @param allowNanAndInf + * The allowNanAndInf to set. + */ + public void setAllowNanAndInf(boolean flag); + + /** + * @param allowUrlInLocalInfile + * The allowUrlInLocalInfile to set. + */ + public void setAllowUrlInLocalInfile(boolean flag); + + /** + * @param alwaysSendSetIsolation + * The alwaysSendSetIsolation to set. + */ + public void setAlwaysSendSetIsolation(boolean flag); + + /** + * @param autoDeserialize + * The autoDeserialize to set. + */ + public void setAutoDeserialize(boolean flag); + + public void setAutoGenerateTestcaseScript(boolean flag); + + /** + * @param flag + * The autoReconnect to set. + */ + public void setAutoReconnect(boolean flag); + + public void setAutoReconnectForConnectionPools(boolean property); + + /** + * @param flag + * The autoReconnectForPools to set. + */ + public void setAutoReconnectForPools(boolean flag); + + /** + * @param blobSendChunkSize + * The blobSendChunkSize to set. + */ + public void setBlobSendChunkSize(String value) throws SQLException; + + /** + * @param flag + * The cacheCallableStatements to set. + */ + public void setCacheCallableStatements(boolean flag); + + /** + * @param flag + * The cachePreparedStatements to set. + */ + public void setCachePreparedStatements(boolean flag); + + /** + * Sets whether or not we should cache result set metadata. + * + * @param property + */ + public void setCacheResultSetMetadata(boolean property); + + /** + * @param cacheServerConfiguration + * The cacheServerConfiguration to set. + */ + public void setCacheServerConfiguration(boolean flag); + + /** + * Configures the number of callable statements to cache. (this is + * configurable during the life of the connection). + * + * @param size + * The callableStatementCacheSize to set. + * @throws SQLException + */ + public void setCallableStatementCacheSize(int size) throws SQLException; + + public void setCapitalizeDBMDTypes(boolean property); + + /** + * @param flag + * The capitalizeTypeNames to set. + */ + public void setCapitalizeTypeNames(boolean flag); + + /** + * @param encoding + * The characterEncoding to set. + */ + public void setCharacterEncoding(String encoding); + + /** + * @param characterSet + * The characterSetResults to set. + */ + public void setCharacterSetResults(String characterSet); + + /** + * @param flag + * The clobberStreamingResults to set. + */ + public void setClobberStreamingResults(boolean flag); + + public void setClobCharacterEncoding(String encoding); + + /** + * @param collation + * The connectionCollation to set. + */ + public void setConnectionCollation(String collation); + + /** + * @param timeoutMs + * @throws SQLException + */ + public void setConnectTimeout(int timeoutMs) throws SQLException; + + /** + * @param property + */ + public void setContinueBatchOnError(boolean property); + + public void setCreateDatabaseIfNotExist(boolean flag); + + public void setDefaultFetchSize(int n) throws SQLException; + + /** + * @param property + */ + public void setDetectServerPreparedStmts(boolean property); + + /** + * @param dontTrackOpenResources + * The dontTrackOpenResources to set. + */ + public void setDontTrackOpenResources(boolean flag); + + /** + * @param flag + * The dumpQueriesOnException to set. + */ + public void setDumpQueriesOnException(boolean flag); + + /** + * @param dynamicCalendars + * The dynamicCalendars to set. + */ + public void setDynamicCalendars(boolean flag); + + /** + * @param flag + * The elideSetAutoCommits to set. + */ + public void setElideSetAutoCommits(boolean flag); + + public void setEmptyStringsConvertToZero(boolean flag); + + /** + * @param property + */ + public void setEmulateLocators(boolean property); + + /** + * @param emulateUnsupportedPstmts + * The emulateUnsupportedPstmts to set. + */ + public void setEmulateUnsupportedPstmts(boolean flag); + + /** + * @param flag + * The enablePacketDebug to set. + */ + public void setEnablePacketDebug(boolean flag); + + /** + * @param property + */ + public void setEncoding(String property); + + /** + * @param flag + * The explainSlowQueries to set. + */ + public void setExplainSlowQueries(boolean flag); + + /** + * @param flag + * The failOverReadOnly to set. + */ + public void setFailOverReadOnly(boolean flag); + + /** + * @param flag + * The gatherPerformanceMetrics to set. + */ + public void setGatherPerformanceMetrics(boolean flag); + + /** + * @param holdResultsOpenOverStatementClose + * The holdResultsOpenOverStatementClose to set. + */ + public void setHoldResultsOpenOverStatementClose(boolean flag); + + /** + * @param property + */ + public void setIgnoreNonTxTables(boolean property); + + /** + * @param property + * @throws SQLException + */ + public void setInitialTimeout(int property) throws SQLException; + + /** + * @param property + */ + public void setIsInteractiveClient(boolean property); + + /** + * @param flag + * The jdbcCompliantTruncation to set. + */ + public void setJdbcCompliantTruncation(boolean flag); + + /** + * @param locatorFetchBufferSize + * The locatorFetchBufferSize to set. + */ + public void setLocatorFetchBufferSize(String value) throws SQLException; + + /** + * @param property + */ + public void setLogger(String property); + + /** + * @param className + * The loggerClassName to set. + */ + public void setLoggerClassName(String className); + + /** + * @param flag + * The logSlowQueries to set. + */ + public void setLogSlowQueries(boolean flag); + + public void setMaintainTimeStats(boolean flag); + + /** + * @param sizeInBytes + * The maxQuerySizeToLog to set. + * @throws SQLException + */ + public void setMaxQuerySizeToLog(int sizeInBytes) throws SQLException; + + /** + * @param property + * @throws SQLException + */ + public void setMaxReconnects(int property) throws SQLException; + + /** + * @param property + * @throws SQLException + */ + public void setMaxRows(int property) throws SQLException; + + /** + * Sets the number of queries that metadata can be cached if caching is + * enabled. + * + * @param value + * the number of queries to cache metadata for. + * @throws SQLException + */ + public void setMetadataCacheSize(int value) throws SQLException; + + /** + * @param noDatetimeStringSync + * The noDatetimeStringSync to set. + */ + public void setNoDatetimeStringSync(boolean flag); + + public void setNullCatalogMeansCurrent(boolean value); + + public void setNullNamePatternMatchesAll(boolean value); + + /** + * @param size + * The packetDebugBufferSize to set. + * @throws SQLException + */ + public void setPacketDebugBufferSize(int size) throws SQLException; + + /** + * @param property + */ + public void setParanoid(boolean property); + + /** + * @param property + */ + public void setPedantic(boolean property); + + /** + * @param cacheSize + * The preparedStatementCacheSize to set. + * @throws SQLException + */ + public void setPreparedStatementCacheSize(int cacheSize) throws SQLException; + + /** + * @param cacheSqlLimit + * The preparedStatementCacheSqlLimit to set. + * @throws SQLException + */ + public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) throws SQLException; + + /** + * @param property + */ + public void setProfileSql(boolean property); + + /** + * @param flag + * The profileSQL to set. + */ + public void setProfileSQL(boolean flag); + + /** + * @param propertiesTransform + * The propertiesTransform to set. + */ + public void setPropertiesTransform(String value); + + /** + * @param property + * @throws SQLException + */ + public void setQueriesBeforeRetryMaster(int property) throws SQLException; + + /** + * @param property + */ + public void setReconnectAtTxEnd(boolean property); + + /** + * @param property + */ + public void setRelaxAutoCommit(boolean property); + + /** + * @param millis + * The reportMetricsIntervalMillis to set. + * @throws SQLException + */ + public void setReportMetricsIntervalMillis(int millis) throws SQLException; + + /** + * @param property + */ + public void setRequireSSL(boolean property); + + public void setRetainStatementAfterResultSetClose(boolean flag); + + /** + * @param rollbackOnPooledClose + * The rollbackOnPooledClose to set. + */ + public void setRollbackOnPooledClose(boolean flag); + + /** + * Sets whether or not hosts will be picked in a round-robin fashion. + * + * @param flag + * The roundRobinLoadBalance property to set. + */ + public void setRoundRobinLoadBalance(boolean flag); + + /** + * @param runningCTS13 + * The runningCTS13 to set. + */ + public void setRunningCTS13(boolean flag); + + /** + * @param property + * @throws SQLException + */ + public void setSecondsBeforeRetryMaster(int property) throws SQLException; + + /** + * @param property + */ + public void setServerTimezone(String property); + + /** + * @param sessionVariables + * The sessionVariables to set. + */ + public void setSessionVariables(String variables); + + /** + * @param millis + * The slowQueryThresholdMillis to set. + * @throws SQLException + */ + public void setSlowQueryThresholdMillis(int millis) throws SQLException; + + /** + * @param property + */ + public void setSocketFactoryClassName(String property); + + /** + * @param property + * @throws SQLException + */ + public void setSocketTimeout(int property) throws SQLException; + + /** + * @param property + */ + public void setStrictFloatingPoint(boolean property); + + /** + * @param property + */ + public void setStrictUpdates(boolean property); + + /** + * @param tinyInt1isBit + * The tinyInt1isBit to set. + */ + public void setTinyInt1isBit(boolean flag); + + /** + * @param flag + * The logProtocol to set. + */ + public void setTraceProtocol(boolean flag); + + public void setTransformedBitIsBoolean(boolean flag); + + /** + * @param property + */ + public void setUseCompression(boolean property); + + /** + * @param useFastIntParsing + * The useFastIntParsing to set. + */ + public void setUseFastIntParsing(boolean flag); + + /** + * @param property + */ + public void setUseHostsInPrivileges(boolean property); + + public void setUseInformationSchema(boolean flag); + + /** + * @param useLocalSessionState + * The useLocalSessionState to set. + */ + public void setUseLocalSessionState(boolean flag); + + /** + * @param useOldUTF8Behavior + * The useOldUTF8Behavior to set. + */ + public void setUseOldUTF8Behavior(boolean flag); + + /** + * @param useOnlyServerErrorMessages + * The useOnlyServerErrorMessages to set. + */ + public void setUseOnlyServerErrorMessages(boolean flag); + + /** + * @param useReadAheadInput + * The useReadAheadInput to set. + */ + public void setUseReadAheadInput(boolean flag); + + /** + * @param flag + * The detectServerPreparedStmts to set. + */ + public void setUseServerPreparedStmts(boolean flag); + + /** + * @param flag + * The useSqlStateCodes to set. + */ + public void setUseSqlStateCodes(boolean flag); + + /** + * @param property + */ + public void setUseSSL(boolean property); + + /** + * @param property + */ + public void setUseStreamLengthsInPrepStmts(boolean property); + + /** + * @param property + */ + public void setUseTimezone(boolean property); + + /** + * @param property + */ + public void setUseUltraDevWorkAround(boolean property); + + /** + * @param flag + * The useUnbufferedInput to set. + */ + public void setUseUnbufferedInput(boolean flag); + + /** + * @param flag + * The useUnicode to set. + */ + public void setUseUnicode(boolean flag); + + /** + * Sets whether or not the driver advises of proper usage. + * + * @param useUsageAdvisorFlag + * whether or not the driver advises of proper usage. + */ + public void setUseUsageAdvisor(boolean useUsageAdvisorFlag); + + public void setYearIsDateType(boolean flag); + + /** + * @param zeroDateTimeBehavior + * The zeroDateTimeBehavior to set. + */ + public void setZeroDateTimeBehavior(String behavior); + + /** + * @return Returns the useUnbufferedInput. + */ + public boolean useUnbufferedInput(); + + public boolean getUseCursorFetch(); + + public void setUseCursorFetch(boolean flag); + + public boolean getOverrideSupportsIntegrityEnhancementFacility(); + + public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag); + + public boolean getNoTimezoneConversionForTimeType(); + + public void setNoTimezoneConversionForTimeType(boolean flag); + + public boolean getNoTimezoneConversionForDateType(); + + public void setNoTimezoneConversionForDateType(boolean flag); + + public boolean getCacheDefaultTimezone(); + + public void setCacheDefaultTimezone(boolean flag); + + public boolean getUseJDBCCompliantTimezoneShift(); + + public void setUseJDBCCompliantTimezoneShift(boolean flag); + + public boolean getAutoClosePStmtStreams(); + + public void setAutoClosePStmtStreams(boolean flag); + + public boolean getProcessEscapeCodesForPrepStmts(); + + public void setProcessEscapeCodesForPrepStmts(boolean flag); + + public boolean getUseGmtMillisForDatetimes(); + + public void setUseGmtMillisForDatetimes(boolean flag); + + public boolean getDumpMetadataOnColumnNotFound(); + + public void setDumpMetadataOnColumnNotFound(boolean flag); + + public String getResourceId(); + + public void setResourceId(String resourceId); + + public boolean getRewriteBatchedStatements(); + + public void setRewriteBatchedStatements(boolean flag); + + public boolean getJdbcCompliantTruncationForReads(); + + public void setJdbcCompliantTruncationForReads(boolean jdbcCompliantTruncationForReads); + + public boolean getUseJvmCharsetConverters(); + + public void setUseJvmCharsetConverters(boolean flag); + + public boolean getPinGlobalTxToPhysicalConnection(); + + public void setPinGlobalTxToPhysicalConnection(boolean flag); + + public void setGatherPerfMetrics(boolean flag); + + public boolean getGatherPerfMetrics(); + + public void setUltraDevHack(boolean flag); + + public boolean getUltraDevHack(); + + public void setInteractiveClient(boolean property); + + public void setSocketFactory(String name); + + public String getSocketFactory(); + + public void setUseServerPrepStmts(boolean flag); + + public boolean getUseServerPrepStmts(); + + public void setCacheCallableStmts(boolean flag); + + public boolean getCacheCallableStmts(); + + public void setCachePrepStmts(boolean flag); + + public boolean getCachePrepStmts(); + + public void setCallableStmtCacheSize(int cacheSize) throws SQLException; + + public int getCallableStmtCacheSize(); + + public void setPrepStmtCacheSize(int cacheSize) throws SQLException; + + public int getPrepStmtCacheSize(); + + public void setPrepStmtCacheSqlLimit(int sqlLimit) throws SQLException; + + public int getPrepStmtCacheSqlLimit(); + + public boolean getNoAccessToProcedureBodies(); + + public void setNoAccessToProcedureBodies(boolean flag); + + public boolean getUseOldAliasMetadataBehavior(); + + public void setUseOldAliasMetadataBehavior(boolean flag); + + public String getClientCertificateKeyStorePassword(); + + public void setClientCertificateKeyStorePassword(String value); + + public String getClientCertificateKeyStoreType(); + + public void setClientCertificateKeyStoreType(String value); + + public String getClientCertificateKeyStoreUrl(); + + public void setClientCertificateKeyStoreUrl(String value); + + public String getTrustCertificateKeyStorePassword(); + + public void setTrustCertificateKeyStorePassword(String value); + + public String getTrustCertificateKeyStoreType(); + + public void setTrustCertificateKeyStoreType(String value); + + public String getTrustCertificateKeyStoreUrl(); + + public void setTrustCertificateKeyStoreUrl(String value); + + public boolean getUseSSPSCompatibleTimezoneShift(); + + public void setUseSSPSCompatibleTimezoneShift(boolean flag); + + public boolean getTreatUtilDateAsTimestamp(); + + public void setTreatUtilDateAsTimestamp(boolean flag); + + public boolean getUseFastDateParsing(); + + public void setUseFastDateParsing(boolean flag); + + public String getLocalSocketAddress(); + + public void setLocalSocketAddress(String address); + + public void setUseConfigs(String configs); + + public String getUseConfigs(); + + public boolean getGenerateSimpleParameterMetadata(); + + public void setGenerateSimpleParameterMetadata(boolean flag); + + public boolean getLogXaCommands(); + + public void setLogXaCommands(boolean flag); + + public int getResultSetSizeThreshold(); + + public void setResultSetSizeThreshold(int threshold) throws SQLException; + + public int getNetTimeoutForStreamingResults(); + + public void setNetTimeoutForStreamingResults(int value) throws SQLException; + + public boolean getEnableQueryTimeouts(); + + public void setEnableQueryTimeouts(boolean flag); + + public boolean getPadCharsWithSpace(); + + public void setPadCharsWithSpace(boolean flag); + + public boolean getUseDynamicCharsetInfo(); + + public void setUseDynamicCharsetInfo(boolean flag); + + public String getClientInfoProvider(); + + public void setClientInfoProvider(String classname); + + public boolean getPopulateInsertRowWithDefaultValues(); + + public void setPopulateInsertRowWithDefaultValues(boolean flag); + + public String getLoadBalanceStrategy(); + + public void setLoadBalanceStrategy(String strategy); + + public boolean getTcpNoDelay(); + + public void setTcpNoDelay(boolean flag); + + public boolean getTcpKeepAlive(); + + public void setTcpKeepAlive(boolean flag); + + public int getTcpRcvBuf(); + + public void setTcpRcvBuf(int bufSize) throws SQLException; + + public int getTcpSndBuf(); + + public void setTcpSndBuf(int bufSize) throws SQLException; + + public int getTcpTrafficClass(); + + public void setTcpTrafficClass(int classFlags) throws SQLException; + + public boolean getUseNanosForElapsedTime(); + + public void setUseNanosForElapsedTime(boolean flag); + + public long getSlowQueryThresholdNanos(); + + public void setSlowQueryThresholdNanos(long nanos) throws SQLException; + + public String getStatementInterceptors(); + + public void setStatementInterceptors(String value); + + public boolean getUseDirectRowUnpack(); + + public void setUseDirectRowUnpack(boolean flag); + + public String getLargeRowSizeThreshold(); + + public void setLargeRowSizeThreshold(String value) throws SQLException; + + public boolean getUseBlobToStoreUTF8OutsideBMP(); + + public void setUseBlobToStoreUTF8OutsideBMP(boolean flag); + + public String getUtf8OutsideBmpExcludedColumnNamePattern(); + + public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern); + + public String getUtf8OutsideBmpIncludedColumnNamePattern(); + + public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern); + + public boolean getIncludeInnodbStatusInDeadlockExceptions(); + + public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag); + + public boolean getIncludeThreadDumpInDeadlockExceptions(); + + public void setIncludeThreadDumpInDeadlockExceptions(boolean flag); + + public boolean getIncludeThreadNamesAsStatementComment(); + + public void setIncludeThreadNamesAsStatementComment(boolean flag); + + public boolean getBlobsAreStrings(); + + public void setBlobsAreStrings(boolean flag); + + public boolean getFunctionsNeverReturnBlobs(); + + public void setFunctionsNeverReturnBlobs(boolean flag); + + public boolean getAutoSlowLog(); + + public void setAutoSlowLog(boolean flag); + + public String getConnectionLifecycleInterceptors(); + + public void setConnectionLifecycleInterceptors(String interceptors); + + public String getProfilerEventHandler(); + + public void setProfilerEventHandler(String handler); + + public boolean getVerifyServerCertificate(); + + public void setVerifyServerCertificate(boolean flag); + + public boolean getUseLegacyDatetimeCode(); + + public void setUseLegacyDatetimeCode(boolean flag); + + public boolean getSendFractionalSeconds(); + + public void setSendFractionalSeconds(boolean flag); + + public int getSelfDestructOnPingSecondsLifetime(); + + public void setSelfDestructOnPingSecondsLifetime(int seconds) throws SQLException; + + public int getSelfDestructOnPingMaxOperations(); + + public void setSelfDestructOnPingMaxOperations(int maxOperations) throws SQLException; + + public boolean getUseColumnNamesInFindColumn(); + + public void setUseColumnNamesInFindColumn(boolean flag); + + public boolean getUseLocalTransactionState(); + + public void setUseLocalTransactionState(boolean flag); + + public boolean getCompensateOnDuplicateKeyUpdateCounts(); + + public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag); + + public void setUseAffectedRows(boolean flag); + + public boolean getUseAffectedRows(); + + public void setPasswordCharacterEncoding(String characterSet); + + public String getPasswordCharacterEncoding(); + + public int getLoadBalanceBlacklistTimeout(); + + public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) throws SQLException; + + public void setRetriesAllDown(int retriesAllDown) throws SQLException; + + public int getRetriesAllDown(); + + public ExceptionInterceptor getExceptionInterceptor(); + + public void setExceptionInterceptors(String exceptionInterceptors); + + public String getExceptionInterceptors(); + + public boolean getQueryTimeoutKillsConnection(); + + public void setQueryTimeoutKillsConnection(boolean queryTimeoutKillsConnection); + + public int getMaxAllowedPacket(); + + boolean getRetainStatementAfterResultSetClose(); + + public int getLoadBalancePingTimeout(); + + public void setLoadBalancePingTimeout(int loadBalancePingTimeout) throws SQLException; + + public boolean getLoadBalanceValidateConnectionOnSwapServer(); + + public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer); + + public String getLoadBalanceConnectionGroup(); + + public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup); + + public String getLoadBalanceExceptionChecker(); + + public void setLoadBalanceExceptionChecker(String loadBalanceExceptionChecker); + + public String getLoadBalanceSQLStateFailover(); + + public void setLoadBalanceSQLStateFailover(String loadBalanceSQLStateFailover); + + public String getLoadBalanceSQLExceptionSubclassFailover(); + + public void setLoadBalanceSQLExceptionSubclassFailover(String loadBalanceSQLExceptionSubclassFailover); + + public boolean getLoadBalanceEnableJMX(); + + public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX); + + public void setLoadBalanceHostRemovalGracePeriod(int loadBalanceHostRemovalGracePeriod) throws SQLException; + + public int getLoadBalanceHostRemovalGracePeriod(); + + public void setLoadBalanceAutoCommitStatementThreshold(int loadBalanceAutoCommitStatementThreshold) throws SQLException; + + public int getLoadBalanceAutoCommitStatementThreshold(); + + public void setLoadBalanceAutoCommitStatementRegex(String loadBalanceAutoCommitStatementRegex); + + public String getLoadBalanceAutoCommitStatementRegex(); + + public void setAuthenticationPlugins(String authenticationPlugins); + + public String getAuthenticationPlugins(); + + public void setDisabledAuthenticationPlugins(String disabledAuthenticationPlugins); + + public String getDisabledAuthenticationPlugins(); + + public void setDefaultAuthenticationPlugin(String defaultAuthenticationPlugin); + + public String getDefaultAuthenticationPlugin(); + + public void setParseInfoCacheFactory(String factoryClassname); + + public String getParseInfoCacheFactory(); + + public void setServerConfigCacheFactory(String factoryClassname); + + public String getServerConfigCacheFactory(); + + public void setDisconnectOnExpiredPasswords(boolean disconnectOnExpiredPasswords); + + public boolean getDisconnectOnExpiredPasswords(); + + public boolean getAllowMasterDownConnections(); + + public void setAllowMasterDownConnections(boolean connectIfMasterDown); + + public boolean getAllowSlaveDownConnections(); + + public void setAllowSlaveDownConnections(boolean connectIfSlaveDown); + + public boolean getReadFromMasterWhenNoSlaves(); + + public void setReadFromMasterWhenNoSlaves(boolean useMasterIfSlavesDown); + + public boolean getReplicationEnableJMX(); + + public void setReplicationEnableJMX(boolean replicationEnableJMX); + + public void setGetProceduresReturnsFunctions(boolean getProcedureReturnsFunctions); + + public boolean getGetProceduresReturnsFunctions(); + + public void setDetectCustomCollations(boolean detectCustomCollations); + + public boolean getDetectCustomCollations(); + + String getConnectionAttributes() throws SQLException; + + public String getServerRSAPublicKeyFile(); + + public void setServerRSAPublicKeyFile(String serverRSAPublicKeyFile) throws SQLException; + + public boolean getAllowPublicKeyRetrieval(); + + public void setAllowPublicKeyRetrieval(boolean allowPublicKeyRetrieval) throws SQLException; + + public void setDontCheckOnDuplicateKeyUpdateInSQL(boolean dontCheckOnDuplicateKeyUpdateInSQL); + + public boolean getDontCheckOnDuplicateKeyUpdateInSQL(); + + public void setSocksProxyHost(String socksProxyHost); + + public String getSocksProxyHost(); + + public void setSocksProxyPort(int socksProxyPort) throws SQLException; + + public int getSocksProxyPort(); + + public boolean getReadOnlyPropagatesToServer(); + + public void setReadOnlyPropagatesToServer(boolean flag); + + public String getEnabledSSLCipherSuites(); + + public void setEnabledSSLCipherSuites(String cipherSuites); + + public boolean getEnableEscapeProcessing(); + + public void setEnableEscapeProcessing(boolean flag); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesImpl.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesImpl.java new file mode 100644 index 0000000..ae8ea44 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesImpl.java @@ -0,0 +1,4933 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.StringRefAddr; + +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.log.StandardLogger; + +/** + * Represents configurable properties for Connections and DataSources. Can also expose properties as JDBC DriverPropertyInfo if required as well. + */ +public class ConnectionPropertiesImpl implements Serializable, ConnectionProperties { + + private static final long serialVersionUID = 4257801713007640580L; + + static class BooleanConnectionProperty extends ConnectionProperty implements Serializable { + + private static final long serialVersionUID = 2540132501709159404L; + + /** + * @param propertyNameToSet + * @param defaultValueToSet + * @param descriptionToSet + * @param sinceVersionToSet + */ + BooleanConnectionProperty(String propertyNameToSet, boolean defaultValueToSet, String descriptionToSet, String sinceVersionToSet, String category, + int orderInCategory) { + super(propertyNameToSet, Boolean.valueOf(defaultValueToSet), null, 0, 0, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#getAllowableValues() + */ + @Override + String[] getAllowableValues() { + return new String[] { "true", "false", "yes", "no" }; + } + + boolean getValueAsBoolean() { + return ((Boolean) this.valueAsObject).booleanValue(); + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#hasValueConstraints() + */ + @Override + boolean hasValueConstraints() { + return true; + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#initializeFrom(java.util.Properties) + */ + @Override + void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (extractedValue != null) { + validateStringValues(extractedValue, exceptionInterceptor); + + this.valueAsObject = Boolean.valueOf(extractedValue.equalsIgnoreCase("TRUE") || extractedValue.equalsIgnoreCase("YES")); + this.wasExplicitlySet = true; + } else { + this.valueAsObject = this.defaultValue; + } + this.updateCount++; + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#isRangeBased() + */ + @Override + boolean isRangeBased() { + return false; + } + + void setValue(boolean valueFlag) { + this.valueAsObject = Boolean.valueOf(valueFlag); + this.wasExplicitlySet = true; + this.updateCount++; + } + } + + static abstract class ConnectionProperty implements Serializable { + + static final long serialVersionUID = -6644853639584478367L; + + String[] allowableValues; + + String categoryName; + + Object defaultValue; + + int lowerBound; + + int order; + + String propertyName; + + String sinceVersion; + + int upperBound; + + Object valueAsObject; + + boolean required; + + String description; + + int updateCount = 0; + + boolean wasExplicitlySet = false; + + public ConnectionProperty() { + } + + ConnectionProperty(String propertyNameToSet, Object defaultValueToSet, String[] allowableValuesToSet, int lowerBoundToSet, int upperBoundToSet, + String descriptionToSet, String sinceVersionToSet, String category, int orderInCategory) { + + this.description = descriptionToSet; + this.propertyName = propertyNameToSet; + this.defaultValue = defaultValueToSet; + this.valueAsObject = defaultValueToSet; + this.allowableValues = allowableValuesToSet; + this.lowerBound = lowerBoundToSet; + this.upperBound = upperBoundToSet; + this.required = false; + this.sinceVersion = sinceVersionToSet; + this.categoryName = category; + this.order = orderInCategory; + } + + String[] getAllowableValues() { + return this.allowableValues; + } + + /** + * @return Returns the categoryName. + */ + String getCategoryName() { + return this.categoryName; + } + + Object getDefaultValue() { + return this.defaultValue; + } + + int getLowerBound() { + return this.lowerBound; + } + + /** + * @return Returns the order. + */ + int getOrder() { + return this.order; + } + + String getPropertyName() { + return this.propertyName; + } + + int getUpperBound() { + return this.upperBound; + } + + Object getValueAsObject() { + return this.valueAsObject; + } + + int getUpdateCount() { + return this.updateCount; + } + + boolean isExplicitlySet() { + return this.wasExplicitlySet; + } + + abstract boolean hasValueConstraints(); + + void initializeFrom(Properties extractFrom, ExceptionInterceptor exceptionInterceptor) throws SQLException { + String extractedValue = extractFrom.getProperty(getPropertyName()); + extractFrom.remove(getPropertyName()); + initializeFrom(extractedValue, exceptionInterceptor); + } + + void initializeFrom(Reference ref, ExceptionInterceptor exceptionInterceptor) throws SQLException { + RefAddr refAddr = ref.get(getPropertyName()); + + if (refAddr != null) { + String refContentAsString = (String) refAddr.getContent(); + + initializeFrom(refContentAsString, exceptionInterceptor); + } + } + + abstract void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException; + + abstract boolean isRangeBased(); + + /** + * @param categoryName + * The categoryName to set. + */ + void setCategoryName(String categoryName) { + this.categoryName = categoryName; + } + + /** + * @param order + * The order to set. + */ + void setOrder(int order) { + this.order = order; + } + + void setValueAsObject(Object obj) { + this.valueAsObject = obj; + this.updateCount++; + } + + void storeTo(Reference ref) { + if (getValueAsObject() != null) { + ref.add(new StringRefAddr(getPropertyName(), getValueAsObject().toString())); + } + } + + DriverPropertyInfo getAsDriverPropertyInfo() { + DriverPropertyInfo dpi = new DriverPropertyInfo(this.propertyName, null); + dpi.choices = getAllowableValues(); + dpi.value = (this.valueAsObject != null) ? this.valueAsObject.toString() : null; + dpi.required = this.required; + dpi.description = this.description; + + return dpi; + } + + void validateStringValues(String valueToValidate, ExceptionInterceptor exceptionInterceptor) throws SQLException { + String[] validateAgainst = getAllowableValues(); + + if (valueToValidate == null) { + return; + } + + if ((validateAgainst == null) || (validateAgainst.length == 0)) { + return; + } + + for (int i = 0; i < validateAgainst.length; i++) { + if ((validateAgainst[i] != null) && validateAgainst[i].equalsIgnoreCase(valueToValidate)) { + return; + } + } + + StringBuilder errorMessageBuf = new StringBuilder(); + + errorMessageBuf.append("The connection property '"); + errorMessageBuf.append(getPropertyName()); + errorMessageBuf.append("' only accepts values of the form: "); + + if (validateAgainst.length != 0) { + errorMessageBuf.append("'"); + errorMessageBuf.append(validateAgainst[0]); + errorMessageBuf.append("'"); + + for (int i = 1; i < (validateAgainst.length - 1); i++) { + errorMessageBuf.append(", "); + errorMessageBuf.append("'"); + errorMessageBuf.append(validateAgainst[i]); + errorMessageBuf.append("'"); + } + + errorMessageBuf.append(" or '"); + errorMessageBuf.append(validateAgainst[validateAgainst.length - 1]); + errorMessageBuf.append("'"); + } + + errorMessageBuf.append(". The value '"); + errorMessageBuf.append(valueToValidate); + errorMessageBuf.append("' is not in this set."); + + throw SQLError.createSQLException(errorMessageBuf.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + static class IntegerConnectionProperty extends ConnectionProperty implements Serializable { + + private static final long serialVersionUID = -3004305481796850832L; + + int multiplier = 1; + + public IntegerConnectionProperty(String propertyNameToSet, Object defaultValueToSet, String[] allowableValuesToSet, int lowerBoundToSet, + int upperBoundToSet, String descriptionToSet, String sinceVersionToSet, String category, int orderInCategory) { + super(propertyNameToSet, defaultValueToSet, allowableValuesToSet, lowerBoundToSet, upperBoundToSet, descriptionToSet, sinceVersionToSet, category, + orderInCategory); + } + + IntegerConnectionProperty(String propertyNameToSet, int defaultValueToSet, int lowerBoundToSet, int upperBoundToSet, String descriptionToSet, + String sinceVersionToSet, String category, int orderInCategory) { + super(propertyNameToSet, Integer.valueOf(defaultValueToSet), null, lowerBoundToSet, upperBoundToSet, descriptionToSet, sinceVersionToSet, category, + orderInCategory); + } + + /** + * @param propertyNameToSet + * @param defaultValueToSet + * @param descriptionToSet + * @param sinceVersionToSet + */ + + IntegerConnectionProperty(String propertyNameToSet, int defaultValueToSet, String descriptionToSet, String sinceVersionToSet, String category, + int orderInCategory) { + this(propertyNameToSet, defaultValueToSet, 0, 0, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getAllowableValues() + */ + @Override + String[] getAllowableValues() { + return null; + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getLowerBound() + */ + @Override + int getLowerBound() { + return this.lowerBound; + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getUpperBound() + */ + @Override + int getUpperBound() { + return this.upperBound; + } + + int getValueAsInt() { + return ((Integer) this.valueAsObject).intValue(); + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints() + */ + @Override + boolean hasValueConstraints() { + return false; + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.lang.String) + */ + @Override + void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (extractedValue != null) { + try { + // Parse decimals, too + int intValue = (int) (Double.valueOf(extractedValue).doubleValue() * this.multiplier); + + setValue(intValue, extractedValue, exceptionInterceptor); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException("The connection property '" + getPropertyName() + "' only accepts integer values. The value '" + + extractedValue + "' can not be converted to an integer.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } else { + this.valueAsObject = this.defaultValue; + } + this.updateCount++; + } + + /** + * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#isRangeBased() + */ + @Override + boolean isRangeBased() { + return getUpperBound() != getLowerBound(); + } + + void setValue(int intValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + setValue(intValue, null, exceptionInterceptor); + } + + void setValue(int intValue, String valueAsString, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (isRangeBased()) { + if ((intValue < getLowerBound()) || (intValue > getUpperBound())) { + throw SQLError.createSQLException( + "The connection property '" + getPropertyName() + "' only accepts integer values in the range of " + getLowerBound() + " - " + + getUpperBound() + ", the value '" + (valueAsString == null ? intValue : valueAsString) + "' exceeds this range.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + this.valueAsObject = Integer.valueOf(intValue); + this.wasExplicitlySet = true; + this.updateCount++; + } + } + + static public class LongConnectionProperty extends IntegerConnectionProperty { + + private static final long serialVersionUID = 6068572984340480895L; + + LongConnectionProperty(String propertyNameToSet, long defaultValueToSet, long lowerBoundToSet, long upperBoundToSet, String descriptionToSet, + String sinceVersionToSet, String category, int orderInCategory) { + super(propertyNameToSet, Long.valueOf(defaultValueToSet), null, (int) lowerBoundToSet, (int) upperBoundToSet, descriptionToSet, sinceVersionToSet, + category, orderInCategory); + } + + LongConnectionProperty(String propertyNameToSet, long defaultValueToSet, String descriptionToSet, String sinceVersionToSet, String category, + int orderInCategory) { + this(propertyNameToSet, defaultValueToSet, 0, 0, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + void setValue(long longValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + setValue(longValue, null, exceptionInterceptor); + } + + void setValue(long longValue, String valueAsString, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (isRangeBased()) { + if ((longValue < getLowerBound()) || (longValue > getUpperBound())) { + throw SQLError.createSQLException( + "The connection property '" + getPropertyName() + "' only accepts long integer values in the range of " + getLowerBound() + " - " + + getUpperBound() + ", the value '" + (valueAsString == null ? longValue : valueAsString) + "' exceeds this range.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + this.valueAsObject = Long.valueOf(longValue); + this.wasExplicitlySet = true; + this.updateCount++; + } + + long getValueAsLong() { + return ((Long) this.valueAsObject).longValue(); + } + + @Override + void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (extractedValue != null) { + try { + // Parse decimals, too + long longValue = Double.valueOf(extractedValue).longValue(); + + setValue(longValue, extractedValue, exceptionInterceptor); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException("The connection property '" + getPropertyName() + "' only accepts long integer values. The value '" + + extractedValue + "' can not be converted to a long integer.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } else { + this.valueAsObject = this.defaultValue; + } + this.updateCount++; + } + } + + static class MemorySizeConnectionProperty extends IntegerConnectionProperty implements Serializable { + + private static final long serialVersionUID = 7351065128998572656L; + + private String valueAsString; + + MemorySizeConnectionProperty(String propertyNameToSet, int defaultValueToSet, int lowerBoundToSet, int upperBoundToSet, String descriptionToSet, + String sinceVersionToSet, String category, int orderInCategory) { + super(propertyNameToSet, defaultValueToSet, lowerBoundToSet, upperBoundToSet, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + @Override + void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + this.valueAsString = extractedValue; + this.multiplier = 1; + + if (extractedValue != null) { + if (extractedValue.endsWith("k") || extractedValue.endsWith("K") || extractedValue.endsWith("kb") || extractedValue.endsWith("Kb") + || extractedValue.endsWith("kB") || extractedValue.endsWith("KB")) { + this.multiplier = 1024; + int indexOfK = StringUtils.indexOfIgnoreCase(extractedValue, "k"); + extractedValue = extractedValue.substring(0, indexOfK); + } else if (extractedValue.endsWith("m") || extractedValue.endsWith("M") || extractedValue.endsWith("mb") || extractedValue.endsWith("Mb") + || extractedValue.endsWith("mB") || extractedValue.endsWith("MB")) { + this.multiplier = 1024 * 1024; + int indexOfM = StringUtils.indexOfIgnoreCase(extractedValue, "m"); + extractedValue = extractedValue.substring(0, indexOfM); + } else if (extractedValue.endsWith("g") || extractedValue.endsWith("G") || extractedValue.endsWith("gb") || extractedValue.endsWith("Gb") + || extractedValue.endsWith("gB") || extractedValue.endsWith("GB")) { + this.multiplier = 1024 * 1024 * 1024; + int indexOfG = StringUtils.indexOfIgnoreCase(extractedValue, "g"); + extractedValue = extractedValue.substring(0, indexOfG); + } + } + + super.initializeFrom(extractedValue, exceptionInterceptor); + } + + void setValue(String value, ExceptionInterceptor exceptionInterceptor) throws SQLException { + initializeFrom(value, exceptionInterceptor); + } + + String getValueAsString() { + return this.valueAsString; + } + } + + static class StringConnectionProperty extends ConnectionProperty implements Serializable { + + private static final long serialVersionUID = 5432127962785948272L; + + StringConnectionProperty(String propertyNameToSet, String defaultValueToSet, String descriptionToSet, String sinceVersionToSet, String category, + int orderInCategory) { + this(propertyNameToSet, defaultValueToSet, null, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + /** + * @param propertyNameToSet + * @param defaultValueToSet + * @param allowableValuesToSet + * @param descriptionToSet + * @param sinceVersionToSet + */ + StringConnectionProperty(String propertyNameToSet, String defaultValueToSet, String[] allowableValuesToSet, String descriptionToSet, + String sinceVersionToSet, String category, int orderInCategory) { + super(propertyNameToSet, defaultValueToSet, allowableValuesToSet, 0, 0, descriptionToSet, sinceVersionToSet, category, orderInCategory); + } + + String getValueAsString() { + return (String) this.valueAsObject; + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#hasValueConstraints() + */ + @Override + boolean hasValueConstraints() { + return (this.allowableValues != null) && (this.allowableValues.length > 0); + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#initializeFrom(java.util.Properties) + */ + @Override + void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (extractedValue != null) { + validateStringValues(extractedValue, exceptionInterceptor); + + this.valueAsObject = extractedValue; + this.wasExplicitlySet = true; + } else { + this.valueAsObject = this.defaultValue; + } + this.updateCount++; + } + + /** + * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#isRangeBased() + */ + @Override + boolean isRangeBased() { + return false; + } + + void setValue(String valueFlag) { + this.valueAsObject = valueFlag; + this.wasExplicitlySet = true; + this.updateCount++; + } + } + + private static final String CONNECTION_AND_AUTH_CATEGORY = Messages.getString("ConnectionProperties.categoryConnectionAuthentication"); + + private static final String NETWORK_CATEGORY = Messages.getString("ConnectionProperties.categoryNetworking"); + + private static final String DEBUGING_PROFILING_CATEGORY = Messages.getString("ConnectionProperties.categoryDebuggingProfiling"); + + private static final String HA_CATEGORY = Messages.getString("ConnectionProperties.categorryHA"); + + private static final String MISC_CATEGORY = Messages.getString("ConnectionProperties.categoryMisc"); + + private static final String PERFORMANCE_CATEGORY = Messages.getString("ConnectionProperties.categoryPerformance"); + + private static final String SECURITY_CATEGORY = Messages.getString("ConnectionProperties.categorySecurity"); + + private static final String[] PROPERTY_CATEGORIES = new String[] { CONNECTION_AND_AUTH_CATEGORY, NETWORK_CATEGORY, HA_CATEGORY, SECURITY_CATEGORY, + PERFORMANCE_CATEGORY, DEBUGING_PROFILING_CATEGORY, MISC_CATEGORY }; + + private static final ArrayList PROPERTY_LIST = new ArrayList(); + + // + // Yes, this looks goofy, but we're trying to avoid intern()ing here + // + private static final String STANDARD_LOGGER_NAME = StandardLogger.class.getName(); + + protected static final String ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL = "convertToNull"; + + protected static final String ZERO_DATETIME_BEHAVIOR_EXCEPTION = "exception"; + + protected static final String ZERO_DATETIME_BEHAVIOR_ROUND = "round"; + + static { + try { + java.lang.reflect.Field[] declaredFields = ConnectionPropertiesImpl.class.getDeclaredFields(); + + for (int i = 0; i < declaredFields.length; i++) { + if (ConnectionPropertiesImpl.ConnectionProperty.class.isAssignableFrom(declaredFields[i].getType())) { + PROPERTY_LIST.add(declaredFields[i]); + } + } + } catch (Exception ex) { + RuntimeException rtEx = new RuntimeException(); + rtEx.initCause(ex); + + throw rtEx; + } + } + + public ExceptionInterceptor getExceptionInterceptor() { + return null; + } + + /** + * Exposes all ConnectionPropertyInfo instances as DriverPropertyInfo + * + * @param info + * the properties to load into these ConnectionPropertyInfo + * instances + * @param slotsToReserve + * the number of DPI slots to reserve for 'standard' DPI + * properties (user, host, password, etc) + * @return a list of all ConnectionPropertyInfo instances, as + * DriverPropertyInfo + * @throws SQLException + * if an error occurs + */ + protected static DriverPropertyInfo[] exposeAsDriverPropertyInfo(Properties info, int slotsToReserve) throws SQLException { + return (new ConnectionPropertiesImpl() { + private static final long serialVersionUID = 4257801713007640581L; + }).exposeAsDriverPropertyInfoInternal(info, slotsToReserve); + } + + private BooleanConnectionProperty allowLoadLocalInfile = new BooleanConnectionProperty("allowLoadLocalInfile", true, + Messages.getString("ConnectionProperties.loadDataLocal"), "3.0.3", SECURITY_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty allowMultiQueries = new BooleanConnectionProperty("allowMultiQueries", false, + Messages.getString("ConnectionProperties.allowMultiQueries"), "3.1.1", SECURITY_CATEGORY, 1); + + private BooleanConnectionProperty allowNanAndInf = new BooleanConnectionProperty("allowNanAndInf", false, + Messages.getString("ConnectionProperties.allowNANandINF"), "3.1.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty allowUrlInLocalInfile = new BooleanConnectionProperty("allowUrlInLocalInfile", false, + Messages.getString("ConnectionProperties.allowUrlInLoadLocal"), "3.1.4", SECURITY_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty alwaysSendSetIsolation = new BooleanConnectionProperty("alwaysSendSetIsolation", true, + Messages.getString("ConnectionProperties.alwaysSendSetIsolation"), "3.1.7", PERFORMANCE_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty autoClosePStmtStreams = new BooleanConnectionProperty("autoClosePStmtStreams", false, + Messages.getString("ConnectionProperties.autoClosePstmtStreams"), "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty allowMasterDownConnections = new BooleanConnectionProperty("allowMasterDownConnections", false, + Messages.getString("ConnectionProperties.allowMasterDownConnections"), "5.1.27", HA_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty allowSlaveDownConnections = new BooleanConnectionProperty("allowSlaveDownConnections", false, + Messages.getString("ConnectionProperties.allowSlaveDownConnections"), "5.1.38", HA_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty readFromMasterWhenNoSlaves = new BooleanConnectionProperty("readFromMasterWhenNoSlaves", false, + Messages.getString("ConnectionProperties.readFromMasterWhenNoSlaves"), "5.1.38", HA_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty autoDeserialize = new BooleanConnectionProperty("autoDeserialize", false, + Messages.getString("ConnectionProperties.autoDeserialize"), "3.1.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty autoGenerateTestcaseScript = new BooleanConnectionProperty("autoGenerateTestcaseScript", false, + Messages.getString("ConnectionProperties.autoGenerateTestcaseScript"), "3.1.9", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private boolean autoGenerateTestcaseScriptAsBoolean = false; + + private BooleanConnectionProperty autoReconnect = new BooleanConnectionProperty("autoReconnect", false, + Messages.getString("ConnectionProperties.autoReconnect"), "1.1", HA_CATEGORY, 0); + + private BooleanConnectionProperty autoReconnectForPools = new BooleanConnectionProperty("autoReconnectForPools", false, + Messages.getString("ConnectionProperties.autoReconnectForPools"), "3.1.3", HA_CATEGORY, 1); + + private boolean autoReconnectForPoolsAsBoolean = false; + + private MemorySizeConnectionProperty blobSendChunkSize = new MemorySizeConnectionProperty("blobSendChunkSize", 1024 * 1024, 0, 0, + Messages.getString("ConnectionProperties.blobSendChunkSize"), "3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty autoSlowLog = new BooleanConnectionProperty("autoSlowLog", true, Messages.getString("ConnectionProperties.autoSlowLog"), + "5.1.4", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty blobsAreStrings = new BooleanConnectionProperty("blobsAreStrings", false, + "Should the driver always treat BLOBs as Strings - specifically to work around dubious metadata returned by the server for GROUP BY clauses?", + "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty functionsNeverReturnBlobs = new BooleanConnectionProperty("functionsNeverReturnBlobs", false, + "Should the driver always treat data from functions returning BLOBs as Strings - specifically to work around dubious metadata " + + "returned by the server for GROUP BY clauses?", + "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty cacheCallableStatements = new BooleanConnectionProperty("cacheCallableStmts", false, + Messages.getString("ConnectionProperties.cacheCallableStatements"), "3.1.2", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty cachePreparedStatements = new BooleanConnectionProperty("cachePrepStmts", false, + Messages.getString("ConnectionProperties.cachePrepStmts"), "3.0.10", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty cacheResultSetMetadata = new BooleanConnectionProperty("cacheResultSetMetadata", false, + Messages.getString("ConnectionProperties.cacheRSMetadata"), "3.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private boolean cacheResultSetMetaDataAsBoolean; + + private StringConnectionProperty serverConfigCacheFactory = new StringConnectionProperty("serverConfigCacheFactory", + PerVmServerConfigCacheFactory.class.getName(), Messages.getString("ConnectionProperties.serverConfigCacheFactory"), "5.1.1", PERFORMANCE_CATEGORY, + 12); + + private BooleanConnectionProperty cacheServerConfiguration = new BooleanConnectionProperty("cacheServerConfiguration", false, + Messages.getString("ConnectionProperties.cacheServerConfiguration"), "3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty callableStatementCacheSize = new IntegerConnectionProperty("callableStmtCacheSize", 100, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.callableStmtCacheSize"), "3.1.2", PERFORMANCE_CATEGORY, 5); + + private BooleanConnectionProperty capitalizeTypeNames = new BooleanConnectionProperty("capitalizeTypeNames", true, + Messages.getString("ConnectionProperties.capitalizeTypeNames"), "2.0.7", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty characterEncoding = new StringConnectionProperty("characterEncoding", null, + Messages.getString("ConnectionProperties.characterEncoding"), "1.1g", MISC_CATEGORY, 5); + + private String characterEncodingAsString = null; + + protected boolean characterEncodingIsAliasForSjis = false; + + private StringConnectionProperty characterSetResults = new StringConnectionProperty("characterSetResults", null, + Messages.getString("ConnectionProperties.characterSetResults"), "3.0.13", MISC_CATEGORY, 6); + + private StringConnectionProperty connectionAttributes = new StringConnectionProperty("connectionAttributes", null, + Messages.getString("ConnectionProperties.connectionAttributes"), "5.1.25", MISC_CATEGORY, 7); + + private StringConnectionProperty clientInfoProvider = new StringConnectionProperty("clientInfoProvider", "com.mysql.jdbc.JDBC4CommentClientInfoProvider", + Messages.getString("ConnectionProperties.clientInfoProvider"), "5.1.0", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty clobberStreamingResults = new BooleanConnectionProperty("clobberStreamingResults", false, + Messages.getString("ConnectionProperties.clobberStreamingResults"), "3.0.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty clobCharacterEncoding = new StringConnectionProperty("clobCharacterEncoding", null, + Messages.getString("ConnectionProperties.clobCharacterEncoding"), "5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty compensateOnDuplicateKeyUpdateCounts = new BooleanConnectionProperty("compensateOnDuplicateKeyUpdateCounts", false, + Messages.getString("ConnectionProperties.compensateOnDuplicateKeyUpdateCounts"), "5.1.7", MISC_CATEGORY, Integer.MIN_VALUE); + private StringConnectionProperty connectionCollation = new StringConnectionProperty("connectionCollation", null, + Messages.getString("ConnectionProperties.connectionCollation"), "3.0.13", MISC_CATEGORY, 7); + + private StringConnectionProperty connectionLifecycleInterceptors = new StringConnectionProperty("connectionLifecycleInterceptors", null, + Messages.getString("ConnectionProperties.connectionLifecycleInterceptors"), "5.1.4", CONNECTION_AND_AUTH_CATEGORY, Integer.MAX_VALUE); + + private IntegerConnectionProperty connectTimeout = new IntegerConnectionProperty("connectTimeout", 0, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.connectTimeout"), "3.0.1", CONNECTION_AND_AUTH_CATEGORY, 9); + + private BooleanConnectionProperty continueBatchOnError = new BooleanConnectionProperty("continueBatchOnError", true, + Messages.getString("ConnectionProperties.continueBatchOnError"), "3.0.3", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty createDatabaseIfNotExist = new BooleanConnectionProperty("createDatabaseIfNotExist", false, + Messages.getString("ConnectionProperties.createDatabaseIfNotExist"), "3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty defaultFetchSize = new IntegerConnectionProperty("defaultFetchSize", 0, + Messages.getString("ConnectionProperties.defaultFetchSize"), "3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + // Think really long and hard about changing the default for this many, many applications have come to be acustomed to the latency profile of preparing + // stuff client-side, rather than prepare (round-trip), execute (round-trip), close (round-trip). + private BooleanConnectionProperty detectServerPreparedStmts = new BooleanConnectionProperty("useServerPrepStmts", false, + Messages.getString("ConnectionProperties.useServerPrepStmts"), "3.1.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty dontTrackOpenResources = new BooleanConnectionProperty("dontTrackOpenResources", false, + Messages.getString("ConnectionProperties.dontTrackOpenResources"), "3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty dumpQueriesOnException = new BooleanConnectionProperty("dumpQueriesOnException", false, + Messages.getString("ConnectionProperties.dumpQueriesOnException"), "3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty dynamicCalendars = new BooleanConnectionProperty("dynamicCalendars", false, + Messages.getString("ConnectionProperties.dynamicCalendars"), "3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty elideSetAutoCommits = new BooleanConnectionProperty("elideSetAutoCommits", false, + Messages.getString("ConnectionProperties.eliseSetAutoCommit"), "3.1.3", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty emptyStringsConvertToZero = new BooleanConnectionProperty("emptyStringsConvertToZero", true, + Messages.getString("ConnectionProperties.emptyStringsConvertToZero"), "3.1.8", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty emulateLocators = new BooleanConnectionProperty("emulateLocators", false, + Messages.getString("ConnectionProperties.emulateLocators"), "3.1.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty emulateUnsupportedPstmts = new BooleanConnectionProperty("emulateUnsupportedPstmts", true, + Messages.getString("ConnectionProperties.emulateUnsupportedPstmts"), "3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty enablePacketDebug = new BooleanConnectionProperty("enablePacketDebug", false, + Messages.getString("ConnectionProperties.enablePacketDebug"), "3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty enableQueryTimeouts = new BooleanConnectionProperty("enableQueryTimeouts", true, + Messages.getString("ConnectionProperties.enableQueryTimeouts"), "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty explainSlowQueries = new BooleanConnectionProperty("explainSlowQueries", false, + Messages.getString("ConnectionProperties.explainSlowQueries"), "3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty exceptionInterceptors = new StringConnectionProperty("exceptionInterceptors", null, + Messages.getString("ConnectionProperties.exceptionInterceptors"), "5.1.8", MISC_CATEGORY, Integer.MIN_VALUE); + + /** When failed-over, set connection to read-only? */ + private BooleanConnectionProperty failOverReadOnly = new BooleanConnectionProperty("failOverReadOnly", true, + Messages.getString("ConnectionProperties.failoverReadOnly"), "3.0.12", HA_CATEGORY, 2); + + private BooleanConnectionProperty gatherPerformanceMetrics = new BooleanConnectionProperty("gatherPerfMetrics", false, + Messages.getString("ConnectionProperties.gatherPerfMetrics"), "3.1.2", DEBUGING_PROFILING_CATEGORY, 1); + + private BooleanConnectionProperty generateSimpleParameterMetadata = new BooleanConnectionProperty("generateSimpleParameterMetadata", false, + Messages.getString("ConnectionProperties.generateSimpleParameterMetadata"), "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private boolean highAvailabilityAsBoolean = false; + + private BooleanConnectionProperty holdResultsOpenOverStatementClose = new BooleanConnectionProperty("holdResultsOpenOverStatementClose", false, + Messages.getString("ConnectionProperties.holdRSOpenOverStmtClose"), "3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty includeInnodbStatusInDeadlockExceptions = new BooleanConnectionProperty("includeInnodbStatusInDeadlockExceptions", false, + Messages.getString("ConnectionProperties.includeInnodbStatusInDeadlockExceptions"), "5.0.7", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty includeThreadDumpInDeadlockExceptions = new BooleanConnectionProperty("includeThreadDumpInDeadlockExceptions", false, + Messages.getString("ConnectionProperties.includeThreadDumpInDeadlockExceptions"), "5.1.15", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty includeThreadNamesAsStatementComment = new BooleanConnectionProperty("includeThreadNamesAsStatementComment", false, + Messages.getString("ConnectionProperties.includeThreadNamesAsStatementComment"), "5.1.15", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty ignoreNonTxTables = new BooleanConnectionProperty("ignoreNonTxTables", false, + Messages.getString("ConnectionProperties.ignoreNonTxTables"), "3.0.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty initialTimeout = new IntegerConnectionProperty("initialTimeout", 2, 1, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.initialTimeout"), "1.1", HA_CATEGORY, 5); + + private BooleanConnectionProperty isInteractiveClient = new BooleanConnectionProperty("interactiveClient", false, + Messages.getString("ConnectionProperties.interactiveClient"), "3.1.0", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty jdbcCompliantTruncation = new BooleanConnectionProperty("jdbcCompliantTruncation", true, + Messages.getString("ConnectionProperties.jdbcCompliantTruncation"), "3.1.2", MISC_CATEGORY, Integer.MIN_VALUE); + + private boolean jdbcCompliantTruncationForReads = this.jdbcCompliantTruncation.getValueAsBoolean(); + + protected MemorySizeConnectionProperty largeRowSizeThreshold = new MemorySizeConnectionProperty("largeRowSizeThreshold", 2048, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.largeRowSizeThreshold"), "5.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loadBalanceStrategy = new StringConnectionProperty("loadBalanceStrategy", "random", null, + Messages.getString("ConnectionProperties.loadBalanceStrategy"), "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty loadBalanceBlacklistTimeout = new IntegerConnectionProperty("loadBalanceBlacklistTimeout", 0, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.loadBalanceBlacklistTimeout"), "5.1.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty loadBalancePingTimeout = new IntegerConnectionProperty("loadBalancePingTimeout", 0, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.loadBalancePingTimeout"), "5.1.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty loadBalanceValidateConnectionOnSwapServer = new BooleanConnectionProperty("loadBalanceValidateConnectionOnSwapServer", + false, Messages.getString("ConnectionProperties.loadBalanceValidateConnectionOnSwapServer"), "5.1.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loadBalanceConnectionGroup = new StringConnectionProperty("loadBalanceConnectionGroup", null, + Messages.getString("ConnectionProperties.loadBalanceConnectionGroup"), "5.1.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loadBalanceExceptionChecker = new StringConnectionProperty("loadBalanceExceptionChecker", + "com.mysql.jdbc.StandardLoadBalanceExceptionChecker", null, Messages.getString("ConnectionProperties.loadBalanceExceptionChecker"), "5.1.13", + MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loadBalanceSQLStateFailover = new StringConnectionProperty("loadBalanceSQLStateFailover", null, + Messages.getString("ConnectionProperties.loadBalanceSQLStateFailover"), "5.1.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loadBalanceSQLExceptionSubclassFailover = new StringConnectionProperty("loadBalanceSQLExceptionSubclassFailover", null, + Messages.getString("ConnectionProperties.loadBalanceSQLExceptionSubclassFailover"), "5.1.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty loadBalanceEnableJMX = new BooleanConnectionProperty("loadBalanceEnableJMX", false, + Messages.getString("ConnectionProperties.loadBalanceEnableJMX"), "5.1.13", MISC_CATEGORY, Integer.MAX_VALUE); + + private IntegerConnectionProperty loadBalanceHostRemovalGracePeriod = new IntegerConnectionProperty("loadBalanceHostRemovalGracePeriod", 15000, 0, + Integer.MAX_VALUE, Messages.getString("ConnectionProperties.loadBalanceHostRemovalGracePeriod"), "5.1.39", MISC_CATEGORY, Integer.MAX_VALUE); + + private StringConnectionProperty loadBalanceAutoCommitStatementRegex = new StringConnectionProperty("loadBalanceAutoCommitStatementRegex", null, + Messages.getString("ConnectionProperties.loadBalanceAutoCommitStatementRegex"), "5.1.15", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty loadBalanceAutoCommitStatementThreshold = new IntegerConnectionProperty("loadBalanceAutoCommitStatementThreshold", 0, 0, + Integer.MAX_VALUE, Messages.getString("ConnectionProperties.loadBalanceAutoCommitStatementThreshold"), "5.1.15", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty localSocketAddress = new StringConnectionProperty("localSocketAddress", null, + Messages.getString("ConnectionProperties.localSocketAddress"), "5.0.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private MemorySizeConnectionProperty locatorFetchBufferSize = new MemorySizeConnectionProperty("locatorFetchBufferSize", 1024 * 1024, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.locatorFetchBufferSize"), "3.2.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty loggerClassName = new StringConnectionProperty("logger", STANDARD_LOGGER_NAME, + Messages.getString("ConnectionProperties.logger", new Object[] { Log.class.getName(), STANDARD_LOGGER_NAME }), "3.1.1", DEBUGING_PROFILING_CATEGORY, + 0); + + private BooleanConnectionProperty logSlowQueries = new BooleanConnectionProperty("logSlowQueries", false, + Messages.getString("ConnectionProperties.logSlowQueries"), "3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty logXaCommands = new BooleanConnectionProperty("logXaCommands", false, + Messages.getString("ConnectionProperties.logXaCommands"), "5.0.5", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty maintainTimeStats = new BooleanConnectionProperty("maintainTimeStats", true, + Messages.getString("ConnectionProperties.maintainTimeStats"), "3.1.9", PERFORMANCE_CATEGORY, Integer.MAX_VALUE); + + private boolean maintainTimeStatsAsBoolean = true; + + private IntegerConnectionProperty maxQuerySizeToLog = new IntegerConnectionProperty("maxQuerySizeToLog", 2048, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.maxQuerySizeToLog"), "3.1.3", DEBUGING_PROFILING_CATEGORY, 4); + + private IntegerConnectionProperty maxReconnects = new IntegerConnectionProperty("maxReconnects", 3, 1, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.maxReconnects"), "1.1", HA_CATEGORY, 4); + + private IntegerConnectionProperty retriesAllDown = new IntegerConnectionProperty("retriesAllDown", 120, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.retriesAllDown"), "5.1.6", HA_CATEGORY, 4); + + private IntegerConnectionProperty maxRows = new IntegerConnectionProperty("maxRows", -1, -1, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.maxRows"), Messages.getString("ConnectionProperties.allVersions"), MISC_CATEGORY, Integer.MIN_VALUE); + + private int maxRowsAsInt = -1; + + private IntegerConnectionProperty metadataCacheSize = new IntegerConnectionProperty("metadataCacheSize", 50, 1, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.metadataCacheSize"), "3.1.1", PERFORMANCE_CATEGORY, 5); + + private IntegerConnectionProperty netTimeoutForStreamingResults = new IntegerConnectionProperty("netTimeoutForStreamingResults", 600, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.netTimeoutForStreamingResults"), "5.1.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty noAccessToProcedureBodies = new BooleanConnectionProperty("noAccessToProcedureBodies", false, + "When determining procedure parameter types for CallableStatements, and the connected user " + + " can't access procedure bodies through \"SHOW CREATE PROCEDURE\" or select on mysql.proc " + + " should the driver instead create basic metadata (all parameters reported as IN VARCHARs," + + " but allowing registerOutParameter() to be called on them anyway) instead of throwing an exception?", + "5.0.3", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty noDatetimeStringSync = new BooleanConnectionProperty("noDatetimeStringSync", false, + Messages.getString("ConnectionProperties.noDatetimeStringSync"), "3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty noTimezoneConversionForTimeType = new BooleanConnectionProperty("noTimezoneConversionForTimeType", false, + Messages.getString("ConnectionProperties.noTzConversionForTimeType"), "5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty noTimezoneConversionForDateType = new BooleanConnectionProperty("noTimezoneConversionForDateType", true, + Messages.getString("ConnectionProperties.noTzConversionForDateType"), "5.1.35", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty cacheDefaultTimezone = new BooleanConnectionProperty("cacheDefaultTimezone", true, + Messages.getString("ConnectionProperties.cacheDefaultTimezone"), "5.1.35", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty nullCatalogMeansCurrent = new BooleanConnectionProperty("nullCatalogMeansCurrent", true, + Messages.getString("ConnectionProperties.nullCatalogMeansCurrent"), "3.1.8", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty nullNamePatternMatchesAll = new BooleanConnectionProperty("nullNamePatternMatchesAll", true, + Messages.getString("ConnectionProperties.nullNamePatternMatchesAll"), "3.1.8", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty packetDebugBufferSize = new IntegerConnectionProperty("packetDebugBufferSize", 20, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.packetDebugBufferSize"), "3.1.3", DEBUGING_PROFILING_CATEGORY, 7); + + private BooleanConnectionProperty padCharsWithSpace = new BooleanConnectionProperty("padCharsWithSpace", false, + Messages.getString("ConnectionProperties.padCharsWithSpace"), "5.0.6", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty paranoid = new BooleanConnectionProperty("paranoid", false, Messages.getString("ConnectionProperties.paranoid"), "3.0.1", + SECURITY_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty pedantic = new BooleanConnectionProperty("pedantic", false, Messages.getString("ConnectionProperties.pedantic"), "3.0.0", + MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty pinGlobalTxToPhysicalConnection = new BooleanConnectionProperty("pinGlobalTxToPhysicalConnection", false, + Messages.getString("ConnectionProperties.pinGlobalTxToPhysicalConnection"), "5.0.1", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty populateInsertRowWithDefaultValues = new BooleanConnectionProperty("populateInsertRowWithDefaultValues", false, + Messages.getString("ConnectionProperties.populateInsertRowWithDefaultValues"), "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty("prepStmtCacheSize", 25, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.prepStmtCacheSize"), "3.0.10", PERFORMANCE_CATEGORY, 10); + + private IntegerConnectionProperty preparedStatementCacheSqlLimit = new IntegerConnectionProperty("prepStmtCacheSqlLimit", 256, 1, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.prepStmtCacheSqlLimit"), "3.0.10", PERFORMANCE_CATEGORY, 11); + + private StringConnectionProperty parseInfoCacheFactory = new StringConnectionProperty("parseInfoCacheFactory", PerConnectionLRUFactory.class.getName(), + Messages.getString("ConnectionProperties.parseInfoCacheFactory"), "5.1.1", PERFORMANCE_CATEGORY, 12); + + private BooleanConnectionProperty processEscapeCodesForPrepStmts = new BooleanConnectionProperty("processEscapeCodesForPrepStmts", true, + Messages.getString("ConnectionProperties.processEscapeCodesForPrepStmts"), "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty profilerEventHandler = new StringConnectionProperty("profilerEventHandler", + "com.mysql.jdbc.profiler.LoggingProfilerEventHandler", Messages.getString("ConnectionProperties.profilerEventHandler"), "5.1.6", + DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty profileSql = new StringConnectionProperty("profileSql", null, + Messages.getString("ConnectionProperties.profileSqlDeprecated"), "2.0.14", DEBUGING_PROFILING_CATEGORY, 3); + + private BooleanConnectionProperty profileSQL = new BooleanConnectionProperty("profileSQL", false, Messages.getString("ConnectionProperties.profileSQL"), + "3.1.0", DEBUGING_PROFILING_CATEGORY, 1); + + private boolean profileSQLAsBoolean = false; + + private StringConnectionProperty propertiesTransform = new StringConnectionProperty(NonRegisteringDriver.PROPERTIES_TRANSFORM_KEY, null, + Messages.getString("ConnectionProperties.connectionPropertiesTransform"), "3.1.4", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty queriesBeforeRetryMaster = new IntegerConnectionProperty("queriesBeforeRetryMaster", 50, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.queriesBeforeRetryMaster"), "3.0.2", HA_CATEGORY, 7); + + private BooleanConnectionProperty queryTimeoutKillsConnection = new BooleanConnectionProperty("queryTimeoutKillsConnection", false, + Messages.getString("ConnectionProperties.queryTimeoutKillsConnection"), "5.1.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty reconnectAtTxEnd = new BooleanConnectionProperty("reconnectAtTxEnd", false, + Messages.getString("ConnectionProperties.reconnectAtTxEnd"), "3.0.10", HA_CATEGORY, 4); + + private boolean reconnectTxAtEndAsBoolean = false; + + private BooleanConnectionProperty relaxAutoCommit = new BooleanConnectionProperty("relaxAutoCommit", false, + Messages.getString("ConnectionProperties.relaxAutoCommit"), "2.0.13", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty reportMetricsIntervalMillis = new IntegerConnectionProperty("reportMetricsIntervalMillis", 30000, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.reportMetricsIntervalMillis"), "3.1.2", DEBUGING_PROFILING_CATEGORY, 3); + + private BooleanConnectionProperty requireSSL = new BooleanConnectionProperty("requireSSL", false, Messages.getString("ConnectionProperties.requireSSL"), + "3.1.0", SECURITY_CATEGORY, 3); + + private StringConnectionProperty resourceId = new StringConnectionProperty("resourceId", null, Messages.getString("ConnectionProperties.resourceId"), + "5.0.1", HA_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty resultSetSizeThreshold = new IntegerConnectionProperty("resultSetSizeThreshold", 100, + Messages.getString("ConnectionProperties.resultSetSizeThreshold"), "5.0.5", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty retainStatementAfterResultSetClose = new BooleanConnectionProperty("retainStatementAfterResultSetClose", false, + Messages.getString("ConnectionProperties.retainStatementAfterResultSetClose"), "3.1.11", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty rewriteBatchedStatements = new BooleanConnectionProperty("rewriteBatchedStatements", false, + Messages.getString("ConnectionProperties.rewriteBatchedStatements"), "3.1.13", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty rollbackOnPooledClose = new BooleanConnectionProperty("rollbackOnPooledClose", true, + Messages.getString("ConnectionProperties.rollbackOnPooledClose"), "3.0.15", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty roundRobinLoadBalance = new BooleanConnectionProperty("roundRobinLoadBalance", false, + Messages.getString("ConnectionProperties.roundRobinLoadBalance"), "3.1.2", HA_CATEGORY, 5); + + private BooleanConnectionProperty runningCTS13 = new BooleanConnectionProperty("runningCTS13", false, + Messages.getString("ConnectionProperties.runningCTS13"), "3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty secondsBeforeRetryMaster = new IntegerConnectionProperty("secondsBeforeRetryMaster", 30, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.secondsBeforeRetryMaster"), "3.0.2", HA_CATEGORY, 8); + + private IntegerConnectionProperty selfDestructOnPingSecondsLifetime = new IntegerConnectionProperty("selfDestructOnPingSecondsLifetime", 0, 0, + Integer.MAX_VALUE, Messages.getString("ConnectionProperties.selfDestructOnPingSecondsLifetime"), "5.1.6", HA_CATEGORY, Integer.MAX_VALUE); + + private IntegerConnectionProperty selfDestructOnPingMaxOperations = new IntegerConnectionProperty("selfDestructOnPingMaxOperations", 0, 0, + Integer.MAX_VALUE, Messages.getString("ConnectionProperties.selfDestructOnPingMaxOperations"), "5.1.6", HA_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty replicationEnableJMX = new BooleanConnectionProperty("replicationEnableJMX", false, + Messages.getString("ConnectionProperties.loadBalanceEnableJMX"), "5.1.27", HA_CATEGORY, Integer.MAX_VALUE); + + private StringConnectionProperty serverTimezone = new StringConnectionProperty("serverTimezone", null, + Messages.getString("ConnectionProperties.serverTimezone"), "3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty sessionVariables = new StringConnectionProperty("sessionVariables", null, + Messages.getString("ConnectionProperties.sessionVariables"), "3.1.8", MISC_CATEGORY, Integer.MAX_VALUE); + + private IntegerConnectionProperty slowQueryThresholdMillis = new IntegerConnectionProperty("slowQueryThresholdMillis", 2000, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.slowQueryThresholdMillis"), "3.1.2", DEBUGING_PROFILING_CATEGORY, 9); + + private LongConnectionProperty slowQueryThresholdNanos = new LongConnectionProperty("slowQueryThresholdNanos", 0, + Messages.getString("ConnectionProperties.slowQueryThresholdNanos"), "5.0.7", DEBUGING_PROFILING_CATEGORY, 10); + + private StringConnectionProperty socketFactoryClassName = new StringConnectionProperty("socketFactory", StandardSocketFactory.class.getName(), + Messages.getString("ConnectionProperties.socketFactory"), "3.0.3", CONNECTION_AND_AUTH_CATEGORY, 4); + + private StringConnectionProperty socksProxyHost = new StringConnectionProperty("socksProxyHost", null, + Messages.getString("ConnectionProperties.socksProxyHost"), "5.1.34", NETWORK_CATEGORY, 1); + + private IntegerConnectionProperty socksProxyPort = new IntegerConnectionProperty("socksProxyPort", SocksProxySocketFactory.SOCKS_DEFAULT_PORT, 0, 65535, + Messages.getString("ConnectionProperties.socksProxyPort"), "5.1.34", NETWORK_CATEGORY, 2); + + private IntegerConnectionProperty socketTimeout = new IntegerConnectionProperty("socketTimeout", 0, 0, Integer.MAX_VALUE, + Messages.getString("ConnectionProperties.socketTimeout"), "3.0.1", CONNECTION_AND_AUTH_CATEGORY, 10); + + private StringConnectionProperty statementInterceptors = new StringConnectionProperty("statementInterceptors", null, + Messages.getString("ConnectionProperties.statementInterceptors"), "5.1.1", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty strictFloatingPoint = new BooleanConnectionProperty("strictFloatingPoint", false, + Messages.getString("ConnectionProperties.strictFloatingPoint"), "3.0.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty strictUpdates = new BooleanConnectionProperty("strictUpdates", true, + Messages.getString("ConnectionProperties.strictUpdates"), "3.0.4", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty overrideSupportsIntegrityEnhancementFacility = new BooleanConnectionProperty( + "overrideSupportsIntegrityEnhancementFacility", false, Messages.getString("ConnectionProperties.overrideSupportsIEF"), "3.1.12", MISC_CATEGORY, + Integer.MIN_VALUE); + + private BooleanConnectionProperty tcpNoDelay = new BooleanConnectionProperty(StandardSocketFactory.TCP_NO_DELAY_PROPERTY_NAME, + Boolean.valueOf(StandardSocketFactory.TCP_NO_DELAY_DEFAULT_VALUE).booleanValue(), Messages.getString("ConnectionProperties.tcpNoDelay"), "5.0.7", + NETWORK_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty tcpKeepAlive = new BooleanConnectionProperty(StandardSocketFactory.TCP_KEEP_ALIVE_PROPERTY_NAME, + Boolean.valueOf(StandardSocketFactory.TCP_KEEP_ALIVE_DEFAULT_VALUE).booleanValue(), Messages.getString("ConnectionProperties.tcpKeepAlive"), + "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty tcpRcvBuf = new IntegerConnectionProperty(StandardSocketFactory.TCP_RCV_BUF_PROPERTY_NAME, + Integer.parseInt(StandardSocketFactory.TCP_RCV_BUF_DEFAULT_VALUE), 0, Integer.MAX_VALUE, Messages.getString("ConnectionProperties.tcpSoRcvBuf"), + "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty tcpSndBuf = new IntegerConnectionProperty(StandardSocketFactory.TCP_SND_BUF_PROPERTY_NAME, + Integer.parseInt(StandardSocketFactory.TCP_SND_BUF_DEFAULT_VALUE), 0, Integer.MAX_VALUE, Messages.getString("ConnectionProperties.tcpSoSndBuf"), + "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty tcpTrafficClass = new IntegerConnectionProperty(StandardSocketFactory.TCP_TRAFFIC_CLASS_PROPERTY_NAME, + Integer.parseInt(StandardSocketFactory.TCP_TRAFFIC_CLASS_DEFAULT_VALUE), 0, 255, Messages.getString("ConnectionProperties.tcpTrafficClass"), + "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty tinyInt1isBit = new BooleanConnectionProperty("tinyInt1isBit", true, + Messages.getString("ConnectionProperties.tinyInt1isBit"), "3.0.16", MISC_CATEGORY, Integer.MIN_VALUE); + + protected BooleanConnectionProperty traceProtocol = new BooleanConnectionProperty("traceProtocol", false, + Messages.getString("ConnectionProperties.traceProtocol"), "3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty treatUtilDateAsTimestamp = new BooleanConnectionProperty("treatUtilDateAsTimestamp", true, + Messages.getString("ConnectionProperties.treatUtilDateAsTimestamp"), "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty transformedBitIsBoolean = new BooleanConnectionProperty("transformedBitIsBoolean", false, + Messages.getString("ConnectionProperties.transformedBitIsBoolean"), "3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useBlobToStoreUTF8OutsideBMP = new BooleanConnectionProperty("useBlobToStoreUTF8OutsideBMP", false, + Messages.getString("ConnectionProperties.useBlobToStoreUTF8OutsideBMP"), "5.1.3", MISC_CATEGORY, 128); + + private StringConnectionProperty utf8OutsideBmpExcludedColumnNamePattern = new StringConnectionProperty("utf8OutsideBmpExcludedColumnNamePattern", null, + Messages.getString("ConnectionProperties.utf8OutsideBmpExcludedColumnNamePattern"), "5.1.3", MISC_CATEGORY, 129); + + private StringConnectionProperty utf8OutsideBmpIncludedColumnNamePattern = new StringConnectionProperty("utf8OutsideBmpIncludedColumnNamePattern", null, + Messages.getString("ConnectionProperties.utf8OutsideBmpIncludedColumnNamePattern"), "5.1.3", MISC_CATEGORY, 129); + + private BooleanConnectionProperty useCompression = new BooleanConnectionProperty("useCompression", false, + Messages.getString("ConnectionProperties.useCompression"), "3.0.17", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useColumnNamesInFindColumn = new BooleanConnectionProperty("useColumnNamesInFindColumn", false, + Messages.getString("ConnectionProperties.useColumnNamesInFindColumn"), "5.1.7", MISC_CATEGORY, Integer.MAX_VALUE); + + private StringConnectionProperty useConfigs = new StringConnectionProperty("useConfigs", null, Messages.getString("ConnectionProperties.useConfigs"), + "3.1.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty useCursorFetch = new BooleanConnectionProperty("useCursorFetch", false, + Messages.getString("ConnectionProperties.useCursorFetch"), "5.0.0", PERFORMANCE_CATEGORY, Integer.MAX_VALUE); + + private BooleanConnectionProperty useDynamicCharsetInfo = new BooleanConnectionProperty("useDynamicCharsetInfo", true, + Messages.getString("ConnectionProperties.useDynamicCharsetInfo"), "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useDirectRowUnpack = new BooleanConnectionProperty("useDirectRowUnpack", true, + "Use newer result set row unpacking code that skips a copy from network buffers " + + " to a MySQL packet instance and instead reads directly into the result set row data buffers.", + "5.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useFastIntParsing = new BooleanConnectionProperty("useFastIntParsing", true, + Messages.getString("ConnectionProperties.useFastIntParsing"), "3.1.4", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useFastDateParsing = new BooleanConnectionProperty("useFastDateParsing", true, + Messages.getString("ConnectionProperties.useFastDateParsing"), "5.0.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useHostsInPrivileges = new BooleanConnectionProperty("useHostsInPrivileges", true, + Messages.getString("ConnectionProperties.useHostsInPrivileges"), "3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); + private BooleanConnectionProperty useInformationSchema = new BooleanConnectionProperty("useInformationSchema", false, + Messages.getString("ConnectionProperties.useInformationSchema"), "5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); + private BooleanConnectionProperty useJDBCCompliantTimezoneShift = new BooleanConnectionProperty("useJDBCCompliantTimezoneShift", false, + Messages.getString("ConnectionProperties.useJDBCCompliantTimezoneShift"), "5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useLocalSessionState = new BooleanConnectionProperty("useLocalSessionState", false, + Messages.getString("ConnectionProperties.useLocalSessionState"), "3.1.7", PERFORMANCE_CATEGORY, 5); + + private BooleanConnectionProperty useLocalTransactionState = new BooleanConnectionProperty("useLocalTransactionState", false, + Messages.getString("ConnectionProperties.useLocalTransactionState"), "5.1.7", PERFORMANCE_CATEGORY, 6); + + private BooleanConnectionProperty useLegacyDatetimeCode = new BooleanConnectionProperty("useLegacyDatetimeCode", true, + Messages.getString("ConnectionProperties.useLegacyDatetimeCode"), "5.1.6", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty sendFractionalSeconds = new BooleanConnectionProperty("sendFractionalSeconds", true, + Messages.getString("ConnectionProperties.sendFractionalSeconds"), "5.1.37", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useNanosForElapsedTime = new BooleanConnectionProperty("useNanosForElapsedTime", false, + Messages.getString("ConnectionProperties.useNanosForElapsedTime"), "5.0.7", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useOldAliasMetadataBehavior = new BooleanConnectionProperty("useOldAliasMetadataBehavior", false, + Messages.getString("ConnectionProperties.useOldAliasMetadataBehavior"), "5.0.4", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useOldUTF8Behavior = new BooleanConnectionProperty("useOldUTF8Behavior", false, + Messages.getString("ConnectionProperties.useOldUtf8Behavior"), "3.1.6", MISC_CATEGORY, Integer.MIN_VALUE); + + private boolean useOldUTF8BehaviorAsBoolean = false; + + private BooleanConnectionProperty useOnlyServerErrorMessages = new BooleanConnectionProperty("useOnlyServerErrorMessages", true, + Messages.getString("ConnectionProperties.useOnlyServerErrorMessages"), "3.0.15", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useReadAheadInput = new BooleanConnectionProperty("useReadAheadInput", true, + Messages.getString("ConnectionProperties.useReadAheadInput"), "3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useSqlStateCodes = new BooleanConnectionProperty("useSqlStateCodes", true, + Messages.getString("ConnectionProperties.useSqlStateCodes"), "3.1.3", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useSSL = new BooleanConnectionProperty("useSSL", false, Messages.getString("ConnectionProperties.useSSL"), "3.0.2", + SECURITY_CATEGORY, 2); + + private BooleanConnectionProperty useSSPSCompatibleTimezoneShift = new BooleanConnectionProperty("useSSPSCompatibleTimezoneShift", false, + Messages.getString("ConnectionProperties.useSSPSCompatibleTimezoneShift"), "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useStreamLengthsInPrepStmts = new BooleanConnectionProperty("useStreamLengthsInPrepStmts", true, + Messages.getString("ConnectionProperties.useStreamLengthsInPrepStmts"), "3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useTimezone = new BooleanConnectionProperty("useTimezone", false, Messages.getString("ConnectionProperties.useTimezone"), + "3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useUltraDevWorkAround = new BooleanConnectionProperty("ultraDevHack", false, + Messages.getString("ConnectionProperties.ultraDevHack"), "2.0.3", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useUnbufferedInput = new BooleanConnectionProperty("useUnbufferedInput", true, + Messages.getString("ConnectionProperties.useUnbufferedInput"), "3.0.11", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useUnicode = new BooleanConnectionProperty("useUnicode", true, Messages.getString("ConnectionProperties.useUnicode"), + "1.1g", MISC_CATEGORY, 0); + + // Cache these values, they are 'hot' + private boolean useUnicodeAsBoolean = true; + + private BooleanConnectionProperty useUsageAdvisor = new BooleanConnectionProperty("useUsageAdvisor", false, + Messages.getString("ConnectionProperties.useUsageAdvisor"), "3.1.1", DEBUGING_PROFILING_CATEGORY, 10); + + private boolean useUsageAdvisorAsBoolean = false; + + private BooleanConnectionProperty yearIsDateType = new BooleanConnectionProperty("yearIsDateType", true, + Messages.getString("ConnectionProperties.yearIsDateType"), "3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty zeroDateTimeBehavior = new StringConnectionProperty("zeroDateTimeBehavior", ZERO_DATETIME_BEHAVIOR_EXCEPTION, + new String[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION, ZERO_DATETIME_BEHAVIOR_ROUND, ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL }, + Messages.getString("ConnectionProperties.zeroDateTimeBehavior", + new Object[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION, ZERO_DATETIME_BEHAVIOR_ROUND, ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL }), + "3.1.4", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useJvmCharsetConverters = new BooleanConnectionProperty("useJvmCharsetConverters", false, + Messages.getString("ConnectionProperties.useJvmCharsetConverters"), "5.0.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty useGmtMillisForDatetimes = new BooleanConnectionProperty("useGmtMillisForDatetimes", false, + Messages.getString("ConnectionProperties.useGmtMillisForDatetimes"), "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty dumpMetadataOnColumnNotFound = new BooleanConnectionProperty("dumpMetadataOnColumnNotFound", false, + Messages.getString("ConnectionProperties.dumpMetadataOnColumnNotFound"), "3.1.13", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); + + // SSL Options + + private StringConnectionProperty clientCertificateKeyStoreUrl = new StringConnectionProperty("clientCertificateKeyStoreUrl", null, + Messages.getString("ConnectionProperties.clientCertificateKeyStoreUrl"), "5.1.0", SECURITY_CATEGORY, 5); + + private StringConnectionProperty trustCertificateKeyStoreUrl = new StringConnectionProperty("trustCertificateKeyStoreUrl", null, + Messages.getString("ConnectionProperties.trustCertificateKeyStoreUrl"), "5.1.0", SECURITY_CATEGORY, 8); + + private StringConnectionProperty clientCertificateKeyStoreType = new StringConnectionProperty("clientCertificateKeyStoreType", "JKS", + Messages.getString("ConnectionProperties.clientCertificateKeyStoreType"), "5.1.0", SECURITY_CATEGORY, 6); + + private StringConnectionProperty clientCertificateKeyStorePassword = new StringConnectionProperty("clientCertificateKeyStorePassword", null, + Messages.getString("ConnectionProperties.clientCertificateKeyStorePassword"), "5.1.0", SECURITY_CATEGORY, 7); + + private StringConnectionProperty trustCertificateKeyStoreType = new StringConnectionProperty("trustCertificateKeyStoreType", "JKS", + Messages.getString("ConnectionProperties.trustCertificateKeyStoreType"), "5.1.0", SECURITY_CATEGORY, 9); + + private StringConnectionProperty trustCertificateKeyStorePassword = new StringConnectionProperty("trustCertificateKeyStorePassword", null, + Messages.getString("ConnectionProperties.trustCertificateKeyStorePassword"), "5.1.0", SECURITY_CATEGORY, 10); + + private BooleanConnectionProperty verifyServerCertificate = new BooleanConnectionProperty("verifyServerCertificate", true, + Messages.getString("ConnectionProperties.verifyServerCertificate"), "5.1.6", SECURITY_CATEGORY, 4); + + private BooleanConnectionProperty useAffectedRows = new BooleanConnectionProperty("useAffectedRows", false, + Messages.getString("ConnectionProperties.useAffectedRows"), "5.1.7", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty passwordCharacterEncoding = new StringConnectionProperty("passwordCharacterEncoding", null, + Messages.getString("ConnectionProperties.passwordCharacterEncoding"), "5.1.7", SECURITY_CATEGORY, Integer.MIN_VALUE); + + private IntegerConnectionProperty maxAllowedPacket = new IntegerConnectionProperty("maxAllowedPacket", -1, + Messages.getString("ConnectionProperties.maxAllowedPacket"), "5.1.8", NETWORK_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty authenticationPlugins = new StringConnectionProperty("authenticationPlugins", null, + Messages.getString("ConnectionProperties.authenticationPlugins"), "5.1.19", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty disabledAuthenticationPlugins = new StringConnectionProperty("disabledAuthenticationPlugins", null, + Messages.getString("ConnectionProperties.disabledAuthenticationPlugins"), "5.1.19", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty defaultAuthenticationPlugin = new StringConnectionProperty("defaultAuthenticationPlugin", + "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin", Messages.getString("ConnectionProperties.defaultAuthenticationPlugin"), "5.1.19", + CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty disconnectOnExpiredPasswords = new BooleanConnectionProperty("disconnectOnExpiredPasswords", true, + Messages.getString("ConnectionProperties.disconnectOnExpiredPasswords"), "5.1.23", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty getProceduresReturnsFunctions = new BooleanConnectionProperty("getProceduresReturnsFunctions", true, + Messages.getString("ConnectionProperties.getProceduresReturnsFunctions"), "5.1.26", MISC_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty detectCustomCollations = new BooleanConnectionProperty("detectCustomCollations", false, + Messages.getString("ConnectionProperties.detectCustomCollations"), "5.1.29", MISC_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty serverRSAPublicKeyFile = new StringConnectionProperty("serverRSAPublicKeyFile", null, + Messages.getString("ConnectionProperties.serverRSAPublicKeyFile"), "5.1.31", SECURITY_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty allowPublicKeyRetrieval = new BooleanConnectionProperty("allowPublicKeyRetrieval", false, + Messages.getString("ConnectionProperties.allowPublicKeyRetrieval"), "5.1.31", SECURITY_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty dontCheckOnDuplicateKeyUpdateInSQL = new BooleanConnectionProperty("dontCheckOnDuplicateKeyUpdateInSQL", false, + Messages.getString("ConnectionProperties.dontCheckOnDuplicateKeyUpdateInSQL"), "5.1.32", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private BooleanConnectionProperty readOnlyPropagatesToServer = new BooleanConnectionProperty("readOnlyPropagatesToServer", true, + Messages.getString("ConnectionProperties.readOnlyPropagatesToServer"), "5.1.35", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + private StringConnectionProperty enabledSSLCipherSuites = new StringConnectionProperty("enabledSSLCipherSuites", null, + Messages.getString("ConnectionProperties.enabledSSLCipherSuites"), "5.1.35", SECURITY_CATEGORY, 11); + + private BooleanConnectionProperty enableEscapeProcessing = new BooleanConnectionProperty("enableEscapeProcessing", true, + Messages.getString("ConnectionProperties.enableEscapeProcessing"), "5.1.37", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); + + protected DriverPropertyInfo[] exposeAsDriverPropertyInfoInternal(Properties info, int slotsToReserve) throws SQLException { + initializeProperties(info); + + int numProperties = PROPERTY_LIST.size(); + + int listSize = numProperties + slotsToReserve; + + DriverPropertyInfo[] driverProperties = new DriverPropertyInfo[listSize]; + + for (int i = slotsToReserve; i < listSize; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i - slotsToReserve); + + try { + ConnectionProperty propToExpose = (ConnectionProperty) propertyField.get(this); + + if (info != null) { + propToExpose.initializeFrom(info, getExceptionInterceptor()); + } + + driverProperties[i] = propToExpose.getAsDriverPropertyInfo(); + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException(Messages.getString("ConnectionProperties.InternalPropertiesFailure"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + + return driverProperties; + } + + protected Properties exposeAsProperties(Properties info) throws SQLException { + if (info == null) { + info = new Properties(); + } + + int numPropertiesToSet = PROPERTY_LIST.size(); + + for (int i = 0; i < numPropertiesToSet; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i); + + try { + ConnectionProperty propToGet = (ConnectionProperty) propertyField.get(this); + + Object propValue = propToGet.getValueAsObject(); + + if (propValue != null) { + info.setProperty(propToGet.getPropertyName(), propValue.toString()); + } + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException("Internal properties failure", SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + return info; + } + + class XmlMap { + protected Map> ordered = new TreeMap>(); + protected Map alpha = new TreeMap(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#exposeAsXml() + */ + public String exposeAsXml() throws SQLException { + StringBuilder xmlBuf = new StringBuilder(); + xmlBuf.append(""); + + int numPropertiesToSet = PROPERTY_LIST.size(); + + int numCategories = PROPERTY_CATEGORIES.length; + + Map propertyListByCategory = new HashMap(); + + for (int i = 0; i < numCategories; i++) { + propertyListByCategory.put(PROPERTY_CATEGORIES[i], new XmlMap()); + } + + // + // The following properties are not exposed as 'normal' properties, but they are settable nonetheless, so we need to have them documented, make sure + // that they sort 'first' as #1 and #2 in the category + // + StringConnectionProperty userProp = new StringConnectionProperty(NonRegisteringDriver.USER_PROPERTY_KEY, null, + Messages.getString("ConnectionProperties.Username"), Messages.getString("ConnectionProperties.allVersions"), CONNECTION_AND_AUTH_CATEGORY, + Integer.MIN_VALUE + 1); + StringConnectionProperty passwordProp = new StringConnectionProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, null, + Messages.getString("ConnectionProperties.Password"), Messages.getString("ConnectionProperties.allVersions"), CONNECTION_AND_AUTH_CATEGORY, + Integer.MIN_VALUE + 2); + + XmlMap connectionSortMaps = propertyListByCategory.get(CONNECTION_AND_AUTH_CATEGORY); + TreeMap userMap = new TreeMap(); + userMap.put(userProp.getPropertyName(), userProp); + + connectionSortMaps.ordered.put(Integer.valueOf(userProp.getOrder()), userMap); + + TreeMap passwordMap = new TreeMap(); + passwordMap.put(passwordProp.getPropertyName(), passwordProp); + + connectionSortMaps.ordered.put(new Integer(passwordProp.getOrder()), passwordMap); + + try { + for (int i = 0; i < numPropertiesToSet; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i); + ConnectionProperty propToGet = (ConnectionProperty) propertyField.get(this); + XmlMap sortMaps = propertyListByCategory.get(propToGet.getCategoryName()); + int orderInCategory = propToGet.getOrder(); + + if (orderInCategory == Integer.MIN_VALUE) { + sortMaps.alpha.put(propToGet.getPropertyName(), propToGet); + } else { + Integer order = Integer.valueOf(orderInCategory); + Map orderMap = sortMaps.ordered.get(order); + + if (orderMap == null) { + orderMap = new TreeMap(); + sortMaps.ordered.put(order, orderMap); + } + + orderMap.put(propToGet.getPropertyName(), propToGet); + } + } + + for (int j = 0; j < numCategories; j++) { + XmlMap sortMaps = propertyListByCategory.get(PROPERTY_CATEGORIES[j]); + + xmlBuf.append("\n "); + + for (Map orderedEl : sortMaps.ordered.values()) { + for (ConnectionProperty propToGet : orderedEl.values()) { + xmlBuf.append("\n \n"); + xmlBuf.append(" "); + String escapedDescription = propToGet.description; + escapedDescription = escapedDescription.replace("&", "&").replace("<", "<").replace(">", ">"); + + xmlBuf.append(escapedDescription); + xmlBuf.append("\n "); + } + } + + for (ConnectionProperty propToGet : sortMaps.alpha.values()) { + xmlBuf.append("\n \n"); + xmlBuf.append(" "); + xmlBuf.append(propToGet.description); + xmlBuf.append("\n "); + } + + xmlBuf.append("\n "); + } + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException("Internal properties failure", SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + xmlBuf.append("\n"); + + return xmlBuf.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAllowLoadLocalInfile() + */ + public boolean getAllowLoadLocalInfile() { + return this.allowLoadLocalInfile.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAllowMultiQueries() + */ + public boolean getAllowMultiQueries() { + return this.allowMultiQueries.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAllowNanAndInf() + */ + public boolean getAllowNanAndInf() { + return this.allowNanAndInf.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAllowUrlInLocalInfile() + */ + public boolean getAllowUrlInLocalInfile() { + return this.allowUrlInLocalInfile.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAlwaysSendSetIsolation() + */ + public boolean getAlwaysSendSetIsolation() { + return this.alwaysSendSetIsolation.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAutoDeserialize() + */ + public boolean getAutoDeserialize() { + return this.autoDeserialize.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAutoGenerateTestcaseScript() + */ + public boolean getAutoGenerateTestcaseScript() { + return this.autoGenerateTestcaseScriptAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAutoReconnectForPools() + */ + public boolean getAutoReconnectForPools() { + return this.autoReconnectForPoolsAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getBlobSendChunkSize() + */ + public int getBlobSendChunkSize() { + return this.blobSendChunkSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCacheCallableStatements() + */ + public boolean getCacheCallableStatements() { + return this.cacheCallableStatements.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCachePreparedStatements() + */ + public boolean getCachePreparedStatements() { + return ((Boolean) this.cachePreparedStatements.getValueAsObject()).booleanValue(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCacheResultSetMetadata() + */ + public boolean getCacheResultSetMetadata() { + return this.cacheResultSetMetaDataAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCacheServerConfiguration() + */ + public boolean getCacheServerConfiguration() { + return this.cacheServerConfiguration.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCallableStatementCacheSize() + */ + public int getCallableStatementCacheSize() { + return this.callableStatementCacheSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCapitalizeTypeNames() + */ + public boolean getCapitalizeTypeNames() { + return this.capitalizeTypeNames.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCharacterSetResults() + */ + public String getCharacterSetResults() { + return this.characterSetResults.getValueAsString(); + } + + public String getConnectionAttributes() { + return this.connectionAttributes.getValueAsString(); + } + + public void setConnectionAttributes(String val) { + this.connectionAttributes.setValue(val); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClobberStreamingResults() + */ + public boolean getClobberStreamingResults() { + return this.clobberStreamingResults.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClobCharacterEncoding() + */ + public String getClobCharacterEncoding() { + return this.clobCharacterEncoding.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getConnectionCollation() + */ + public String getConnectionCollation() { + return this.connectionCollation.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getConnectTimeout() + */ + public int getConnectTimeout() { + return this.connectTimeout.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getContinueBatchOnError() + */ + public boolean getContinueBatchOnError() { + return this.continueBatchOnError.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCreateDatabaseIfNotExist() + */ + public boolean getCreateDatabaseIfNotExist() { + return this.createDatabaseIfNotExist.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getDefaultFetchSize() + */ + public int getDefaultFetchSize() { + return this.defaultFetchSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getDontTrackOpenResources() + */ + public boolean getDontTrackOpenResources() { + return this.dontTrackOpenResources.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getDumpQueriesOnException() + */ + public boolean getDumpQueriesOnException() { + return this.dumpQueriesOnException.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getDynamicCalendars() + */ + public boolean getDynamicCalendars() { + return this.dynamicCalendars.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getElideSetAutoCommits() + */ + public boolean getElideSetAutoCommits() { + return this.elideSetAutoCommits.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEmptyStringsConvertToZero() + */ + public boolean getEmptyStringsConvertToZero() { + return this.emptyStringsConvertToZero.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEmulateLocators() + */ + public boolean getEmulateLocators() { + return this.emulateLocators.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEmulateUnsupportedPstmts() + */ + public boolean getEmulateUnsupportedPstmts() { + return this.emulateUnsupportedPstmts.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEnablePacketDebug() + */ + public boolean getEnablePacketDebug() { + return this.enablePacketDebug.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEncoding() + */ + public String getEncoding() { + return this.characterEncodingAsString; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getExplainSlowQueries() + */ + public boolean getExplainSlowQueries() { + return this.explainSlowQueries.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getFailOverReadOnly() + */ + public boolean getFailOverReadOnly() { + return this.failOverReadOnly.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getGatherPerformanceMetrics() + */ + public boolean getGatherPerformanceMetrics() { + return this.gatherPerformanceMetrics.getValueAsBoolean(); + } + + protected boolean getHighAvailability() { + return this.highAvailabilityAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getHoldResultsOpenOverStatementClose() + */ + public boolean getHoldResultsOpenOverStatementClose() { + return this.holdResultsOpenOverStatementClose.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getIgnoreNonTxTables() + */ + public boolean getIgnoreNonTxTables() { + return this.ignoreNonTxTables.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getInitialTimeout() + */ + public int getInitialTimeout() { + return this.initialTimeout.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getInteractiveClient() + */ + public boolean getInteractiveClient() { + return this.isInteractiveClient.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getIsInteractiveClient() + */ + public boolean getIsInteractiveClient() { + return this.isInteractiveClient.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getJdbcCompliantTruncation() + */ + public boolean getJdbcCompliantTruncation() { + return this.jdbcCompliantTruncation.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLocatorFetchBufferSize() + */ + public int getLocatorFetchBufferSize() { + return this.locatorFetchBufferSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLogger() + */ + public String getLogger() { + return this.loggerClassName.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLoggerClassName() + */ + public String getLoggerClassName() { + return this.loggerClassName.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLogSlowQueries() + */ + public boolean getLogSlowQueries() { + return this.logSlowQueries.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getMaintainTimeStats() + */ + public boolean getMaintainTimeStats() { + return this.maintainTimeStatsAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getMaxQuerySizeToLog() + */ + public int getMaxQuerySizeToLog() { + return this.maxQuerySizeToLog.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getMaxReconnects() + */ + public int getMaxReconnects() { + return this.maxReconnects.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getMaxRows() + */ + public int getMaxRows() { + return this.maxRowsAsInt; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getMetadataCacheSize() + */ + public int getMetadataCacheSize() { + return this.metadataCacheSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNoDatetimeStringSync() + */ + public boolean getNoDatetimeStringSync() { + return this.noDatetimeStringSync.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNullCatalogMeansCurrent() + */ + public boolean getNullCatalogMeansCurrent() { + return this.nullCatalogMeansCurrent.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNullNamePatternMatchesAll() + */ + public boolean getNullNamePatternMatchesAll() { + return this.nullNamePatternMatchesAll.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPacketDebugBufferSize() + */ + public int getPacketDebugBufferSize() { + return this.packetDebugBufferSize.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getParanoid() + */ + public boolean getParanoid() { + return this.paranoid.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPedantic() + */ + public boolean getPedantic() { + return this.pedantic.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPreparedStatementCacheSize() + */ + public int getPreparedStatementCacheSize() { + return ((Integer) this.preparedStatementCacheSize.getValueAsObject()).intValue(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPreparedStatementCacheSqlLimit() + */ + public int getPreparedStatementCacheSqlLimit() { + return ((Integer) this.preparedStatementCacheSqlLimit.getValueAsObject()).intValue(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getProfileSql() + */ + public boolean getProfileSql() { + return this.profileSQLAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getProfileSQL() + */ + public boolean getProfileSQL() { + return this.profileSQL.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPropertiesTransform() + */ + public String getPropertiesTransform() { + return this.propertiesTransform.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getQueriesBeforeRetryMaster() + */ + public int getQueriesBeforeRetryMaster() { + return this.queriesBeforeRetryMaster.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getReconnectAtTxEnd() + */ + public boolean getReconnectAtTxEnd() { + return this.reconnectTxAtEndAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRelaxAutoCommit() + */ + public boolean getRelaxAutoCommit() { + return this.relaxAutoCommit.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getReportMetricsIntervalMillis() + */ + public int getReportMetricsIntervalMillis() { + return this.reportMetricsIntervalMillis.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRequireSSL() + */ + public boolean getRequireSSL() { + return this.requireSSL.getValueAsBoolean(); + } + + public boolean getRetainStatementAfterResultSetClose() { + return this.retainStatementAfterResultSetClose.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRollbackOnPooledClose() + */ + public boolean getRollbackOnPooledClose() { + return this.rollbackOnPooledClose.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRoundRobinLoadBalance() + */ + public boolean getRoundRobinLoadBalance() { + return this.roundRobinLoadBalance.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRunningCTS13() + */ + public boolean getRunningCTS13() { + return this.runningCTS13.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSecondsBeforeRetryMaster() + */ + public int getSecondsBeforeRetryMaster() { + return this.secondsBeforeRetryMaster.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getServerTimezone() + */ + public String getServerTimezone() { + return this.serverTimezone.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSessionVariables() + */ + public String getSessionVariables() { + return this.sessionVariables.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSlowQueryThresholdMillis() + */ + public int getSlowQueryThresholdMillis() { + return this.slowQueryThresholdMillis.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSocketFactoryClassName() + */ + public String getSocketFactoryClassName() { + return this.socketFactoryClassName.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSocketTimeout() + */ + public int getSocketTimeout() { + return this.socketTimeout.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getStrictFloatingPoint() + */ + public boolean getStrictFloatingPoint() { + return this.strictFloatingPoint.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getStrictUpdates() + */ + public boolean getStrictUpdates() { + return this.strictUpdates.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTinyInt1isBit() + */ + public boolean getTinyInt1isBit() { + return this.tinyInt1isBit.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTraceProtocol() + */ + public boolean getTraceProtocol() { + return this.traceProtocol.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTransformedBitIsBoolean() + */ + public boolean getTransformedBitIsBoolean() { + return this.transformedBitIsBoolean.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseCompression() + */ + public boolean getUseCompression() { + return this.useCompression.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseFastIntParsing() + */ + public boolean getUseFastIntParsing() { + return this.useFastIntParsing.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseHostsInPrivileges() + */ + public boolean getUseHostsInPrivileges() { + return this.useHostsInPrivileges.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseInformationSchema() + */ + public boolean getUseInformationSchema() { + return this.useInformationSchema.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseLocalSessionState() + */ + public boolean getUseLocalSessionState() { + return this.useLocalSessionState.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseOldUTF8Behavior() + */ + public boolean getUseOldUTF8Behavior() { + return this.useOldUTF8BehaviorAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseOnlyServerErrorMessages() + */ + public boolean getUseOnlyServerErrorMessages() { + return this.useOnlyServerErrorMessages.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseReadAheadInput() + */ + public boolean getUseReadAheadInput() { + return this.useReadAheadInput.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseServerPreparedStmts() + */ + public boolean getUseServerPreparedStmts() { + return this.detectServerPreparedStmts.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseSqlStateCodes() + */ + public boolean getUseSqlStateCodes() { + return this.useSqlStateCodes.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseSSL() + */ + public boolean getUseSSL() { + return this.useSSL.getValueAsBoolean(); + } + + /** + * Was the value of useSSL set explicitly or just got from defaults. + * + * @return + */ + public boolean isUseSSLExplicit() { + return this.useSSL.wasExplicitlySet; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseStreamLengthsInPrepStmts() + */ + public boolean getUseStreamLengthsInPrepStmts() { + return this.useStreamLengthsInPrepStmts.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseTimezone() + */ + public boolean getUseTimezone() { + return this.useTimezone.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseUltraDevWorkAround() + */ + public boolean getUseUltraDevWorkAround() { + return this.useUltraDevWorkAround.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseUnbufferedInput() + */ + public boolean getUseUnbufferedInput() { + return this.useUnbufferedInput.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseUnicode() + */ + public boolean getUseUnicode() { + return this.useUnicodeAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseUsageAdvisor() + */ + public boolean getUseUsageAdvisor() { + return this.useUsageAdvisorAsBoolean; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getYearIsDateType() + */ + public boolean getYearIsDateType() { + return this.yearIsDateType.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getZeroDateTimeBehavior() + */ + public String getZeroDateTimeBehavior() { + return this.zeroDateTimeBehavior.getValueAsString(); + } + + /** + * Initializes driver properties that come from a JNDI reference (in the + * case of a javax.sql.DataSource bound into some name service that doesn't + * handle Java objects directly). + * + * @param ref + * The JNDI Reference that holds RefAddrs for all properties + * @throws SQLException + */ + protected void initializeFromRef(Reference ref) throws SQLException { + int numPropertiesToSet = PROPERTY_LIST.size(); + + for (int i = 0; i < numPropertiesToSet; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i); + + try { + ConnectionProperty propToSet = (ConnectionProperty) propertyField.get(this); + + if (ref != null) { + propToSet.initializeFrom(ref, getExceptionInterceptor()); + } + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException("Internal properties failure", SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + postInitialization(); + } + + /** + * Initializes driver properties that come from URL or properties passed to + * the driver manager. + * + * @param info + * @throws SQLException + */ + protected void initializeProperties(Properties info) throws SQLException { + if (info != null) { + // For backwards-compatibility + String profileSqlLc = info.getProperty("profileSql"); + + if (profileSqlLc != null) { + info.put("profileSQL", profileSqlLc); + } + + Properties infoCopy = (Properties) info.clone(); + + infoCopy.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + infoCopy.remove(NonRegisteringDriver.USER_PROPERTY_KEY); + infoCopy.remove(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + infoCopy.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + infoCopy.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + infoCopy.remove("profileSql"); + + int numPropertiesToSet = PROPERTY_LIST.size(); + + for (int i = 0; i < numPropertiesToSet; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i); + + try { + ConnectionProperty propToSet = (ConnectionProperty) propertyField.get(this); + + propToSet.initializeFrom(infoCopy, getExceptionInterceptor()); + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException(Messages.getString("ConnectionProperties.unableToInitDriverProperties") + iae.toString(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + postInitialization(); + } + } + + protected void postInitialization() throws SQLException { + + // Support 'old' profileSql capitalization + if (this.profileSql.getValueAsObject() != null) { + this.profileSQL.initializeFrom(this.profileSql.getValueAsObject().toString(), getExceptionInterceptor()); + } + + this.reconnectTxAtEndAsBoolean = ((Boolean) this.reconnectAtTxEnd.getValueAsObject()).booleanValue(); + + // Adjust max rows + if (this.getMaxRows() == 0) { + // adjust so that it will become MysqlDefs.MAX_ROWS in execSQL() + this.maxRows.setValueAsObject(Integer.valueOf(-1)); + } + + // + // Check character encoding + // + String testEncoding = ((String) this.characterEncoding.getValueAsObject()); + + if (testEncoding != null) { + // Attempt to use the encoding, and bail out if it can't be used + try { + String testString = "abc"; + StringUtils.getBytes(testString, testEncoding); + } catch (UnsupportedEncodingException UE) { + throw SQLError.createSQLException(Messages.getString("ConnectionProperties.unsupportedCharacterEncoding", new Object[] { testEncoding }), + "0S100", getExceptionInterceptor()); + } + } + + // Metadata caching is only supported on JDK-1.4 and newer because it relies on LinkedHashMap being present. + // Check (and disable) if not supported + if (((Boolean) this.cacheResultSetMetadata.getValueAsObject()).booleanValue()) { + try { + Class.forName("java.util.LinkedHashMap"); + } catch (ClassNotFoundException cnfe) { + this.cacheResultSetMetadata.setValue(false); + } + } + + this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata.getValueAsBoolean(); + this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean(); + this.characterEncodingAsString = ((String) this.characterEncoding.getValueAsObject()); + this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean(); + this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools.getValueAsBoolean(); + this.maxRowsAsInt = ((Integer) this.maxRows.getValueAsObject()).intValue(); + this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean(); + this.useUsageAdvisorAsBoolean = this.useUsageAdvisor.getValueAsBoolean(); + this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior.getValueAsBoolean(); + this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript.getValueAsBoolean(); + this.maintainTimeStatsAsBoolean = this.maintainTimeStats.getValueAsBoolean(); + this.jdbcCompliantTruncationForReads = getJdbcCompliantTruncation(); + + if (getUseCursorFetch()) { + // assume they want to use server-side prepared statements because they're required for this functionality + setDetectServerPreparedStmts(true); + } + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAllowLoadLocalInfile(boolean) + */ + public void setAllowLoadLocalInfile(boolean property) { + this.allowLoadLocalInfile.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAllowMultiQueries(boolean) + */ + public void setAllowMultiQueries(boolean property) { + this.allowMultiQueries.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAllowNanAndInf(boolean) + */ + public void setAllowNanAndInf(boolean flag) { + this.allowNanAndInf.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAllowUrlInLocalInfile(boolean) + */ + public void setAllowUrlInLocalInfile(boolean flag) { + this.allowUrlInLocalInfile.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAlwaysSendSetIsolation(boolean) + */ + public void setAlwaysSendSetIsolation(boolean flag) { + this.alwaysSendSetIsolation.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoDeserialize(boolean) + */ + public void setAutoDeserialize(boolean flag) { + this.autoDeserialize.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoGenerateTestcaseScript(boolean) + */ + public void setAutoGenerateTestcaseScript(boolean flag) { + this.autoGenerateTestcaseScript.setValue(flag); + this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnect(boolean) + */ + public void setAutoReconnect(boolean flag) { + this.autoReconnect.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnectForConnectionPools(boolean) + */ + public void setAutoReconnectForConnectionPools(boolean property) { + this.autoReconnectForPools.setValue(property); + this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnectForPools(boolean) + */ + public void setAutoReconnectForPools(boolean flag) { + this.autoReconnectForPools.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setBlobSendChunkSize(java.lang.String) + */ + public void setBlobSendChunkSize(String value) throws SQLException { + this.blobSendChunkSize.setValue(value, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCacheCallableStatements(boolean) + */ + public void setCacheCallableStatements(boolean flag) { + this.cacheCallableStatements.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCachePreparedStatements(boolean) + */ + public void setCachePreparedStatements(boolean flag) { + this.cachePreparedStatements.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCacheResultSetMetadata(boolean) + */ + public void setCacheResultSetMetadata(boolean property) { + this.cacheResultSetMetadata.setValue(property); + this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCacheServerConfiguration(boolean) + */ + public void setCacheServerConfiguration(boolean flag) { + this.cacheServerConfiguration.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCallableStatementCacheSize(int) + */ + public void setCallableStatementCacheSize(int size) throws SQLException { + this.callableStatementCacheSize.setValue(size, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCapitalizeDBMDTypes(boolean) + */ + public void setCapitalizeDBMDTypes(boolean property) { + this.capitalizeTypeNames.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCapitalizeTypeNames(boolean) + */ + public void setCapitalizeTypeNames(boolean flag) { + this.capitalizeTypeNames.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCharacterEncoding(java.lang.String) + */ + public void setCharacterEncoding(String encoding) { + this.characterEncoding.setValue(encoding); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCharacterSetResults(java.lang.String) + */ + public void setCharacterSetResults(String characterSet) { + this.characterSetResults.setValue(characterSet); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClobberStreamingResults(boolean) + */ + public void setClobberStreamingResults(boolean flag) { + this.clobberStreamingResults.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClobCharacterEncoding(java.lang.String) + */ + public void setClobCharacterEncoding(String encoding) { + this.clobCharacterEncoding.setValue(encoding); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setConnectionCollation(java.lang.String) + */ + public void setConnectionCollation(String collation) { + this.connectionCollation.setValue(collation); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setConnectTimeout(int) + */ + public void setConnectTimeout(int timeoutMs) throws SQLException { + this.connectTimeout.setValue(timeoutMs, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setContinueBatchOnError(boolean) + */ + public void setContinueBatchOnError(boolean property) { + this.continueBatchOnError.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCreateDatabaseIfNotExist(boolean) + */ + public void setCreateDatabaseIfNotExist(boolean flag) { + this.createDatabaseIfNotExist.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDefaultFetchSize(int) + */ + public void setDefaultFetchSize(int n) throws SQLException { + this.defaultFetchSize.setValue(n, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDetectServerPreparedStmts(boolean) + */ + public void setDetectServerPreparedStmts(boolean property) { + this.detectServerPreparedStmts.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDontTrackOpenResources(boolean) + */ + public void setDontTrackOpenResources(boolean flag) { + this.dontTrackOpenResources.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDumpQueriesOnException(boolean) + */ + public void setDumpQueriesOnException(boolean flag) { + this.dumpQueriesOnException.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDynamicCalendars(boolean) + */ + public void setDynamicCalendars(boolean flag) { + this.dynamicCalendars.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setElideSetAutoCommits(boolean) + */ + public void setElideSetAutoCommits(boolean flag) { + this.elideSetAutoCommits.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEmptyStringsConvertToZero(boolean) + */ + public void setEmptyStringsConvertToZero(boolean flag) { + this.emptyStringsConvertToZero.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEmulateLocators(boolean) + */ + public void setEmulateLocators(boolean property) { + this.emulateLocators.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEmulateUnsupportedPstmts(boolean) + */ + public void setEmulateUnsupportedPstmts(boolean flag) { + this.emulateUnsupportedPstmts.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEnablePacketDebug(boolean) + */ + public void setEnablePacketDebug(boolean flag) { + this.enablePacketDebug.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEncoding(java.lang.String) + */ + public void setEncoding(String property) { + this.characterEncoding.setValue(property); + this.characterEncodingAsString = this.characterEncoding.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setExplainSlowQueries(boolean) + */ + public void setExplainSlowQueries(boolean flag) { + this.explainSlowQueries.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setFailOverReadOnly(boolean) + */ + public void setFailOverReadOnly(boolean flag) { + this.failOverReadOnly.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setGatherPerformanceMetrics(boolean) + */ + public void setGatherPerformanceMetrics(boolean flag) { + this.gatherPerformanceMetrics.setValue(flag); + } + + protected void setHighAvailability(boolean property) { + this.autoReconnect.setValue(property); + this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setHoldResultsOpenOverStatementClose(boolean) + */ + public void setHoldResultsOpenOverStatementClose(boolean flag) { + this.holdResultsOpenOverStatementClose.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setIgnoreNonTxTables(boolean) + */ + public void setIgnoreNonTxTables(boolean property) { + this.ignoreNonTxTables.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setInitialTimeout(int) + */ + public void setInitialTimeout(int property) throws SQLException { + this.initialTimeout.setValue(property, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setIsInteractiveClient(boolean) + */ + public void setIsInteractiveClient(boolean property) { + this.isInteractiveClient.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setJdbcCompliantTruncation(boolean) + */ + public void setJdbcCompliantTruncation(boolean flag) { + this.jdbcCompliantTruncation.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLocatorFetchBufferSize(java.lang.String) + */ + public void setLocatorFetchBufferSize(String value) throws SQLException { + this.locatorFetchBufferSize.setValue(value, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLogger(java.lang.String) + */ + public void setLogger(String property) { + this.loggerClassName.setValueAsObject(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLoggerClassName(java.lang.String) + */ + public void setLoggerClassName(String className) { + this.loggerClassName.setValue(className); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLogSlowQueries(boolean) + */ + public void setLogSlowQueries(boolean flag) { + this.logSlowQueries.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setMaintainTimeStats(boolean) + */ + public void setMaintainTimeStats(boolean flag) { + this.maintainTimeStats.setValue(flag); + this.maintainTimeStatsAsBoolean = this.maintainTimeStats.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setMaxQuerySizeToLog(int) + */ + public void setMaxQuerySizeToLog(int sizeInBytes) throws SQLException { + this.maxQuerySizeToLog.setValue(sizeInBytes, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setMaxReconnects(int) + */ + public void setMaxReconnects(int property) throws SQLException { + this.maxReconnects.setValue(property, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setMaxRows(int) + */ + public void setMaxRows(int property) throws SQLException { + this.maxRows.setValue(property, getExceptionInterceptor()); + this.maxRowsAsInt = this.maxRows.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setMetadataCacheSize(int) + */ + public void setMetadataCacheSize(int value) throws SQLException { + this.metadataCacheSize.setValue(value, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNoDatetimeStringSync(boolean) + */ + public void setNoDatetimeStringSync(boolean flag) { + this.noDatetimeStringSync.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNullCatalogMeansCurrent(boolean) + */ + public void setNullCatalogMeansCurrent(boolean value) { + this.nullCatalogMeansCurrent.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNullNamePatternMatchesAll(boolean) + */ + public void setNullNamePatternMatchesAll(boolean value) { + this.nullNamePatternMatchesAll.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPacketDebugBufferSize(int) + */ + public void setPacketDebugBufferSize(int size) throws SQLException { + this.packetDebugBufferSize.setValue(size, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setParanoid(boolean) + */ + public void setParanoid(boolean property) { + this.paranoid.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPedantic(boolean) + */ + public void setPedantic(boolean property) { + this.pedantic.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPreparedStatementCacheSize(int) + */ + public void setPreparedStatementCacheSize(int cacheSize) throws SQLException { + this.preparedStatementCacheSize.setValue(cacheSize, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPreparedStatementCacheSqlLimit(int) + */ + public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) throws SQLException { + this.preparedStatementCacheSqlLimit.setValue(cacheSqlLimit, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setProfileSql(boolean) + */ + public void setProfileSql(boolean property) { + this.profileSQL.setValue(property); + this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setProfileSQL(boolean) + */ + public void setProfileSQL(boolean flag) { + this.profileSQL.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPropertiesTransform(java.lang.String) + */ + public void setPropertiesTransform(String value) { + this.propertiesTransform.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setQueriesBeforeRetryMaster(int) + */ + public void setQueriesBeforeRetryMaster(int property) throws SQLException { + this.queriesBeforeRetryMaster.setValue(property, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setReconnectAtTxEnd(boolean) + */ + public void setReconnectAtTxEnd(boolean property) { + this.reconnectAtTxEnd.setValue(property); + this.reconnectTxAtEndAsBoolean = this.reconnectAtTxEnd.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRelaxAutoCommit(boolean) + */ + public void setRelaxAutoCommit(boolean property) { + this.relaxAutoCommit.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setReportMetricsIntervalMillis(int) + */ + public void setReportMetricsIntervalMillis(int millis) throws SQLException { + this.reportMetricsIntervalMillis.setValue(millis, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRequireSSL(boolean) + */ + public void setRequireSSL(boolean property) { + this.requireSSL.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRetainStatementAfterResultSetClose(boolean) + */ + public void setRetainStatementAfterResultSetClose(boolean flag) { + this.retainStatementAfterResultSetClose.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRollbackOnPooledClose(boolean) + */ + public void setRollbackOnPooledClose(boolean flag) { + this.rollbackOnPooledClose.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRoundRobinLoadBalance(boolean) + */ + public void setRoundRobinLoadBalance(boolean flag) { + this.roundRobinLoadBalance.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRunningCTS13(boolean) + */ + public void setRunningCTS13(boolean flag) { + this.runningCTS13.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSecondsBeforeRetryMaster(int) + */ + public void setSecondsBeforeRetryMaster(int property) throws SQLException { + this.secondsBeforeRetryMaster.setValue(property, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setServerTimezone(java.lang.String) + */ + public void setServerTimezone(String property) { + this.serverTimezone.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSessionVariables(java.lang.String) + */ + public void setSessionVariables(String variables) { + this.sessionVariables.setValue(variables); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSlowQueryThresholdMillis(int) + */ + public void setSlowQueryThresholdMillis(int millis) throws SQLException { + this.slowQueryThresholdMillis.setValue(millis, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSocketFactoryClassName(java.lang.String) + */ + public void setSocketFactoryClassName(String property) { + this.socketFactoryClassName.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSocketTimeout(int) + */ + public void setSocketTimeout(int property) throws SQLException { + this.socketTimeout.setValue(property, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setStrictFloatingPoint(boolean) + */ + public void setStrictFloatingPoint(boolean property) { + this.strictFloatingPoint.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setStrictUpdates(boolean) + */ + public void setStrictUpdates(boolean property) { + this.strictUpdates.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTinyInt1isBit(boolean) + */ + public void setTinyInt1isBit(boolean flag) { + this.tinyInt1isBit.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTraceProtocol(boolean) + */ + public void setTraceProtocol(boolean flag) { + this.traceProtocol.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTransformedBitIsBoolean(boolean) + */ + public void setTransformedBitIsBoolean(boolean flag) { + this.transformedBitIsBoolean.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseCompression(boolean) + */ + public void setUseCompression(boolean property) { + this.useCompression.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseFastIntParsing(boolean) + */ + public void setUseFastIntParsing(boolean flag) { + this.useFastIntParsing.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseHostsInPrivileges(boolean) + */ + public void setUseHostsInPrivileges(boolean property) { + this.useHostsInPrivileges.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseInformationSchema(boolean) + */ + public void setUseInformationSchema(boolean flag) { + this.useInformationSchema.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseLocalSessionState(boolean) + */ + public void setUseLocalSessionState(boolean flag) { + this.useLocalSessionState.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseOldUTF8Behavior(boolean) + */ + public void setUseOldUTF8Behavior(boolean flag) { + this.useOldUTF8Behavior.setValue(flag); + this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseOnlyServerErrorMessages(boolean) + */ + public void setUseOnlyServerErrorMessages(boolean flag) { + this.useOnlyServerErrorMessages.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseReadAheadInput(boolean) + */ + public void setUseReadAheadInput(boolean flag) { + this.useReadAheadInput.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseServerPreparedStmts(boolean) + */ + public void setUseServerPreparedStmts(boolean flag) { + this.detectServerPreparedStmts.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseSqlStateCodes(boolean) + */ + public void setUseSqlStateCodes(boolean flag) { + this.useSqlStateCodes.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseSSL(boolean) + */ + public void setUseSSL(boolean property) { + this.useSSL.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseStreamLengthsInPrepStmts(boolean) + */ + public void setUseStreamLengthsInPrepStmts(boolean property) { + this.useStreamLengthsInPrepStmts.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseTimezone(boolean) + */ + public void setUseTimezone(boolean property) { + this.useTimezone.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseUltraDevWorkAround(boolean) + */ + public void setUseUltraDevWorkAround(boolean property) { + this.useUltraDevWorkAround.setValue(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseUnbufferedInput(boolean) + */ + public void setUseUnbufferedInput(boolean flag) { + this.useUnbufferedInput.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseUnicode(boolean) + */ + public void setUseUnicode(boolean flag) { + this.useUnicode.setValue(flag); + this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseUsageAdvisor(boolean) + */ + public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) { + this.useUsageAdvisor.setValue(useUsageAdvisorFlag); + this.useUsageAdvisorAsBoolean = this.useUsageAdvisor.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setYearIsDateType(boolean) + */ + public void setYearIsDateType(boolean flag) { + this.yearIsDateType.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setZeroDateTimeBehavior(java.lang.String) + */ + public void setZeroDateTimeBehavior(String behavior) { + this.zeroDateTimeBehavior.setValue(behavior); + } + + protected void storeToRef(Reference ref) throws SQLException { + int numPropertiesToSet = PROPERTY_LIST.size(); + + for (int i = 0; i < numPropertiesToSet; i++) { + java.lang.reflect.Field propertyField = PROPERTY_LIST.get(i); + + try { + ConnectionProperty propToStore = (ConnectionProperty) propertyField.get(this); + + if (ref != null) { + propToStore.storeTo(ref); + } + } catch (IllegalAccessException iae) { + throw SQLError.createSQLException(Messages.getString("ConnectionProperties.errorNotExpected"), getExceptionInterceptor()); + } + } + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#useUnbufferedInput() + */ + public boolean useUnbufferedInput() { + return this.useUnbufferedInput.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseCursorFetch() + */ + public boolean getUseCursorFetch() { + return this.useCursorFetch.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseCursorFetch(boolean) + */ + public void setUseCursorFetch(boolean flag) { + this.useCursorFetch.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getOverrideSupportsIntegrityEnhancementFacility() + */ + public boolean getOverrideSupportsIntegrityEnhancementFacility() { + return this.overrideSupportsIntegrityEnhancementFacility.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setOverrideSupportsIntegrityEnhancementFacility(boolean) + */ + public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) { + this.overrideSupportsIntegrityEnhancementFacility.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNoTimezoneConversionForTimeType() + */ + public boolean getNoTimezoneConversionForTimeType() { + return this.noTimezoneConversionForTimeType.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNoTimezoneConversionForTimeType(boolean) + */ + public void setNoTimezoneConversionForTimeType(boolean flag) { + this.noTimezoneConversionForTimeType.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNoTimezoneConversionForDateType() + */ + public boolean getNoTimezoneConversionForDateType() { + return this.noTimezoneConversionForDateType.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNoTimezoneConversionForDateType(boolean) + */ + public void setNoTimezoneConversionForDateType(boolean flag) { + this.noTimezoneConversionForDateType.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCacheDefaultTimezone() + */ + public boolean getCacheDefaultTimezone() { + return this.cacheDefaultTimezone.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCacheDefaultTimezone(boolean) + */ + public void setCacheDefaultTimezone(boolean flag) { + this.cacheDefaultTimezone.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseJDBCCompliantTimezoneShift() + */ + public boolean getUseJDBCCompliantTimezoneShift() { + return this.useJDBCCompliantTimezoneShift.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseJDBCCompliantTimezoneShift(boolean) + */ + public void setUseJDBCCompliantTimezoneShift(boolean flag) { + this.useJDBCCompliantTimezoneShift.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getAutoClosePStmtStreams() + */ + public boolean getAutoClosePStmtStreams() { + return this.autoClosePStmtStreams.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setAutoClosePStmtStreams(boolean) + */ + public void setAutoClosePStmtStreams(boolean flag) { + this.autoClosePStmtStreams.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getProcessEscapeCodesForPrepStmts() + */ + public boolean getProcessEscapeCodesForPrepStmts() { + return this.processEscapeCodesForPrepStmts.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setProcessEscapeCodesForPrepStmts(boolean) + */ + public void setProcessEscapeCodesForPrepStmts(boolean flag) { + this.processEscapeCodesForPrepStmts.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseGmtMillisForDatetimes() + */ + public boolean getUseGmtMillisForDatetimes() { + return this.useGmtMillisForDatetimes.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseGmtMillisForDatetimes(boolean) + */ + public void setUseGmtMillisForDatetimes(boolean flag) { + this.useGmtMillisForDatetimes.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getDumpMetadataOnColumnNotFound() + */ + public boolean getDumpMetadataOnColumnNotFound() { + return this.dumpMetadataOnColumnNotFound.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setDumpMetadataOnColumnNotFound(boolean) + */ + public void setDumpMetadataOnColumnNotFound(boolean flag) { + this.dumpMetadataOnColumnNotFound.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getResourceId() + */ + public String getResourceId() { + return this.resourceId.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setResourceId(java.lang.String) + */ + public void setResourceId(String resourceId) { + this.resourceId.setValue(resourceId); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getRewriteBatchedStatements() + */ + public boolean getRewriteBatchedStatements() { + return this.rewriteBatchedStatements.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setRewriteBatchedStatements(boolean) + */ + public void setRewriteBatchedStatements(boolean flag) { + this.rewriteBatchedStatements.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getJdbcCompliantTruncationForReads() + */ + public boolean getJdbcCompliantTruncationForReads() { + return this.jdbcCompliantTruncationForReads; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setJdbcCompliantTruncationForReads(boolean) + */ + public void setJdbcCompliantTruncationForReads(boolean jdbcCompliantTruncationForReads) { + this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseJvmCharsetConverters() + */ + public boolean getUseJvmCharsetConverters() { + return this.useJvmCharsetConverters.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseJvmCharsetConverters(boolean) + */ + public void setUseJvmCharsetConverters(boolean flag) { + this.useJvmCharsetConverters.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPinGlobalTxToPhysicalConnection() + */ + public boolean getPinGlobalTxToPhysicalConnection() { + return this.pinGlobalTxToPhysicalConnection.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPinGlobalTxToPhysicalConnection(boolean) + */ + public void setPinGlobalTxToPhysicalConnection(boolean flag) { + this.pinGlobalTxToPhysicalConnection.setValue(flag); + } + + /* + * "Aliases" which match the property names to make using + * from datasources easier. + */ + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setGatherPerfMetrics(boolean) + */ + public void setGatherPerfMetrics(boolean flag) { + setGatherPerformanceMetrics(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getGatherPerfMetrics() + */ + public boolean getGatherPerfMetrics() { + return getGatherPerformanceMetrics(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUltraDevHack(boolean) + */ + public void setUltraDevHack(boolean flag) { + setUseUltraDevWorkAround(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUltraDevHack() + */ + public boolean getUltraDevHack() { + return getUseUltraDevWorkAround(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setInteractiveClient(boolean) + */ + public void setInteractiveClient(boolean property) { + setIsInteractiveClient(property); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setSocketFactory(java.lang.String) + */ + public void setSocketFactory(String name) { + setSocketFactoryClassName(name); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getSocketFactory() + */ + public String getSocketFactory() { + return getSocketFactoryClassName(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseServerPrepStmts(boolean) + */ + public void setUseServerPrepStmts(boolean flag) { + setUseServerPreparedStmts(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseServerPrepStmts() + */ + public boolean getUseServerPrepStmts() { + return getUseServerPreparedStmts(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCacheCallableStmts(boolean) + */ + public void setCacheCallableStmts(boolean flag) { + setCacheCallableStatements(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCacheCallableStmts() + */ + public boolean getCacheCallableStmts() { + return getCacheCallableStatements(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCachePrepStmts(boolean) + */ + public void setCachePrepStmts(boolean flag) { + setCachePreparedStatements(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCachePrepStmts() + */ + public boolean getCachePrepStmts() { + return getCachePreparedStatements(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setCallableStmtCacheSize(int) + */ + public void setCallableStmtCacheSize(int cacheSize) throws SQLException { + setCallableStatementCacheSize(cacheSize); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getCallableStmtCacheSize() + */ + public int getCallableStmtCacheSize() { + return getCallableStatementCacheSize(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPrepStmtCacheSize(int) + */ + public void setPrepStmtCacheSize(int cacheSize) throws SQLException { + setPreparedStatementCacheSize(cacheSize); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPrepStmtCacheSize() + */ + public int getPrepStmtCacheSize() { + return getPreparedStatementCacheSize(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPrepStmtCacheSqlLimit(int) + */ + public void setPrepStmtCacheSqlLimit(int sqlLimit) throws SQLException { + setPreparedStatementCacheSqlLimit(sqlLimit); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPrepStmtCacheSqlLimit() + */ + public int getPrepStmtCacheSqlLimit() { + return getPreparedStatementCacheSqlLimit(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNoAccessToProcedureBodies() + */ + public boolean getNoAccessToProcedureBodies() { + return this.noAccessToProcedureBodies.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNoAccessToProcedureBodies(boolean) + */ + public void setNoAccessToProcedureBodies(boolean flag) { + this.noAccessToProcedureBodies.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseOldAliasMetadataBehavior() + */ + public boolean getUseOldAliasMetadataBehavior() { + return this.useOldAliasMetadataBehavior.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseOldAliasMetadataBehavior(boolean) + */ + public void setUseOldAliasMetadataBehavior(boolean flag) { + this.useOldAliasMetadataBehavior.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStorePassword() + */ + public String getClientCertificateKeyStorePassword() { + return this.clientCertificateKeyStorePassword.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStorePassword(java.lang.String) + */ + public void setClientCertificateKeyStorePassword(String value) { + this.clientCertificateKeyStorePassword.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStoreType() + */ + public String getClientCertificateKeyStoreType() { + return this.clientCertificateKeyStoreType.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStoreType(java.lang.String) + */ + public void setClientCertificateKeyStoreType(String value) { + this.clientCertificateKeyStoreType.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStoreUrl() + */ + public String getClientCertificateKeyStoreUrl() { + return this.clientCertificateKeyStoreUrl.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStoreUrl(java.lang.String) + */ + public void setClientCertificateKeyStoreUrl(String value) { + this.clientCertificateKeyStoreUrl.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStorePassword() + */ + public String getTrustCertificateKeyStorePassword() { + return this.trustCertificateKeyStorePassword.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStorePassword(java.lang.String) + */ + public void setTrustCertificateKeyStorePassword(String value) { + this.trustCertificateKeyStorePassword.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStoreType() + */ + public String getTrustCertificateKeyStoreType() { + return this.trustCertificateKeyStoreType.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStoreType(java.lang.String) + */ + public void setTrustCertificateKeyStoreType(String value) { + this.trustCertificateKeyStoreType.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStoreUrl() + */ + public String getTrustCertificateKeyStoreUrl() { + return this.trustCertificateKeyStoreUrl.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStoreUrl(java.lang.String) + */ + public void setTrustCertificateKeyStoreUrl(String value) { + this.trustCertificateKeyStoreUrl.setValue(value); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseSSPSCompatibleTimezoneShift() + */ + public boolean getUseSSPSCompatibleTimezoneShift() { + return this.useSSPSCompatibleTimezoneShift.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseSSPSCompatibleTimezoneShift(boolean) + */ + public void setUseSSPSCompatibleTimezoneShift(boolean flag) { + this.useSSPSCompatibleTimezoneShift.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getTreatUtilDateAsTimestamp() + */ + public boolean getTreatUtilDateAsTimestamp() { + return this.treatUtilDateAsTimestamp.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setTreatUtilDateAsTimestamp(boolean) + */ + public void setTreatUtilDateAsTimestamp(boolean flag) { + this.treatUtilDateAsTimestamp.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseFastDateParsing() + */ + public boolean getUseFastDateParsing() { + return this.useFastDateParsing.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseFastDateParsing(boolean) + */ + public void setUseFastDateParsing(boolean flag) { + this.useFastDateParsing.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLocalSocketAddress() + */ + public String getLocalSocketAddress() { + return this.localSocketAddress.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLocalSocketAddress(java.lang.String) + */ + public void setLocalSocketAddress(String address) { + this.localSocketAddress.setValue(address); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseConfigs(java.lang.String) + */ + public void setUseConfigs(String configs) { + this.useConfigs.setValue(configs); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseConfigs() + */ + public String getUseConfigs() { + return this.useConfigs.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getGenerateSimpleParameterMetadata() + */ + public boolean getGenerateSimpleParameterMetadata() { + return this.generateSimpleParameterMetadata.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setGenerateSimpleParameterMetadata(boolean) + */ + public void setGenerateSimpleParameterMetadata(boolean flag) { + this.generateSimpleParameterMetadata.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getLogXaCommands() + */ + public boolean getLogXaCommands() { + return this.logXaCommands.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setLogXaCommands(boolean) + */ + public void setLogXaCommands(boolean flag) { + this.logXaCommands.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getResultSetSizeThreshold() + */ + public int getResultSetSizeThreshold() { + return this.resultSetSizeThreshold.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setResultSetSizeThreshold(int) + */ + public void setResultSetSizeThreshold(int threshold) throws SQLException { + this.resultSetSizeThreshold.setValue(threshold, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getNetTimeoutForStreamingResults() + */ + public int getNetTimeoutForStreamingResults() { + return this.netTimeoutForStreamingResults.getValueAsInt(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setNetTimeoutForStreamingResults(int) + */ + public void setNetTimeoutForStreamingResults(int value) throws SQLException { + this.netTimeoutForStreamingResults.setValue(value, getExceptionInterceptor()); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getEnableQueryTimeouts() + */ + public boolean getEnableQueryTimeouts() { + return this.enableQueryTimeouts.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setEnableQueryTimeouts(boolean) + */ + public void setEnableQueryTimeouts(boolean flag) { + this.enableQueryTimeouts.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getPadCharsWithSpace() + */ + public boolean getPadCharsWithSpace() { + return this.padCharsWithSpace.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setPadCharsWithSpace(boolean) + */ + public void setPadCharsWithSpace(boolean flag) { + this.padCharsWithSpace.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getUseDynamicCharsetInfo() + */ + public boolean getUseDynamicCharsetInfo() { + return this.useDynamicCharsetInfo.getValueAsBoolean(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setUseDynamicCharsetInfo(boolean) + */ + public void setUseDynamicCharsetInfo(boolean flag) { + this.useDynamicCharsetInfo.setValue(flag); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#getClientInfoProvider() + */ + public String getClientInfoProvider() { + return this.clientInfoProvider.getValueAsString(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IConnectionProperties#setClientInfoProvider(java.lang.String) + */ + public void setClientInfoProvider(String classname) { + this.clientInfoProvider.setValue(classname); + } + + public boolean getPopulateInsertRowWithDefaultValues() { + return this.populateInsertRowWithDefaultValues.getValueAsBoolean(); + } + + public void setPopulateInsertRowWithDefaultValues(boolean flag) { + this.populateInsertRowWithDefaultValues.setValue(flag); + } + + public String getLoadBalanceStrategy() { + return this.loadBalanceStrategy.getValueAsString(); + } + + public void setLoadBalanceStrategy(String strategy) { + this.loadBalanceStrategy.setValue(strategy); + } + + public boolean getTcpNoDelay() { + return this.tcpNoDelay.getValueAsBoolean(); + } + + public void setTcpNoDelay(boolean flag) { + this.tcpNoDelay.setValue(flag); + } + + public boolean getTcpKeepAlive() { + return this.tcpKeepAlive.getValueAsBoolean(); + } + + public void setTcpKeepAlive(boolean flag) { + this.tcpKeepAlive.setValue(flag); + } + + public int getTcpRcvBuf() { + return this.tcpRcvBuf.getValueAsInt(); + } + + public void setTcpRcvBuf(int bufSize) throws SQLException { + this.tcpRcvBuf.setValue(bufSize, getExceptionInterceptor()); + } + + public int getTcpSndBuf() { + return this.tcpSndBuf.getValueAsInt(); + } + + public void setTcpSndBuf(int bufSize) throws SQLException { + this.tcpSndBuf.setValue(bufSize, getExceptionInterceptor()); + } + + public int getTcpTrafficClass() { + return this.tcpTrafficClass.getValueAsInt(); + } + + public void setTcpTrafficClass(int classFlags) throws SQLException { + this.tcpTrafficClass.setValue(classFlags, getExceptionInterceptor()); + } + + public boolean getUseNanosForElapsedTime() { + return this.useNanosForElapsedTime.getValueAsBoolean(); + } + + public void setUseNanosForElapsedTime(boolean flag) { + this.useNanosForElapsedTime.setValue(flag); + } + + public long getSlowQueryThresholdNanos() { + return this.slowQueryThresholdNanos.getValueAsLong(); + } + + public void setSlowQueryThresholdNanos(long nanos) throws SQLException { + this.slowQueryThresholdNanos.setValue(nanos, getExceptionInterceptor()); + } + + public String getStatementInterceptors() { + return this.statementInterceptors.getValueAsString(); + } + + public void setStatementInterceptors(String value) { + this.statementInterceptors.setValue(value); + } + + public boolean getUseDirectRowUnpack() { + return this.useDirectRowUnpack.getValueAsBoolean(); + } + + public void setUseDirectRowUnpack(boolean flag) { + this.useDirectRowUnpack.setValue(flag); + } + + public String getLargeRowSizeThreshold() { + return this.largeRowSizeThreshold.getValueAsString(); + } + + public void setLargeRowSizeThreshold(String value) throws SQLException { + this.largeRowSizeThreshold.setValue(value, getExceptionInterceptor()); + } + + public boolean getUseBlobToStoreUTF8OutsideBMP() { + return this.useBlobToStoreUTF8OutsideBMP.getValueAsBoolean(); + } + + public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) { + this.useBlobToStoreUTF8OutsideBMP.setValue(flag); + } + + public String getUtf8OutsideBmpExcludedColumnNamePattern() { + return this.utf8OutsideBmpExcludedColumnNamePattern.getValueAsString(); + } + + public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) { + this.utf8OutsideBmpExcludedColumnNamePattern.setValue(regexPattern); + } + + public String getUtf8OutsideBmpIncludedColumnNamePattern() { + return this.utf8OutsideBmpIncludedColumnNamePattern.getValueAsString(); + } + + public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) { + this.utf8OutsideBmpIncludedColumnNamePattern.setValue(regexPattern); + } + + public boolean getIncludeInnodbStatusInDeadlockExceptions() { + return this.includeInnodbStatusInDeadlockExceptions.getValueAsBoolean(); + } + + public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) { + this.includeInnodbStatusInDeadlockExceptions.setValue(flag); + } + + public boolean getBlobsAreStrings() { + return this.blobsAreStrings.getValueAsBoolean(); + } + + public void setBlobsAreStrings(boolean flag) { + this.blobsAreStrings.setValue(flag); + } + + public boolean getFunctionsNeverReturnBlobs() { + return this.functionsNeverReturnBlobs.getValueAsBoolean(); + } + + public void setFunctionsNeverReturnBlobs(boolean flag) { + this.functionsNeverReturnBlobs.setValue(flag); + } + + public boolean getAutoSlowLog() { + return this.autoSlowLog.getValueAsBoolean(); + } + + public void setAutoSlowLog(boolean flag) { + this.autoSlowLog.setValue(flag); + } + + public String getConnectionLifecycleInterceptors() { + return this.connectionLifecycleInterceptors.getValueAsString(); + } + + public void setConnectionLifecycleInterceptors(String interceptors) { + this.connectionLifecycleInterceptors.setValue(interceptors); + } + + public String getProfilerEventHandler() { + return this.profilerEventHandler.getValueAsString(); + } + + public void setProfilerEventHandler(String handler) { + this.profilerEventHandler.setValue(handler); + } + + public boolean getVerifyServerCertificate() { + return this.verifyServerCertificate.getValueAsBoolean(); + } + + public void setVerifyServerCertificate(boolean flag) { + this.verifyServerCertificate.setValue(flag); + } + + public boolean getUseLegacyDatetimeCode() { + return this.useLegacyDatetimeCode.getValueAsBoolean(); + } + + public void setUseLegacyDatetimeCode(boolean flag) { + this.useLegacyDatetimeCode.setValue(flag); + } + + public boolean getSendFractionalSeconds() { + return this.sendFractionalSeconds.getValueAsBoolean(); + } + + public void setSendFractionalSeconds(boolean flag) { + this.sendFractionalSeconds.setValue(flag); + } + + public int getSelfDestructOnPingSecondsLifetime() { + return this.selfDestructOnPingSecondsLifetime.getValueAsInt(); + } + + public void setSelfDestructOnPingSecondsLifetime(int seconds) throws SQLException { + this.selfDestructOnPingSecondsLifetime.setValue(seconds, getExceptionInterceptor()); + } + + public int getSelfDestructOnPingMaxOperations() { + return this.selfDestructOnPingMaxOperations.getValueAsInt(); + } + + public void setSelfDestructOnPingMaxOperations(int maxOperations) throws SQLException { + this.selfDestructOnPingMaxOperations.setValue(maxOperations, getExceptionInterceptor()); + } + + public boolean getUseColumnNamesInFindColumn() { + return this.useColumnNamesInFindColumn.getValueAsBoolean(); + } + + public void setUseColumnNamesInFindColumn(boolean flag) { + this.useColumnNamesInFindColumn.setValue(flag); + } + + public boolean getUseLocalTransactionState() { + return this.useLocalTransactionState.getValueAsBoolean(); + } + + public void setUseLocalTransactionState(boolean flag) { + this.useLocalTransactionState.setValue(flag); + } + + public boolean getCompensateOnDuplicateKeyUpdateCounts() { + return this.compensateOnDuplicateKeyUpdateCounts.getValueAsBoolean(); + } + + public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag) { + this.compensateOnDuplicateKeyUpdateCounts.setValue(flag); + } + + public int getLoadBalanceBlacklistTimeout() { + return this.loadBalanceBlacklistTimeout.getValueAsInt(); + } + + public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) throws SQLException { + this.loadBalanceBlacklistTimeout.setValue(loadBalanceBlacklistTimeout, getExceptionInterceptor()); + } + + public int getLoadBalancePingTimeout() { + return this.loadBalancePingTimeout.getValueAsInt(); + } + + public void setLoadBalancePingTimeout(int loadBalancePingTimeout) throws SQLException { + this.loadBalancePingTimeout.setValue(loadBalancePingTimeout, getExceptionInterceptor()); + } + + public void setRetriesAllDown(int retriesAllDown) throws SQLException { + this.retriesAllDown.setValue(retriesAllDown, getExceptionInterceptor()); + } + + public int getRetriesAllDown() { + return this.retriesAllDown.getValueAsInt(); + } + + public void setUseAffectedRows(boolean flag) { + this.useAffectedRows.setValue(flag); + } + + public boolean getUseAffectedRows() { + return this.useAffectedRows.getValueAsBoolean(); + } + + public void setPasswordCharacterEncoding(String characterSet) { + this.passwordCharacterEncoding.setValue(characterSet); + } + + public String getPasswordCharacterEncoding() { + String encoding; + if ((encoding = this.passwordCharacterEncoding.getValueAsString()) != null) { + return encoding; + } + if (getUseUnicode() && (encoding = getEncoding()) != null) { + return encoding; + } + return "UTF-8"; + } + + public void setExceptionInterceptors(String exceptionInterceptors) { + this.exceptionInterceptors.setValue(exceptionInterceptors); + } + + public String getExceptionInterceptors() { + return this.exceptionInterceptors.getValueAsString(); + } + + public void setMaxAllowedPacket(int max) throws SQLException { + this.maxAllowedPacket.setValue(max, getExceptionInterceptor()); + } + + public int getMaxAllowedPacket() { + return this.maxAllowedPacket.getValueAsInt(); + } + + public boolean getQueryTimeoutKillsConnection() { + return this.queryTimeoutKillsConnection.getValueAsBoolean(); + } + + public void setQueryTimeoutKillsConnection(boolean queryTimeoutKillsConnection) { + this.queryTimeoutKillsConnection.setValue(queryTimeoutKillsConnection); + } + + public boolean getLoadBalanceValidateConnectionOnSwapServer() { + return this.loadBalanceValidateConnectionOnSwapServer.getValueAsBoolean(); + } + + public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer) { + this.loadBalanceValidateConnectionOnSwapServer.setValue(loadBalanceValidateConnectionOnSwapServer); + } + + public String getLoadBalanceConnectionGroup() { + return this.loadBalanceConnectionGroup.getValueAsString(); + } + + public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup) { + this.loadBalanceConnectionGroup.setValue(loadBalanceConnectionGroup); + } + + public String getLoadBalanceExceptionChecker() { + return this.loadBalanceExceptionChecker.getValueAsString(); + } + + public void setLoadBalanceExceptionChecker(String loadBalanceExceptionChecker) { + this.loadBalanceExceptionChecker.setValue(loadBalanceExceptionChecker); + } + + public String getLoadBalanceSQLStateFailover() { + return this.loadBalanceSQLStateFailover.getValueAsString(); + } + + public void setLoadBalanceSQLStateFailover(String loadBalanceSQLStateFailover) { + this.loadBalanceSQLStateFailover.setValue(loadBalanceSQLStateFailover); + } + + public String getLoadBalanceSQLExceptionSubclassFailover() { + return this.loadBalanceSQLExceptionSubclassFailover.getValueAsString(); + } + + public void setLoadBalanceSQLExceptionSubclassFailover(String loadBalanceSQLExceptionSubclassFailover) { + this.loadBalanceSQLExceptionSubclassFailover.setValue(loadBalanceSQLExceptionSubclassFailover); + } + + public boolean getLoadBalanceEnableJMX() { + return this.loadBalanceEnableJMX.getValueAsBoolean(); + } + + public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX) { + this.loadBalanceEnableJMX.setValue(loadBalanceEnableJMX); + } + + public void setLoadBalanceHostRemovalGracePeriod(int loadBalanceHostRemovalGracePeriod) throws SQLException { + this.loadBalanceHostRemovalGracePeriod.setValue(loadBalanceHostRemovalGracePeriod, getExceptionInterceptor()); + } + + public int getLoadBalanceHostRemovalGracePeriod() { + return this.loadBalanceHostRemovalGracePeriod.getValueAsInt(); + } + + public void setLoadBalanceAutoCommitStatementThreshold(int loadBalanceAutoCommitStatementThreshold) throws SQLException { + this.loadBalanceAutoCommitStatementThreshold.setValue(loadBalanceAutoCommitStatementThreshold, getExceptionInterceptor()); + } + + public int getLoadBalanceAutoCommitStatementThreshold() { + return this.loadBalanceAutoCommitStatementThreshold.getValueAsInt(); + } + + public void setLoadBalanceAutoCommitStatementRegex(String loadBalanceAutoCommitStatementRegex) { + this.loadBalanceAutoCommitStatementRegex.setValue(loadBalanceAutoCommitStatementRegex); + } + + public String getLoadBalanceAutoCommitStatementRegex() { + return this.loadBalanceAutoCommitStatementRegex.getValueAsString(); + } + + public void setIncludeThreadDumpInDeadlockExceptions(boolean flag) { + this.includeThreadDumpInDeadlockExceptions.setValue(flag); + } + + public boolean getIncludeThreadDumpInDeadlockExceptions() { + return this.includeThreadDumpInDeadlockExceptions.getValueAsBoolean(); + } + + public void setIncludeThreadNamesAsStatementComment(boolean flag) { + this.includeThreadNamesAsStatementComment.setValue(flag); + } + + public boolean getIncludeThreadNamesAsStatementComment() { + return this.includeThreadNamesAsStatementComment.getValueAsBoolean(); + } + + public void setAuthenticationPlugins(String authenticationPlugins) { + this.authenticationPlugins.setValue(authenticationPlugins); + } + + public String getAuthenticationPlugins() { + return this.authenticationPlugins.getValueAsString(); + } + + public void setDisabledAuthenticationPlugins(String disabledAuthenticationPlugins) { + this.disabledAuthenticationPlugins.setValue(disabledAuthenticationPlugins); + } + + public String getDisabledAuthenticationPlugins() { + return this.disabledAuthenticationPlugins.getValueAsString(); + } + + public void setDefaultAuthenticationPlugin(String defaultAuthenticationPlugin) { + this.defaultAuthenticationPlugin.setValue(defaultAuthenticationPlugin); + } + + public String getDefaultAuthenticationPlugin() { + return this.defaultAuthenticationPlugin.getValueAsString(); + } + + public void setParseInfoCacheFactory(String factoryClassname) { + this.parseInfoCacheFactory.setValue(factoryClassname); + } + + public String getParseInfoCacheFactory() { + return this.parseInfoCacheFactory.getValueAsString(); + } + + public void setServerConfigCacheFactory(String factoryClassname) { + this.serverConfigCacheFactory.setValue(factoryClassname); + } + + public String getServerConfigCacheFactory() { + return this.serverConfigCacheFactory.getValueAsString(); + } + + public void setDisconnectOnExpiredPasswords(boolean disconnectOnExpiredPasswords) { + this.disconnectOnExpiredPasswords.setValue(disconnectOnExpiredPasswords); + } + + public boolean getDisconnectOnExpiredPasswords() { + return this.disconnectOnExpiredPasswords.getValueAsBoolean(); + } + + public boolean getAllowMasterDownConnections() { + return this.allowMasterDownConnections.getValueAsBoolean(); + } + + public void setAllowMasterDownConnections(boolean connectIfMasterDown) { + this.allowMasterDownConnections.setValue(connectIfMasterDown); + } + + public boolean getAllowSlaveDownConnections() { + return this.allowSlaveDownConnections.getValueAsBoolean(); + } + + public void setAllowSlaveDownConnections(boolean connectIfSlaveDown) { + this.allowSlaveDownConnections.setValue(connectIfSlaveDown); + } + + public boolean getReadFromMasterWhenNoSlaves() { + return this.readFromMasterWhenNoSlaves.getValueAsBoolean(); + } + + public void setReadFromMasterWhenNoSlaves(boolean useMasterIfSlavesDown) { + this.readFromMasterWhenNoSlaves.setValue(useMasterIfSlavesDown); + } + + public boolean getReplicationEnableJMX() { + return this.replicationEnableJMX.getValueAsBoolean(); + } + + public void setReplicationEnableJMX(boolean replicationEnableJMX) { + this.replicationEnableJMX.setValue(replicationEnableJMX); + } + + public void setGetProceduresReturnsFunctions(boolean getProcedureReturnsFunctions) { + this.getProceduresReturnsFunctions.setValue(getProcedureReturnsFunctions); + } + + public boolean getGetProceduresReturnsFunctions() { + return this.getProceduresReturnsFunctions.getValueAsBoolean(); + } + + public void setDetectCustomCollations(boolean detectCustomCollations) { + this.detectCustomCollations.setValue(detectCustomCollations); + } + + public boolean getDetectCustomCollations() { + return this.detectCustomCollations.getValueAsBoolean(); + } + + public String getServerRSAPublicKeyFile() { + return this.serverRSAPublicKeyFile.getValueAsString(); + } + + public void setServerRSAPublicKeyFile(String serverRSAPublicKeyFile) throws SQLException { + if (this.serverRSAPublicKeyFile.getUpdateCount() > 0) { + throw SQLError.createSQLException(Messages.getString("ConnectionProperties.dynamicChangeIsNotAllowed", new Object[] { "'serverRSAPublicKeyFile'" }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + this.serverRSAPublicKeyFile.setValue(serverRSAPublicKeyFile); + } + + public boolean getAllowPublicKeyRetrieval() { + return this.allowPublicKeyRetrieval.getValueAsBoolean(); + } + + public void setAllowPublicKeyRetrieval(boolean allowPublicKeyRetrieval) throws SQLException { + if (this.allowPublicKeyRetrieval.getUpdateCount() > 0) { + throw SQLError.createSQLException( + Messages.getString("ConnectionProperties.dynamicChangeIsNotAllowed", new Object[] { "'allowPublicKeyRetrieval'" }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + this.allowPublicKeyRetrieval.setValue(allowPublicKeyRetrieval); + } + + public void setDontCheckOnDuplicateKeyUpdateInSQL(boolean dontCheckOnDuplicateKeyUpdateInSQL) { + this.dontCheckOnDuplicateKeyUpdateInSQL.setValue(dontCheckOnDuplicateKeyUpdateInSQL); + } + + public boolean getDontCheckOnDuplicateKeyUpdateInSQL() { + return this.dontCheckOnDuplicateKeyUpdateInSQL.getValueAsBoolean(); + } + + public void setSocksProxyHost(String socksProxyHost) { + this.socksProxyHost.setValue(socksProxyHost); + } + + public String getSocksProxyHost() { + return this.socksProxyHost.getValueAsString(); + } + + public void setSocksProxyPort(int socksProxyPort) throws SQLException { + this.socksProxyPort.setValue(socksProxyPort, null); + } + + public int getSocksProxyPort() { + return this.socksProxyPort.getValueAsInt(); + } + + public boolean getReadOnlyPropagatesToServer() { + return this.readOnlyPropagatesToServer.getValueAsBoolean(); + } + + public void setReadOnlyPropagatesToServer(boolean flag) { + this.readOnlyPropagatesToServer.setValue(flag); + } + + public String getEnabledSSLCipherSuites() { + return this.enabledSSLCipherSuites.getValueAsString(); + } + + public void setEnabledSSLCipherSuites(String cipherSuites) { + this.enabledSSLCipherSuites.setValue(cipherSuites); + } + + public boolean getEnableEscapeProcessing() { + return this.enableEscapeProcessing.getValueAsBoolean(); + } + + public void setEnableEscapeProcessing(boolean flag) { + this.enableEscapeProcessing.setValue(flag); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesTransform.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesTransform.java new file mode 100644 index 0000000..555ec73 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ConnectionPropertiesTransform.java @@ -0,0 +1,49 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +/** + * Implement this interface, and pass the class name as the 'propertiesTransform' property in your JDBC URL, and the driver will pass the properties it has + * parsed to your transform implementation so that you can modify/substitute/add any that you desire. + */ +public interface ConnectionPropertiesTransform { + /** + * The JDBC driver will call this method if the user has loaded your + * implementation of this interface by specifying the 'propertiesTransform' + * property in their JDBC URL. + * + * @param props + * the properties as passed by the driver (never null) + * + * @return the same properties with any transformations that your + * implementation has made + * + * @throws SQLException + * if a transform can not be made for any reason. + */ + public Properties transformProperties(Properties props) throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Constants.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Constants.java new file mode 100644 index 0000000..39597eb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Constants.java @@ -0,0 +1,49 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Represents various constants used in the driver. + */ +public class Constants { + /** + * Avoids allocation of empty byte[] when representing 0-length strings. + */ + public final static byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** + * I18N'd representation of the abbreviation for "ms" + */ + public final static String MILLIS_I18N = Messages.getString("Milliseconds"); + + public final static byte[] SLASH_STAR_SPACE_AS_BYTES = new byte[] { (byte) '/', (byte) '*', (byte) ' ' }; + + public final static byte[] SPACE_STAR_SLASH_SPACE_AS_BYTES = new byte[] { (byte) ' ', (byte) '*', (byte) '/', (byte) ' ' }; + + /** + * Prevents instantiation + */ + private Constants() { + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaData.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaData.java new file mode 100644 index 0000000..c041f78 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaData.java @@ -0,0 +1,7907 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import static com.mysql.jdbc.DatabaseMetaData.ProcedureType.FUNCTION; +import static com.mysql.jdbc.DatabaseMetaData.ProcedureType.PROCEDURE; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * JDBC Interface to Mysql functions + *

    + * This class provides information about the database as a whole. + *

    + *

    + * Many of the methods here return lists of information in ResultSets. You can use the normal ResultSet methods such as getString and getInt to retrieve the + * data from these ResultSets. If a given form of metadata is not available, these methods show throw a SQLException. + *

    + *

    + * Some of these methods take arguments that are String patterns. These methods all have names such as fooPattern. Within a pattern String "%" means match any + * substring of 0 or more characters and "_" means match any one character. + *

    + */ +public class DatabaseMetaData implements java.sql.DatabaseMetaData { + + protected abstract class IteratorWithCleanup { + abstract void close() throws SQLException; + + abstract boolean hasNext() throws SQLException; + + abstract T next() throws SQLException; + } + + class LocalAndReferencedColumns { + String constraintName; + + List localColumnsList; + + String referencedCatalog; + + List referencedColumnsList; + + String referencedTable; + + LocalAndReferencedColumns(List localColumns, List refColumns, String constName, String refCatalog, String refTable) { + this.localColumnsList = localColumns; + this.referencedColumnsList = refColumns; + this.constraintName = constName; + this.referencedTable = refTable; + this.referencedCatalog = refCatalog; + } + } + + protected class ResultSetIterator extends IteratorWithCleanup { + int colIndex; + + ResultSet resultSet; + + ResultSetIterator(ResultSet rs, int index) { + this.resultSet = rs; + this.colIndex = index; + } + + @Override + void close() throws SQLException { + this.resultSet.close(); + } + + @Override + boolean hasNext() throws SQLException { + return this.resultSet.next(); + } + + @Override + String next() throws SQLException { + return this.resultSet.getObject(this.colIndex).toString(); + } + } + + protected class SingleStringIterator extends IteratorWithCleanup { + boolean onFirst = true; + + String value; + + SingleStringIterator(String s) { + this.value = s; + } + + @Override + void close() throws SQLException { + // not needed + + } + + @Override + boolean hasNext() throws SQLException { + return this.onFirst; + } + + @Override + String next() throws SQLException { + this.onFirst = false; + return this.value; + } + } + + /** + * Parses and represents common data type information used by various + * column/parameter methods. + */ + class TypeDescriptor { + int bufferLength; + + int charOctetLength; + + Integer columnSize; + + short dataType; + + Integer decimalDigits; + + String isNullable; + + int nullability; + + int numPrecRadix = 10; + + String typeName; + + TypeDescriptor(String typeInfo, String nullabilityInfo) throws SQLException { + if (typeInfo == null) { + throw SQLError.createSQLException("NULL typeinfo not supported.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + String mysqlType = ""; + String fullMysqlType = null; + + if (typeInfo.indexOf("(") != -1) { + mysqlType = typeInfo.substring(0, typeInfo.indexOf("(")).trim(); + } else { + mysqlType = typeInfo; + } + + int indexOfUnsignedInMysqlType = StringUtils.indexOfIgnoreCase(mysqlType, "unsigned"); + + if (indexOfUnsignedInMysqlType != -1) { + mysqlType = mysqlType.substring(0, (indexOfUnsignedInMysqlType - 1)); + } + + // Add unsigned to typename reported to enduser as 'native type', if present + + boolean isUnsigned = false; + + if ((StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) && (StringUtils.indexOfIgnoreCase(typeInfo, "set") != 0) + && (StringUtils.indexOfIgnoreCase(typeInfo, "enum") != 0)) { + fullMysqlType = mysqlType + " unsigned"; + isUnsigned = true; + } else { + fullMysqlType = mysqlType; + } + + if (DatabaseMetaData.this.conn.getCapitalizeTypeNames()) { + fullMysqlType = fullMysqlType.toUpperCase(Locale.ENGLISH); + } + + this.dataType = (short) MysqlDefs.mysqlToJavaType(mysqlType); + + this.typeName = fullMysqlType; + + // Figure Out the Size + + if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")) { + String temp = typeInfo.substring(typeInfo.indexOf("("), typeInfo.lastIndexOf(")")); + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp, ","); + int maxLength = 0; + + while (tokenizer.hasMoreTokens()) { + maxLength = Math.max(maxLength, (tokenizer.nextToken().length() - 2)); + } + + this.columnSize = Integer.valueOf(maxLength); + this.decimalDigits = null; + } else if (StringUtils.startsWithIgnoreCase(typeInfo, "set")) { + String temp = typeInfo.substring(typeInfo.indexOf("(") + 1, typeInfo.lastIndexOf(")")); + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp, ","); + int maxLength = 0; + + int numElements = tokenizer.countTokens(); + + if (numElements > 0) { + maxLength += (numElements - 1); + } + + while (tokenizer.hasMoreTokens()) { + String setMember = tokenizer.nextToken().trim(); + + if (setMember.startsWith("'") && setMember.endsWith("'")) { + maxLength += setMember.length() - 2; + } else { + maxLength += setMember.length(); + } + } + + this.columnSize = Integer.valueOf(maxLength); + this.decimalDigits = null; + } else if (typeInfo.indexOf(",") != -1) { + // Numeric with decimals + this.columnSize = Integer.valueOf(typeInfo.substring((typeInfo.indexOf("(") + 1), (typeInfo.indexOf(","))).trim()); + this.decimalDigits = Integer.valueOf(typeInfo.substring((typeInfo.indexOf(",") + 1), (typeInfo.indexOf(")"))).trim()); + } else { + this.columnSize = null; + this.decimalDigits = null; + + /* If the size is specified with the DDL, use that */ + if ((StringUtils.indexOfIgnoreCase(typeInfo, "char") != -1 || StringUtils.indexOfIgnoreCase(typeInfo, "text") != -1 + || StringUtils.indexOfIgnoreCase(typeInfo, "blob") != -1 || StringUtils.indexOfIgnoreCase(typeInfo, "binary") != -1 + || StringUtils.indexOfIgnoreCase(typeInfo, "bit") != -1) && typeInfo.indexOf("(") != -1) { + int endParenIndex = typeInfo.indexOf(")"); + + if (endParenIndex == -1) { + endParenIndex = typeInfo.length(); + } + + this.columnSize = Integer.valueOf(typeInfo.substring((typeInfo.indexOf("(") + 1), endParenIndex).trim()); + + // Adjust for pseudo-boolean + if (DatabaseMetaData.this.conn.getTinyInt1isBit() && this.columnSize.intValue() == 1 + && StringUtils.startsWithIgnoreCase(typeInfo, 0, "tinyint")) { + if (DatabaseMetaData.this.conn.getTransformedBitIsBoolean()) { + this.dataType = Types.BOOLEAN; + this.typeName = "BOOLEAN"; + } else { + this.dataType = Types.BIT; + this.typeName = "BIT"; + } + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "tinyint")) { + if (DatabaseMetaData.this.conn.getTinyInt1isBit() && typeInfo.indexOf("(1)") != -1) { + if (DatabaseMetaData.this.conn.getTransformedBitIsBoolean()) { + this.dataType = Types.BOOLEAN; + this.typeName = "BOOLEAN"; + } else { + this.dataType = Types.BIT; + this.typeName = "BIT"; + } + } else { + this.columnSize = Integer.valueOf(3); + this.decimalDigits = Integer.valueOf(0); + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "smallint")) { + this.columnSize = Integer.valueOf(5); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "mediumint")) { + this.columnSize = Integer.valueOf(isUnsigned ? 8 : 7); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "int")) { + this.columnSize = Integer.valueOf(10); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "integer")) { + this.columnSize = Integer.valueOf(10); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "bigint")) { + this.columnSize = Integer.valueOf(isUnsigned ? 20 : 19); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "int24")) { + this.columnSize = Integer.valueOf(19); + this.decimalDigits = Integer.valueOf(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "real")) { + this.columnSize = Integer.valueOf(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "float")) { + this.columnSize = Integer.valueOf(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "decimal")) { + this.columnSize = Integer.valueOf(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "numeric")) { + this.columnSize = Integer.valueOf(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "double")) { + this.columnSize = Integer.valueOf(22); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "char")) { + this.columnSize = Integer.valueOf(1); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "varchar")) { + this.columnSize = Integer.valueOf(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "timestamp")) { + this.columnSize = Integer.valueOf(19); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "datetime")) { + this.columnSize = Integer.valueOf(19); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "date")) { + this.columnSize = Integer.valueOf(10); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "time")) { + this.columnSize = Integer.valueOf(8); + + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "tinyblob")) { + this.columnSize = Integer.valueOf(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "blob")) { + this.columnSize = Integer.valueOf(65535); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "mediumblob")) { + this.columnSize = Integer.valueOf(16777215); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "longblob")) { + this.columnSize = Integer.valueOf(Integer.MAX_VALUE); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "tinytext")) { + this.columnSize = Integer.valueOf(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "text")) { + this.columnSize = Integer.valueOf(65535); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "mediumtext")) { + this.columnSize = Integer.valueOf(16777215); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "longtext")) { + this.columnSize = Integer.valueOf(Integer.MAX_VALUE); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "enum")) { + this.columnSize = Integer.valueOf(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, "set")) { + this.columnSize = Integer.valueOf(255); + } + + } + + // BUFFER_LENGTH + this.bufferLength = MysqlIO.getMaxBuf(); + + // NUM_PREC_RADIX (is this right for char?) + this.numPrecRadix = 10; + + // Nullable? + if (nullabilityInfo != null) { + if (nullabilityInfo.equals("YES")) { + this.nullability = java.sql.DatabaseMetaData.columnNullable; + this.isNullable = "YES"; + + } else if (nullabilityInfo.equals("UNKNOWN")) { + this.nullability = java.sql.DatabaseMetaData.columnNullableUnknown; + this.isNullable = ""; + + // IS_NULLABLE + } else { + this.nullability = java.sql.DatabaseMetaData.columnNoNulls; + this.isNullable = "NO"; + } + } else { + this.nullability = java.sql.DatabaseMetaData.columnNoNulls; + this.isNullable = "NO"; + } + } + } + + /** + * Helper class to provide means of comparing indexes by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + */ + protected class IndexMetaDataKey implements Comparable { + Boolean columnNonUnique; + Short columnType; + String columnIndexName; + Short columnOrdinalPosition; + + IndexMetaDataKey(boolean columnNonUnique, short columnType, String columnIndexName, short columnOrdinalPosition) { + this.columnNonUnique = columnNonUnique; + this.columnType = columnType; + this.columnIndexName = columnIndexName; + this.columnOrdinalPosition = columnOrdinalPosition; + } + + public int compareTo(IndexMetaDataKey indexInfoKey) { + int compareResult; + + if ((compareResult = this.columnNonUnique.compareTo(indexInfoKey.columnNonUnique)) != 0) { + return compareResult; + } + if ((compareResult = this.columnType.compareTo(indexInfoKey.columnType)) != 0) { + return compareResult; + } + if ((compareResult = this.columnIndexName.compareTo(indexInfoKey.columnIndexName)) != 0) { + return compareResult; + } + return this.columnOrdinalPosition.compareTo(indexInfoKey.columnOrdinalPosition); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof IndexMetaDataKey)) { + return false; + } + return compareTo((IndexMetaDataKey) obj) == 0; + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + } + + /** + * Helper class to provide means of comparing tables by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM and TABLE_NAME. + */ + protected class TableMetaDataKey implements Comparable { + String tableType; + String tableCat; + String tableSchem; + String tableName; + + TableMetaDataKey(String tableType, String tableCat, String tableSchem, String tableName) { + this.tableType = tableType == null ? "" : tableType; + this.tableCat = tableCat == null ? "" : tableCat; + this.tableSchem = tableSchem == null ? "" : tableSchem; + this.tableName = tableName == null ? "" : tableName; + } + + public int compareTo(TableMetaDataKey tablesKey) { + int compareResult; + + if ((compareResult = this.tableType.compareTo(tablesKey.tableType)) != 0) { + return compareResult; + } + if ((compareResult = this.tableCat.compareTo(tablesKey.tableCat)) != 0) { + return compareResult; + } + if ((compareResult = this.tableSchem.compareTo(tablesKey.tableSchem)) != 0) { + return compareResult; + } + return this.tableName.compareTo(tablesKey.tableName); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof TableMetaDataKey)) { + return false; + } + return compareTo((TableMetaDataKey) obj) == 0; + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + } + + /** + * Helper/wrapper class to provide means of sorting objects by using a sorting key. + */ + protected class ComparableWrapper, V> implements Comparable> { + K key; + V value; + + public ComparableWrapper(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public int compareTo(ComparableWrapper other) { + return getKey().compareTo(other.getKey()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof ComparableWrapper)) { + return false; + } + + Object otherKey = ((ComparableWrapper) obj).getKey(); + return this.key.equals(otherKey); + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + + @Override + public String toString() { + return "{KEY:" + this.key + "; VALUE:" + this.value + "}"; + } + } + + /** + * Enumeration for Table Types + */ + protected enum TableType { + LOCAL_TEMPORARY("LOCAL TEMPORARY"), SYSTEM_TABLE("SYSTEM TABLE"), SYSTEM_VIEW("SYSTEM VIEW"), TABLE("TABLE", new String[] { "BASE TABLE" }), + VIEW("VIEW"), UNKNOWN("UNKNOWN"); + + private String name; + private byte[] nameAsBytes; + private String[] synonyms; + + TableType(String tableTypeName) { + this(tableTypeName, null); + } + + TableType(String tableTypeName, String[] tableTypeSynonyms) { + this.name = tableTypeName; + this.nameAsBytes = tableTypeName.getBytes(); + this.synonyms = tableTypeSynonyms; + } + + String getName() { + return this.name; + } + + byte[] asBytes() { + return this.nameAsBytes; + } + + boolean equalsTo(String tableTypeName) { + return this.name.equalsIgnoreCase(tableTypeName); + } + + static TableType getTableTypeEqualTo(String tableTypeName) { + for (TableType tableType : TableType.values()) { + if (tableType.equalsTo(tableTypeName)) { + return tableType; + } + } + return UNKNOWN; + } + + boolean compliesWith(String tableTypeName) { + if (equalsTo(tableTypeName)) { + return true; + } + if (this.synonyms != null) { + for (String synonym : this.synonyms) { + if (synonym.equalsIgnoreCase(tableTypeName)) { + return true; + } + } + } + return false; + } + + static TableType getTableTypeCompliantWith(String tableTypeName) { + for (TableType tableType : TableType.values()) { + if (tableType.compliesWith(tableTypeName)) { + return tableType; + } + } + return UNKNOWN; + } + } + + /** + * Enumeration for Procedure Types + */ + protected enum ProcedureType { + PROCEDURE, FUNCTION; + } + + protected static final int MAX_IDENTIFIER_LENGTH = 64; + + private static final int DEFERRABILITY = 13; + + private static final int DELETE_RULE = 10; + + private static final int FK_NAME = 11; + + private static final int FKCOLUMN_NAME = 7; + + private static final int FKTABLE_CAT = 4; + + private static final int FKTABLE_NAME = 6; + + private static final int FKTABLE_SCHEM = 5; + + private static final int KEY_SEQ = 8; + + private static final int PK_NAME = 12; + + private static final int PKCOLUMN_NAME = 3; + + // + // Column indexes used by all DBMD foreign key ResultSets + // + private static final int PKTABLE_CAT = 0; + + private static final int PKTABLE_NAME = 2; + + private static final int PKTABLE_SCHEM = 1; + + /** The table type for generic tables that support foreign keys. */ + private static final String SUPPORTS_FK = "SUPPORTS_FK"; + + protected static final byte[] TABLE_AS_BYTES = "TABLE".getBytes(); + + protected static final byte[] SYSTEM_TABLE_AS_BYTES = "SYSTEM TABLE".getBytes(); + + private static final int UPDATE_RULE = 9; + + protected static final byte[] VIEW_AS_BYTES = "VIEW".getBytes(); + + private static final Constructor JDBC_4_DBMD_SHOW_CTOR; + + private static final Constructor JDBC_4_DBMD_IS_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_DBMD_SHOW_CTOR = Class.forName("com.mysql.jdbc.JDBC4DatabaseMetaData") + .getConstructor(new Class[] { com.mysql.jdbc.MySQLConnection.class, String.class }); + JDBC_4_DBMD_IS_CTOR = Class.forName("com.mysql.jdbc.JDBC4DatabaseMetaDataUsingInfoSchema") + .getConstructor(new Class[] { com.mysql.jdbc.MySQLConnection.class, String.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_DBMD_IS_CTOR = null; + JDBC_4_DBMD_SHOW_CTOR = null; + } + } + + // MySQL reserved words (all versions superset) + private static final String[] MYSQL_KEYWORDS = new String[] { "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE", + "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", + "CONDITION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", + "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", + "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "ENCLOSED", "ESCAPED", + "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", "GENERATED", "GET", + "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IN", "INDEX", "INFILE", "INNER", + "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", + "IS", "ITERATE", "JOIN", "KEY", "KEYS", "KILL", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", + "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER_BIND", "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", + "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", + "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", + "PARTITION", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE", "RANGE", "READ", "READS", "READ_WRITE", "REAL", "REFERENCES", "REGEXP", "RELEASE", + "RENAME", "REPEAT", "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", + "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SIGNAL", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", + "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", "SSL", "STARTING", "STORED", "STRAIGHT_JOIN", "TABLE", "TERMINATED", "THEN", + "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE", "USE", + "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "VIRTUAL", "WHEN", "WHERE", "WHILE", + "WITH", "WRITE", "XOR", "YEAR_MONTH", "ZEROFILL" }; + + // SQL:92 reserved words from 'ANSI X3.135-1992, January 4, 1993' + private static final String[] SQL92_KEYWORDS = new String[] { "ABSOLUTE", "ACTION", "ADD", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "AS", "ASC", + "ASSERTION", "AT", "AUTHORIZATION", "AVG", "BEGIN", "BETWEEN", "BIT", "BIT_LENGTH", "BOTH", "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", + "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", "COMMIT", "CONNECT", + "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", + "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DATE", "DAY", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", + "DEFERRED", "DELETE", "DESC", "DESCRIBE", "DESCRIPTOR", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DOMAIN", "DOUBLE", "DROP", "ELSE", "END", + "END-EXEC", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FIRST", "FLOAT", "FOR", + "FOREIGN", "FOUND", "FROM", "FULL", "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GROUP", "HAVING", "HOUR", "IDENTITY", "IMMEDIATE", "IN", "INDICATOR", + "INITIALLY", "INNER", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", "INTO", "IS", "ISOLATION", "JOIN", "KEY", + "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL", "LIKE", "LOCAL", "LOWER", "MATCH", "MAX", "MIN", "MINUTE", "MODULE", "MONTH", "NAMES", "NATIONAL", + "NATURAL", "NCHAR", "NEXT", "NO", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "OUTER", + "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "POSITION", "PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", + "READ", "REAL", "REFERENCES", "RELATIVE", "RESTRICT", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL", "SECOND", "SECTION", "SELECT", + "SESSION", "SESSION_USER", "SET", "SIZE", "SMALLINT", "SOME", "SPACE", "SQL", "SQLCODE", "SQLERROR", "SQLSTATE", "SUBSTRING", "SUM", "SYSTEM_USER", + "TABLE", "TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", + "TRIM", "TRUE", "UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPPER", "USAGE", "USER", "USING", "VALUE", "VALUES", "VARCHAR", "VARYING", "VIEW", "WHEN", + "WHENEVER", "WHERE", "WITH", "WORK", "WRITE", "YEAR", "ZONE" }; + + // SQL:2003 reserved words from 'ISO/IEC 9075-2:2003 (E), 2003-07-25' + private static final String[] SQL2003_KEYWORDS = new String[] { "ABS", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "ARRAY", "AS", "ASENSITIVE", + "ASYMMETRIC", "AT", "ATOMIC", "AUTHORIZATION", "AVG", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BY", "CALL", "CALLED", + "CARDINALITY", "CASCADED", "CASE", "CAST", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", + "COALESCE", "COLLATE", "COLLECT", "COLUMN", "COMMIT", "CONDITION", "CONNECT", "CONSTRAINT", "CONVERT", "CORR", "CORRESPONDING", "COUNT", + "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", + "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATE", "DAY", + "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DENSE_RANK", "DEREF", "DESCRIBE", "DETERMINISTIC", "DISCONNECT", "DISTINCT", + "DOUBLE", "DROP", "DYNAMIC", "EACH", "ELEMENT", "ELSE", "END", "END-EXEC", "ESCAPE", "EVERY", "EXCEPT", "EXEC", "EXECUTE", "EXISTS", "EXP", + "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FILTER", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FROM", "FULL", "FUNCTION", "FUSION", "GET", + "GLOBAL", "GRANT", "GROUP", "GROUPING", "HAVING", "HOLD", "HOUR", "IDENTITY", "IN", "INDICATOR", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", + "INTEGER", "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "IS", "JOIN", "LANGUAGE", "LARGE", "LATERAL", "LEADING", "LEFT", "LIKE", "LN", "LOCAL", + "LOCALTIME", "LOCALTIMESTAMP", "LOWER", "MATCH", "MAX", "MEMBER", "MERGE", "METHOD", "MIN", "MINUTE", "MOD", "MODIFIES", "MODULE", "MONTH", + "MULTISET", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NO", "NONE", "NORMALIZE", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", + "OLD", "ON", "ONLY", "OPEN", "OR", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "PARAMETER", "PARTITION", "PERCENTILE_CONT", + "PERCENTILE_DISC", "PERCENT_RANK", "POSITION", "POWER", "PRECISION", "PREPARE", "PRIMARY", "PROCEDURE", "RANGE", "RANK", "READS", "REAL", + "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", + "REGR_SXY", "REGR_SYY", "RELEASE", "RESULT", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLLBACK", "ROLLUP", "ROW", "ROWS", "ROW_NUMBER", "SAVEPOINT", + "SCOPE", "SCROLL", "SEARCH", "SECOND", "SELECT", "SENSITIVE", "SESSION_USER", "SET", "SIMILAR", "SMALLINT", "SOME", "SPECIFIC", "SPECIFICTYPE", + "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQRT", "START", "STATIC", "STDDEV_POP", "STDDEV_SAMP", "SUBMULTISET", "SUBSTRING", "SUM", + "SYMMETRIC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", + "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "UESCAPE", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UPDATE", "UPPER", "USER", + "USING", "VALUE", "VALUES", "VARCHAR", "VARYING", "VAR_POP", "VAR_SAMP", "WHEN", "WHENEVER", "WHERE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", + "WITHOUT", "YEAR" }; + + private static volatile String mysqlKeywords = null; + + /** The connection to the database */ + protected MySQLConnection conn; + + /** The 'current' database name being used */ + protected String database = null; + + /** What character to use when quoting identifiers */ + protected final String quotedId; + + // We need to provide factory-style methods so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise the class verifier complains... + + /** + * + * @param connToSet + * must not be null + * @param databaseToSet + * @param checkForInfoSchema + * @return + * @throws SQLException + */ + protected static DatabaseMetaData getInstance(MySQLConnection connToSet, String databaseToSet, boolean checkForInfoSchema) throws SQLException { + if (!Util.isJdbc4()) { + if (checkForInfoSchema && connToSet.getUseInformationSchema() && connToSet.versionMeetsMinimum(5, 0, 7)) { + return new DatabaseMetaDataUsingInfoSchema(connToSet, databaseToSet); + } + + return new DatabaseMetaData(connToSet, databaseToSet); + } + + if (checkForInfoSchema && connToSet.getUseInformationSchema() && connToSet.versionMeetsMinimum(5, 0, 7)) { + + return (DatabaseMetaData) Util.handleNewInstance(JDBC_4_DBMD_IS_CTOR, new Object[] { connToSet, databaseToSet }, + connToSet.getExceptionInterceptor()); + } + + return (DatabaseMetaData) Util.handleNewInstance(JDBC_4_DBMD_SHOW_CTOR, new Object[] { connToSet, databaseToSet }, connToSet.getExceptionInterceptor()); + } + + /** + * Creates a new DatabaseMetaData object. + * + * @param connToSet + * @param databaseToSet + */ + protected DatabaseMetaData(MySQLConnection connToSet, String databaseToSet) { + this.conn = connToSet; + this.database = databaseToSet; + this.exceptionInterceptor = this.conn.getExceptionInterceptor(); + + String identifierQuote = null; + try { + identifierQuote = getIdentifierQuoteString(); + } catch (SQLException sqlEx) { + // Forced by API, never thrown from getIdentifierQuoteString() in this implementation. + AssertionFailedException.shouldNotHappen(sqlEx); + } finally { + this.quotedId = identifierQuote; + } + } + + /** + * Can all the procedures returned by getProcedures be called by the current + * user? + * + * @return true if so + * @throws SQLException + */ + public boolean allProceduresAreCallable() throws SQLException { + return false; + } + + /** + * Can all the tables returned by getTable be SELECTed by the current user? + * + * @return true if so + * @throws SQLException + */ + public boolean allTablesAreSelectable() throws SQLException { + return false; + } + + private java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields, java.util.ArrayList rows) throws SQLException { + return buildResultSet(fields, rows, this.conn); + } + + static java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields, java.util.ArrayList rows, MySQLConnection c) throws SQLException { + int fieldsLength = fields.length; + + for (int i = 0; i < fieldsLength; i++) { + int jdbcType = fields[i].getSQLType(); + + switch (jdbcType) { + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + fields[i].setEncoding(c.getCharacterSetMetadata(), c); + break; + default: + // do nothing + } + + fields[i].setConnection(c); + fields[i].setUseOldNameMetadata(true); + } + + return com.mysql.jdbc.ResultSetImpl.getInstance(c.getCatalog(), fields, new RowDataStatic(rows), c, null, false); + } + + protected void convertToJdbcFunctionList(String catalog, ResultSet proceduresRs, boolean needsClientFiltering, String db, + List> procedureRows, int nameIndex, Field[] fields) throws SQLException { + while (proceduresRs.next()) { + boolean shouldAdd = true; + + if (needsClientFiltering) { + shouldAdd = false; + + String procDb = proceduresRs.getString(1); + + if (db == null && procDb == null) { + shouldAdd = true; + } else if (db != null && db.equals(procDb)) { + shouldAdd = true; + } + } + + if (shouldAdd) { + String functionName = proceduresRs.getString(nameIndex); + + byte[][] rowData = null; + + if (fields != null && fields.length == 9) { + + rowData = new byte[9][]; + rowData[0] = catalog == null ? null : s2b(catalog); // PROCEDURE_CAT + rowData[1] = null; // PROCEDURE_SCHEM + rowData[2] = s2b(functionName); // PROCEDURE_NAME + rowData[3] = null; // reserved1 + rowData[4] = null; // reserved2 + rowData[5] = null; // reserved3 + rowData[6] = s2b(proceduresRs.getString("comment")); // REMARKS + rowData[7] = s2b(Integer.toString(procedureReturnsResult)); // PROCEDURE_TYPE + rowData[8] = s2b(functionName); + } else { + + rowData = new byte[6][]; + + rowData[0] = catalog == null ? null : s2b(catalog); // FUNCTION_CAT + rowData[1] = null; // FUNCTION_SCHEM + rowData[2] = s2b(functionName); // FUNCTION_NAME + rowData[3] = s2b(proceduresRs.getString("comment")); // REMARKS + rowData[4] = s2b(Integer.toString(getJDBC4FunctionNoTableConstant())); // FUNCTION_TYPE + rowData[5] = s2b(functionName); // SPECFIC NAME + } + + procedureRows.add(new ComparableWrapper(getFullyQualifiedName(catalog, functionName), + new ByteArrayRow(rowData, getExceptionInterceptor()))); + } + } + } + + /** + * Builds and returns a fully qualified name, quoted if necessary, for the given catalog and database entity. + */ + protected String getFullyQualifiedName(String catalog, String entity) { + StringBuilder fullyQualifiedName = new StringBuilder( + StringUtils.quoteIdentifier(catalog == null ? "" : catalog, this.quotedId, this.conn.getPedantic())); + fullyQualifiedName.append('.'); + fullyQualifiedName.append(StringUtils.quoteIdentifier(entity, this.quotedId, this.conn.getPedantic())); + return fullyQualifiedName.toString(); + } + + /** + * Getter to JDBC4 DatabaseMetaData.functionNoTable constant. + * This method must be overridden by JDBC4 subclasses. This implementation should never be called. + * + * @return 0 + */ + protected int getJDBC4FunctionNoTableConstant() { + return 0; + } + + protected void convertToJdbcProcedureList(boolean fromSelect, String catalog, ResultSet proceduresRs, boolean needsClientFiltering, String db, + List> procedureRows, int nameIndex) throws SQLException { + while (proceduresRs.next()) { + boolean shouldAdd = true; + + if (needsClientFiltering) { + shouldAdd = false; + + String procDb = proceduresRs.getString(1); + + if (db == null && procDb == null) { + shouldAdd = true; + } else if (db != null && db.equals(procDb)) { + shouldAdd = true; + } + } + + if (shouldAdd) { + String procedureName = proceduresRs.getString(nameIndex); + byte[][] rowData = new byte[9][]; + rowData[0] = catalog == null ? null : s2b(catalog); + rowData[1] = null; + rowData[2] = s2b(procedureName); + rowData[3] = null; + rowData[4] = null; + rowData[5] = null; + rowData[6] = s2b(proceduresRs.getString("comment")); + + boolean isFunction = fromSelect ? "FUNCTION".equalsIgnoreCase(proceduresRs.getString("type")) : false; + rowData[7] = s2b(isFunction ? Integer.toString(procedureReturnsResult) : Integer.toString(procedureNoResult)); + + rowData[8] = s2b(procedureName); + + procedureRows.add(new ComparableWrapper(getFullyQualifiedName(catalog, procedureName), + new ByteArrayRow(rowData, getExceptionInterceptor()))); + } + } + } + + private ResultSetRow convertTypeDescriptorToProcedureRow(byte[] procNameAsBytes, byte[] procCatAsBytes, String paramName, boolean isOutParam, + boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc, boolean forGetFunctionColumns, int ordinal) throws SQLException { + byte[][] row = forGetFunctionColumns ? new byte[17][] : new byte[20][]; + row[0] = procCatAsBytes; // PROCEDURE_CAT + row[1] = null; // PROCEDURE_SCHEM + row[2] = procNameAsBytes; // PROCEDURE/NAME + row[3] = s2b(paramName); // COLUMN_NAME + row[4] = s2b(String.valueOf(getColumnType(isOutParam, isInParam, isReturnParam, forGetFunctionColumns))); // COLUMN_TYPE + row[5] = s2b(Short.toString(typeDesc.dataType)); // DATA_TYPE + row[6] = s2b(typeDesc.typeName); // TYPE_NAME + row[7] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString()); // PRECISION + row[8] = row[7]; // LENGTH + row[9] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); // SCALE + row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX + // Map 'column****' to 'procedure****' + switch (typeDesc.nullability) { + case columnNoNulls: + row[11] = s2b(String.valueOf(procedureNoNulls)); // NULLABLE + break; + + case columnNullable: + row[11] = s2b(String.valueOf(procedureNullable)); // NULLABLE + break; + + case columnNullableUnknown: + row[11] = s2b(String.valueOf(procedureNullableUnknown)); // NULLABLE + break; + + default: + throw SQLError.createSQLException("Internal error while parsing callable statement metadata (unknown nullability value fount)", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + row[12] = null; + + if (forGetFunctionColumns) { + // CHAR_OCTECT_LENGTH + row[13] = null; + + // ORDINAL_POSITION + row[14] = s2b(String.valueOf(ordinal)); + + // IS_NULLABLE + row[15] = s2b(typeDesc.isNullable); + + // SPECIFIC_NAME + row[16] = procNameAsBytes; + } else { + // COLUMN_DEF + row[13] = null; + + // SQL_DATA_TYPE (future use) + row[14] = null; + + // SQL_DATETIME_SUB (future use) + row[15] = null; + + // CHAR_OCTET_LENGTH + row[16] = null; + + // ORDINAL_POSITION + row[17] = s2b(String.valueOf(ordinal)); + + // IS_NULLABLE + row[18] = s2b(typeDesc.isNullable); + + // SPECIFIC_NAME + row[19] = procNameAsBytes; + } + + return new ByteArrayRow(row, getExceptionInterceptor()); + } + + /** + * Determines the COLUMN_TYPE information based on parameter type (IN, OUT or INOUT) or function return parameter. + * + * @param isOutParam + * Indicates whether it's an output parameter. + * @param isInParam + * Indicates whether it's an input parameter. + * @param isReturnParam + * Indicates whether it's a function return parameter. + * @param forGetFunctionColumns + * Indicates whether the column belong to a function. This argument is required for JDBC4, in which case + * this method must be overridden to provide the correct functionality. + * + * @return The corresponding COLUMN_TYPE as in java.sql.getProcedureColumns API. + */ + protected int getColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + if (isInParam && isOutParam) { + return procedureColumnInOut; + } else if (isInParam) { + return procedureColumnIn; + } else if (isOutParam) { + return procedureColumnOut; + } else if (isReturnParam) { + return procedureColumnReturn; + } else { + return procedureColumnUnknown; + } + } + + private ExceptionInterceptor exceptionInterceptor; + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + /** + * Does a data definition statement within a transaction force the + * transaction to commit? + * + * @return true if so + * @throws SQLException + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return true; + } + + /** + * Is a data definition statement within a transaction ignored? + * + * @return true if so + * @throws SQLException + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether or not a visible row delete can be detected by + * calling ResultSet.rowDeleted(). If deletesAreDetected() returns false, + * then deleted rows are removed from the result set. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean deletesAreDetected(int type) throws SQLException { + return false; + } + + // ---------------------------------------------------------------------- + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs? + * + * @return true if so + * @throws SQLException + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return true; + } + + /** + * Extracts foreign key info for one table. + * + * @param rows + * the list of rows to add to + * @param rs + * the result set from 'SHOW CREATE TABLE' + * @param catalog + * the database name + * @return the list of rows with new rows added + * @throws SQLException + * if a database access error occurs + */ + public List extractForeignKeyForTable(ArrayList rows, java.sql.ResultSet rs, String catalog) throws SQLException { + byte[][] row = new byte[3][]; + row[0] = rs.getBytes(1); + row[1] = s2b(SUPPORTS_FK); + + String createTableString = rs.getString(2); + StringTokenizer lineTokenizer = new StringTokenizer(createTableString, "\n"); + StringBuilder commentBuf = new StringBuilder("comment; "); + boolean firstTime = true; + + while (lineTokenizer.hasMoreTokens()) { + String line = lineTokenizer.nextToken().trim(); + + String constraintName = null; + + if (StringUtils.startsWithIgnoreCase(line, "CONSTRAINT")) { + boolean usingBackTicks = true; + int beginPos = StringUtils.indexOfQuoteDoubleAware(line, this.quotedId, 0); + + if (beginPos == -1) { + beginPos = line.indexOf("\""); + usingBackTicks = false; + } + + if (beginPos != -1) { + int endPos = -1; + + if (usingBackTicks) { + endPos = StringUtils.indexOfQuoteDoubleAware(line, this.quotedId, beginPos + 1); + } else { + endPos = StringUtils.indexOfQuoteDoubleAware(line, "\"", beginPos + 1); + } + + if (endPos != -1) { + constraintName = line.substring(beginPos + 1, endPos); + line = line.substring(endPos + 1, line.length()).trim(); + } + } + } + + if (line.startsWith("FOREIGN KEY")) { + if (line.endsWith(",")) { + line = line.substring(0, line.length() - 1); + } + + int indexOfFK = line.indexOf("FOREIGN KEY"); + + String localColumnName = null; + String referencedCatalogName = StringUtils.quoteIdentifier(catalog, this.quotedId, this.conn.getPedantic()); + String referencedTableName = null; + String referencedColumnName = null; + + if (indexOfFK != -1) { + int afterFk = indexOfFK + "FOREIGN KEY".length(); + + int indexOfRef = StringUtils.indexOfIgnoreCase(afterFk, line, "REFERENCES", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfRef != -1) { + + int indexOfParenOpen = line.indexOf('(', afterFk); + int indexOfParenClose = StringUtils.indexOfIgnoreCase(indexOfParenOpen, line, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfParenOpen == -1 || indexOfParenClose == -1) { + // throw SQLError.createSQLException(); + } + + localColumnName = line.substring(indexOfParenOpen + 1, indexOfParenClose); + + int afterRef = indexOfRef + "REFERENCES".length(); + + int referencedColumnBegin = StringUtils.indexOfIgnoreCase(afterRef, line, "(", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (referencedColumnBegin != -1) { + referencedTableName = line.substring(afterRef, referencedColumnBegin); + + int referencedColumnEnd = StringUtils.indexOfIgnoreCase(referencedColumnBegin + 1, line, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (referencedColumnEnd != -1) { + referencedColumnName = line.substring(referencedColumnBegin + 1, referencedColumnEnd); + } + + int indexOfCatalogSep = StringUtils.indexOfIgnoreCase(0, referencedTableName, ".", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCatalogSep != -1) { + referencedCatalogName = referencedTableName.substring(0, indexOfCatalogSep); + referencedTableName = referencedTableName.substring(indexOfCatalogSep + 1); + } + } + } + } + + if (!firstTime) { + commentBuf.append("; "); + } else { + firstTime = false; + } + + if (constraintName != null) { + commentBuf.append(constraintName); + } else { + commentBuf.append("not_available"); + } + + commentBuf.append("("); + commentBuf.append(localColumnName); + commentBuf.append(") REFER "); + commentBuf.append(referencedCatalogName); + commentBuf.append("/"); + commentBuf.append(referencedTableName); + commentBuf.append("("); + commentBuf.append(referencedColumnName); + commentBuf.append(")"); + + int lastParenIndex = line.lastIndexOf(")"); + + if (lastParenIndex != (line.length() - 1)) { + String cascadeOptions = line.substring(lastParenIndex + 1); + commentBuf.append(" "); + commentBuf.append(cascadeOptions); + } + } + } + + row[2] = s2b(commentBuf.toString()); + rows.add(new ByteArrayRow(row, getExceptionInterceptor())); + + return rows; + } + + /** + * Creates a result set similar enough to 'SHOW TABLE STATUS' to allow the + * same code to work on extracting the foreign key data + * + * @param connToUse + * the database connection to use + * @param metadata + * the DatabaseMetaData instance calling this method + * @param catalog + * the database name to extract foreign key info for + * @param tableName + * the table to extract foreign key info for + * @return A result set that has the structure of 'show table status' + * @throws SQLException + * if a database access error occurs. + */ + public ResultSet extractForeignKeyFromCreateTable(String catalog, String tableName) throws SQLException { + ArrayList tableList = new ArrayList(); + java.sql.ResultSet rs = null; + java.sql.Statement stmt = null; + + if (tableName != null) { + tableList.add(tableName); + } else { + try { + rs = getTables(catalog, "", "%", new String[] { "TABLE" }); + + while (rs.next()) { + tableList.add(rs.getString("TABLE_NAME")); + } + } finally { + if (rs != null) { + rs.close(); + } + + rs = null; + } + } + + ArrayList rows = new ArrayList(); + Field[] fields = new Field[3]; + fields[0] = new Field("", "Name", Types.CHAR, Integer.MAX_VALUE); + fields[1] = new Field("", "Type", Types.CHAR, 255); + fields[2] = new Field("", "Comment", Types.CHAR, Integer.MAX_VALUE); + + int numTables = tableList.size(); + stmt = this.conn.getMetadataSafeStatement(); + + try { + for (int i = 0; i < numTables; i++) { + String tableToExtract = tableList.get(i); + + String query = new StringBuilder("SHOW CREATE TABLE ").append(getFullyQualifiedName(catalog, tableToExtract)).toString(); + + try { + rs = stmt.executeQuery(query); + } catch (SQLException sqlEx) { + // Table might've disappeared on us, not really an error + String sqlState = sqlEx.getSQLState(); + + if (!"42S02".equals(sqlState) && sqlEx.getErrorCode() != MysqlErrorNumbers.ER_NO_SUCH_TABLE) { + throw sqlEx; + } + + continue; + } + + while (rs.next()) { + extractForeignKeyForTable(rows, rs, catalog); + } + } + } finally { + if (rs != null) { + rs.close(); + } + + rs = null; + + if (stmt != null) { + stmt.close(); + } + + stmt = null; + } + + return buildResultSet(fields, rows); + } + + /** + * @see DatabaseMetaData#getAttributes(String, String, String, String) + */ + public java.sql.ResultSet getAttributes(String arg0, String arg1, String arg2, String arg3) throws SQLException { + Field[] fields = new Field[21]; + fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32); + fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32); + fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32); + fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32); + fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32); + fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32); + fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32); + fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32); + fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32); + fields[10] = new Field("", "REMARKS", Types.CHAR, 32); + fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32); + fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32); + fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 32); + fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32); + fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 32); + fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32); + fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32); + fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32); + fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32); + fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 32); + + return buildResultSet(fields, new ArrayList()); + } + + /** + * Get a description of a table's optimal set of columns that uniquely + * identifies a row. They are ordered by SCOPE. + *

    + * Each column description has the following columns: + *

      + *
    1. SCOPE short => actual scope of result + *
        + *
      • bestRowTemporary - very temporary, while using row
      • + *
      • bestRowTransaction - valid for remainder of current transaction
      • + *
      • bestRowSession - valid for remainder of current session
      • + *
      + *
    2. + *
    3. COLUMN_NAME String => column name
    4. + *
    5. DATA_TYPE short => SQL data type from java.sql.Types
    6. + *
    7. TYPE_NAME String => Data source dependent type name
    8. + *
    9. COLUMN_SIZE int => precision
    10. + *
    11. BUFFER_LENGTH int => not used
    12. + *
    13. DECIMAL_DIGITS short => scale
    14. + *
    15. PSEUDO_COLUMN short => is this a pseudo column like an Oracle ROWID + *
        + *
      • bestRowUnknown - may or may not be pseudo column
      • + *
      • bestRowNotPseudo - is NOT a pseudo column
      • + *
      • bestRowPseudo - is a pseudo column
      • + *
      + *
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name; "" retrieves those without a schema + * @param table + * a table name + * @param scope + * the scope of interest; use same values as SCOPE + * @param nullable + * include columns that are nullable? + * @return ResultSet each row is a column description + * @throws SQLException + */ + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, final String table, int scope, boolean nullable) throws SQLException { + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + Field[] fields = new Field[8]; + fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5); + fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32); + fields[2] = new Field("", "DATA_TYPE", Types.INTEGER, 32); + fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32); + fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10); + fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10); + fields[6] = new Field("", "DECIMAL_DIGITS", Types.SMALLINT, 10); + fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5); + + final ArrayList rows = new ArrayList(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + results = stmt.executeQuery(queryBuf.toString()); + + while (results.next()) { + String keyType = results.getString("Key"); + + if (keyType != null) { + if (StringUtils.startsWithIgnoreCase(keyType, "PRI")) { + byte[][] rowVal = new byte[8][]; + rowVal[0] = Integer.toString(java.sql.DatabaseMetaData.bestRowSession).getBytes(); + rowVal[1] = results.getBytes("Field"); + + String type = results.getString("Type"); + int size = MysqlIO.getMaxBuf(); + int decimals = 0; + + /* + * Parse the Type column from MySQL + */ + if (type.indexOf("enum") != -1) { + String temp = type.substring(type.indexOf("("), type.indexOf(")")); + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp, ","); + int maxLength = 0; + + while (tokenizer.hasMoreTokens()) { + maxLength = Math.max(maxLength, (tokenizer.nextToken().length() - 2)); + } + + size = maxLength; + decimals = 0; + type = "enum"; + } else if (type.indexOf("(") != -1) { + if (type.indexOf(",") != -1) { + size = Integer.parseInt(type.substring(type.indexOf("(") + 1, type.indexOf(","))); + decimals = Integer.parseInt(type.substring(type.indexOf(",") + 1, type.indexOf(")"))); + } else { + size = Integer.parseInt(type.substring(type.indexOf("(") + 1, type.indexOf(")"))); + } + + type = type.substring(0, type.indexOf("(")); + } + + rowVal[2] = s2b(String.valueOf(MysqlDefs.mysqlToJavaType(type))); + rowVal[3] = s2b(type); + rowVal[4] = Integer.toString(size + decimals).getBytes(); + rowVal[5] = Integer.toString(size + decimals).getBytes(); + rowVal[6] = Integer.toString(decimals).getBytes(); + rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.bestRowNotPseudo).getBytes(); + + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } + } + } catch (SQLException sqlEx) { + if (!SQLError.SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = buildResultSet(fields, rows); + + return results; + + } + + /* + * Extract parameter details for Procedures and Functions by parsing the DDL query obtained from SHOW CREATE [PROCEDURE|FUNCTION] ... statements. + * The result rows returned follow the required structure for getProcedureColumns() and getFunctionColumns() methods. + * + * Internal use only. + */ + private void getCallStmtParameterTypes(String catalog, String quotedProcName, ProcedureType procType, String parameterNamePattern, + List resultRows, boolean forGetFunctionColumns) throws SQLException { + java.sql.Statement paramRetrievalStmt = null; + java.sql.ResultSet paramRetrievalRs = null; + + if (parameterNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + parameterNamePattern = "%"; + } else { + throw SQLError.createSQLException("Parameter/Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + String parameterDef = null; + + byte[] procNameAsBytes = null; + byte[] procCatAsBytes = null; + + boolean isProcedureInAnsiMode = false; + String storageDefnDelims = null; + String storageDefnClosures = null; + + try { + paramRetrievalStmt = this.conn.getMetadataSafeStatement(); + + String oldCatalog = this.conn.getCatalog(); + if (this.conn.lowerCaseTableNames() && catalog != null && catalog.length() != 0 && oldCatalog != null && oldCatalog.length() != 0) { + // Workaround for bug in server wrt. to SHOW CREATE PROCEDURE not respecting lower-case table names + + ResultSet rs = null; + + try { + this.conn.setCatalog(StringUtils.unQuoteIdentifier(catalog, this.quotedId)); + rs = paramRetrievalStmt.executeQuery("SELECT DATABASE()"); + rs.next(); + + catalog = rs.getString(1); + + } finally { + + this.conn.setCatalog(oldCatalog); + + if (rs != null) { + rs.close(); + } + } + } + + if (paramRetrievalStmt.getMaxRows() != 0) { + paramRetrievalStmt.setMaxRows(0); + } + + int dotIndex = -1; + + if (!" ".equals(this.quotedId)) { + dotIndex = StringUtils.indexOfIgnoreCase(0, quotedProcName, ".", this.quotedId, this.quotedId, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } else { + dotIndex = quotedProcName.indexOf("."); + } + + String dbName = null; + + if (dotIndex != -1 && (dotIndex + 1) < quotedProcName.length()) { + dbName = quotedProcName.substring(0, dotIndex); + quotedProcName = quotedProcName.substring(dotIndex + 1); + } else { + dbName = StringUtils.quoteIdentifier(catalog, this.quotedId, this.conn.getPedantic()); + } + + // Moved from above so that procName is *without* database as expected by the rest of code + // Removing QuoteChar to get output as it was before PROC_CAT fixes + String tmpProcName = StringUtils.unQuoteIdentifier(quotedProcName, this.quotedId); + try { + procNameAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8"); + } catch (UnsupportedEncodingException ueEx) { + procNameAsBytes = s2b(tmpProcName); + + // Set all fields to connection encoding + } + + tmpProcName = StringUtils.unQuoteIdentifier(dbName, this.quotedId); + try { + procCatAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8"); + } catch (UnsupportedEncodingException ueEx) { + procCatAsBytes = s2b(tmpProcName); + + // Set all fields to connection encoding + } + + // there is no need to quote the identifier here since 'dbName' and 'procName' are guaranteed to be already quoted. + StringBuilder procNameBuf = new StringBuilder(); + procNameBuf.append(dbName); + procNameBuf.append('.'); + procNameBuf.append(quotedProcName); + + String fieldName = null; + if (procType == PROCEDURE) { + paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE PROCEDURE " + procNameBuf.toString()); + fieldName = "Create Procedure"; + } else { + paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE FUNCTION " + procNameBuf.toString()); + fieldName = "Create Function"; + } + + if (paramRetrievalRs.next()) { + String procedureDef = paramRetrievalRs.getString(fieldName); + + if (!this.conn.getNoAccessToProcedureBodies() && (procedureDef == null || procedureDef.length() == 0)) { + throw SQLError.createSQLException( + "User does not have access to metadata required to determine " + + "stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" " + + "to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + try { + String sqlMode = paramRetrievalRs.getString("sql_mode"); + + if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) { + isProcedureInAnsiMode = true; + } + } catch (SQLException sqlEx) { + // doesn't exist + } + + String identifierMarkers = isProcedureInAnsiMode ? "`\"" : "`"; + String identifierAndStringMarkers = "'" + identifierMarkers; + storageDefnDelims = "(" + identifierMarkers; + storageDefnClosures = ")" + identifierMarkers; + + if (procedureDef != null && procedureDef.length() != 0) { + // sanitize/normalize by stripping out comments + procedureDef = StringUtils.stripComments(procedureDef, identifierAndStringMarkers, identifierAndStringMarkers, true, false, true, true); + + int openParenIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, "(", this.quotedId, this.quotedId, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + int endOfParamDeclarationIndex = 0; + + endOfParamDeclarationIndex = endPositionOfParameterDeclaration(openParenIndex, procedureDef, this.quotedId); + + if (procType == FUNCTION) { + + // Grab the return column since it needs + // to go first in the output result set + int returnsIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, " RETURNS ", this.quotedId, this.quotedId, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + int endReturnsDef = findEndOfReturnsClause(procedureDef, returnsIndex); + + // Trim off whitespace after "RETURNS" + + int declarationStart = returnsIndex + "RETURNS ".length(); + + while (declarationStart < procedureDef.length()) { + if (Character.isWhitespace(procedureDef.charAt(declarationStart))) { + declarationStart++; + } else { + break; + } + } + + String returnsDefn = procedureDef.substring(declarationStart, endReturnsDef).trim(); + TypeDescriptor returnDescriptor = new TypeDescriptor(returnsDefn, "YES"); + + resultRows.add(convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, "", false, false, true, returnDescriptor, + forGetFunctionColumns, 0)); + } + + if ((openParenIndex == -1) || (endOfParamDeclarationIndex == -1)) { + // parse error? + throw SQLError.createSQLException("Internal error when parsing callable statement metadata", SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + parameterDef = procedureDef.substring(openParenIndex + 1, endOfParamDeclarationIndex); + } + + } + } finally { + SQLException sqlExRethrow = null; + + if (paramRetrievalRs != null) { + try { + paramRetrievalRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramRetrievalRs = null; + } + + if (paramRetrievalStmt != null) { + try { + paramRetrievalStmt.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramRetrievalStmt = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + + if (parameterDef != null) { + int ordinal = 1; + + List parseList = StringUtils.split(parameterDef, ",", storageDefnDelims, storageDefnClosures, true); + + int parseListLen = parseList.size(); + + for (int i = 0; i < parseListLen; i++) { + String declaration = parseList.get(i); + + if (declaration.trim().length() == 0) { + break; // no parameters actually declared, but whitespace spans lines + } + + // Bug#52167, tokenizer will break if declaration contains special characters like \n + declaration = declaration.replaceAll("[\\t\\n\\x0B\\f\\r]", " "); + StringTokenizer declarationTok = new StringTokenizer(declaration, " \t"); + + String paramName = null; + boolean isOutParam = false; + boolean isInParam = false; + + if (declarationTok.hasMoreTokens()) { + String possibleParamName = declarationTok.nextToken(); + + if (possibleParamName.equalsIgnoreCase("OUT")) { + isOutParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException("Internal error when parsing callable statement metadata (missing parameter name)", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } else if (possibleParamName.equalsIgnoreCase("INOUT")) { + isOutParam = true; + isInParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException("Internal error when parsing callable statement metadata (missing parameter name)", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } else if (possibleParamName.equalsIgnoreCase("IN")) { + isOutParam = false; + isInParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException("Internal error when parsing callable statement metadata (missing parameter name)", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } else { + isOutParam = false; + isInParam = true; + + paramName = possibleParamName; + } + + TypeDescriptor typeDesc = null; + + if (declarationTok.hasMoreTokens()) { + StringBuilder typeInfoBuf = new StringBuilder(declarationTok.nextToken()); + + while (declarationTok.hasMoreTokens()) { + typeInfoBuf.append(" "); + typeInfoBuf.append(declarationTok.nextToken()); + } + + String typeInfo = typeInfoBuf.toString(); + + typeDesc = new TypeDescriptor(typeInfo, "YES"); + } else { + throw SQLError.createSQLException("Internal error when parsing callable statement metadata (missing parameter type)", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + if ((paramName.startsWith("`") && paramName.endsWith("`")) + || (isProcedureInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\""))) { + paramName = paramName.substring(1, paramName.length() - 1); + } + + int wildCompareRes = StringUtils.wildCompare(paramName, parameterNamePattern); + + if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) { + ResultSetRow row = convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, paramName, isOutParam, isInParam, false, + typeDesc, forGetFunctionColumns, ordinal++); + + resultRows.add(row); + } + } else { + throw SQLError.createSQLException("Internal error when parsing callable statement metadata (unknown output from 'SHOW CREATE PROCEDURE')", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + } else { + // Is this an error? JDBC spec doesn't make it clear if stored procedure doesn't exist, is it an error.... + } + } + + /** + * Finds the end of the parameter declaration from the output of "SHOW + * CREATE PROCEDURE". + * + * @param beginIndex + * should be the index of the procedure body that contains the + * first "(". + * @param procedureDef + * the procedure body + * @param quoteChar + * the identifier quote character in use + * @return the ending index of the parameter declaration, not including the + * closing ")" + * @throws SQLException + * if a parse error occurs. + */ + private int endPositionOfParameterDeclaration(int beginIndex, String procedureDef, String quoteChar) throws SQLException { + int currentPos = beginIndex + 1; + int parenDepth = 1; // counting the first openParen + + while (parenDepth > 0 && currentPos < procedureDef.length()) { + int closedParenIndex = StringUtils.indexOfIgnoreCase(currentPos, procedureDef, ")", quoteChar, quoteChar, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (closedParenIndex != -1) { + int nextOpenParenIndex = StringUtils.indexOfIgnoreCase(currentPos, procedureDef, "(", quoteChar, quoteChar, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (nextOpenParenIndex != -1 && nextOpenParenIndex < closedParenIndex) { + parenDepth++; + currentPos = closedParenIndex + 1; // set after closed paren that increases depth + } else { + parenDepth--; + currentPos = closedParenIndex; // start search from same position + } + } else { + // we should always get closed paren of some sort + throw SQLError.createSQLException("Internal error when parsing callable statement metadata", SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + + return currentPos; + } + + /** + * Finds the end of the RETURNS clause for SQL Functions by using any of the + * keywords allowed after the RETURNS clause, or a label. + * + * @param procedureDefn + * the function body containing the definition of the function + * @param quoteChar + * the identifier quote string in use + * @param positionOfReturnKeyword + * the position of "RETURNS" in the definition + * @return the end of the returns clause + * @throws SQLException + * if a parse error occurs + */ + private int findEndOfReturnsClause(String procedureDefn, int positionOfReturnKeyword) throws SQLException { + /* + * characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | + * NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { + * DEFINER | INVOKER } | COMMENT 'string' + */ + String openingMarkers = this.quotedId + "("; + String closingMarkers = this.quotedId + ")"; + + String[] tokens = new String[] { "LANGUAGE", "NOT", "DETERMINISTIC", "CONTAINS", "NO", "READ", "MODIFIES", "SQL", "COMMENT", "BEGIN", "RETURN" }; + + int startLookingAt = positionOfReturnKeyword + "RETURNS".length() + 1; + + int endOfReturn = -1; + + for (int i = 0; i < tokens.length; i++) { + int nextEndOfReturn = StringUtils.indexOfIgnoreCase(startLookingAt, procedureDefn, tokens[i], openingMarkers, closingMarkers, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (nextEndOfReturn != -1) { + if (endOfReturn == -1 || (nextEndOfReturn < endOfReturn)) { + endOfReturn = nextEndOfReturn; + } + } + } + + if (endOfReturn != -1) { + return endOfReturn; + } + + // Label? + endOfReturn = StringUtils.indexOfIgnoreCase(startLookingAt, procedureDefn, ":", openingMarkers, closingMarkers, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (endOfReturn != -1) { + // seek back until whitespace + for (int i = endOfReturn; i > 0; i--) { + if (Character.isWhitespace(procedureDefn.charAt(i))) { + return i; + } + } + } + + // We can't parse it. + + throw SQLError.createSQLException("Internal error when parsing callable statement metadata", SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + /** + * Parses the cascade option string and returns the DBMD constant that + * represents it (for deletes) + * + * @param cascadeOptions + * the comment from 'SHOW TABLE STATUS' + * @return the DBMD constant that represents the cascade option + */ + private int getCascadeDeleteOption(String cascadeOptions) { + int onDeletePos = cascadeOptions.indexOf("ON DELETE"); + + if (onDeletePos != -1) { + String deleteOptions = cascadeOptions.substring(onDeletePos, cascadeOptions.length()); + + if (deleteOptions.startsWith("ON DELETE CASCADE")) { + return java.sql.DatabaseMetaData.importedKeyCascade; + } else if (deleteOptions.startsWith("ON DELETE SET NULL")) { + return java.sql.DatabaseMetaData.importedKeySetNull; + } else if (deleteOptions.startsWith("ON DELETE RESTRICT")) { + return java.sql.DatabaseMetaData.importedKeyRestrict; + } else if (deleteOptions.startsWith("ON DELETE NO ACTION")) { + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + } + + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + + /** + * Parses the cascade option string and returns the DBMD constant that + * represents it (for Updates) + * + * @param cascadeOptions + * the comment from 'SHOW TABLE STATUS' + * @return the DBMD constant that represents the cascade option + */ + private int getCascadeUpdateOption(String cascadeOptions) { + int onUpdatePos = cascadeOptions.indexOf("ON UPDATE"); + + if (onUpdatePos != -1) { + String updateOptions = cascadeOptions.substring(onUpdatePos, cascadeOptions.length()); + + if (updateOptions.startsWith("ON UPDATE CASCADE")) { + return java.sql.DatabaseMetaData.importedKeyCascade; + } else if (updateOptions.startsWith("ON UPDATE SET NULL")) { + return java.sql.DatabaseMetaData.importedKeySetNull; + } else if (updateOptions.startsWith("ON UPDATE RESTRICT")) { + return java.sql.DatabaseMetaData.importedKeyRestrict; + } else if (updateOptions.startsWith("ON UPDATE NO ACTION")) { + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + } + + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + + protected IteratorWithCleanup getCatalogIterator(String catalogSpec) throws SQLException { + IteratorWithCleanup allCatalogsIter; + if (catalogSpec != null) { + if (!catalogSpec.equals("")) { + if (this.conn.getPedantic()) { + allCatalogsIter = new SingleStringIterator(catalogSpec); + } else { + allCatalogsIter = new SingleStringIterator(StringUtils.unQuoteIdentifier(catalogSpec, this.quotedId)); + } + } else { + // legacy mode of operation + allCatalogsIter = new SingleStringIterator(this.database); + } + } else if (this.conn.getNullCatalogMeansCurrent()) { + + allCatalogsIter = new SingleStringIterator(this.database); + } else { + allCatalogsIter = new ResultSetIterator(getCatalogs(), 1); + } + + return allCatalogsIter; + } + + /** + * Get the catalog names available in this database. The results are ordered + * by catalog name. + *

    + * The catalog column is: + *

      + *
    1. TABLE_CAT String => catalog name
    2. + *
    + *

    + * + * @return ResultSet each row has a single String column that is a catalog + * name + * @throws SQLException + */ + public java.sql.ResultSet getCatalogs() throws SQLException { + java.sql.ResultSet results = null; + java.sql.Statement stmt = null; + + try { + stmt = this.conn.getMetadataSafeStatement(); + results = stmt.executeQuery("SHOW DATABASES"); + + int catalogsCount = 0; + if (results.last()) { + catalogsCount = results.getRow(); + results.beforeFirst(); + } + + List resultsAsList = new ArrayList(catalogsCount); + while (results.next()) { + resultsAsList.add(results.getString(1)); + } + Collections.sort(resultsAsList); + + Field[] fields = new Field[1]; + fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR, results.getMetaData().getColumnDisplaySize(1)); + + ArrayList tuples = new ArrayList(catalogsCount); + for (String cat : resultsAsList) { + byte[][] rowVal = new byte[1][]; + rowVal[0] = s2b(cat); + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + + return buildResultSet(fields, tuples); + } finally { + if (results != null) { + try { + results.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + results = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + stmt = null; + } + } + } + + /** + * What's the separator between catalog and table name? + * + * @return the separator string + * @throws SQLException + */ + public String getCatalogSeparator() throws SQLException { + return "."; + } + + // The following group of methods exposes various limitations based on the target database with the current driver. Unless otherwise specified, a result of + // zero means there is no limit, or the limit is not known. + + /** + * What's the database vendor's preferred term for "catalog"? + * + * @return the vendor term + * @throws SQLException + */ + public String getCatalogTerm() throws SQLException { + return "database"; + } + + /** + * Get a description of the access rights for a table's columns. + *

    + * Only privileges matching the column name criteria are returned. They are ordered by COLUMN_NAME and PRIVILEGE. + *

    + *

    + * Each privilige description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. GRANTOR => grantor of access (may be null)
    10. + *
    11. GRANTEE String => grantee of access
    12. + *
    13. PRIVILEGE String => name of access (SELECT, INSERT, UPDATE, REFRENCES, ...)
    14. + *
    15. IS_GRANTABLE String => "YES" if grantee is permitted to grant to others; "NO" if not; null if unknown
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name; "" retrieves those without a schema + * @param table + * a table name + * @param columnNamePattern + * a column name pattern + * @return ResultSet each row is a column privilege description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + Field[] fields = new Field[8]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64); + fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64); + fields[4] = new Field("", "GRANTOR", Types.CHAR, 77); + fields[5] = new Field("", "GRANTEE", Types.CHAR, 77); + fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64); + fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3); + + String grantQuery = "SELECT c.host, c.db, t.grantor, c.user, c.table_name, c.column_name, c.column_priv " + + "FROM mysql.columns_priv c, mysql.tables_priv t WHERE c.host = t.host AND c.db = t.db AND " + + "c.table_name = t.table_name AND c.db LIKE ? AND c.table_name = ? AND c.column_name LIKE ?"; + + PreparedStatement pStmt = null; + ResultSet results = null; + ArrayList grantRows = new ArrayList(); + + try { + pStmt = prepareMetaDataSafeStatement(grantQuery); + + pStmt.setString(1, (catalog != null) && (catalog.length() != 0) ? catalog : "%"); + pStmt.setString(2, table); + pStmt.setString(3, columnNamePattern); + + results = pStmt.executeQuery(); + + while (results.next()) { + String host = results.getString(1); + String db = results.getString(2); + String grantor = results.getString(3); + String user = results.getString(4); + + if ((user == null) || (user.length() == 0)) { + user = "%"; + } + + StringBuilder fullUser = new StringBuilder(user); + + if ((host != null) && this.conn.getUseHostsInPrivileges()) { + fullUser.append("@"); + fullUser.append(host); + } + + String columnName = results.getString(6); + String allPrivileges = results.getString(7); + + if (allPrivileges != null) { + allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH); + + StringTokenizer st = new StringTokenizer(allPrivileges, ","); + + while (st.hasMoreTokens()) { + String privilege = st.nextToken().trim(); + byte[][] tuple = new byte[8][]; + tuple[0] = s2b(db); + tuple[1] = null; + tuple[2] = s2b(table); + tuple[3] = s2b(columnName); + + if (grantor != null) { + tuple[4] = s2b(grantor); + } else { + tuple[4] = null; + } + + tuple[5] = s2b(fullUser.toString()); + tuple[6] = s2b(privilege); + tuple[7] = null; + grantRows.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (Exception ex) { + } + + pStmt = null; + } + } + + return buildResultSet(fields, grantRows); + } + + /** + * Get a description of table columns available in a catalog. + *

    + * Only column descriptions matching the catalog, schema, table and column name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME and + * ORDINAL_POSITION. + *

    + *

    + * Each column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. DATA_TYPE short => SQL type from java.sql.Types
    10. + *
    11. TYPE_NAME String => Data source dependent type name
    12. + *
    13. COLUMN_SIZE int => column size. For char or date types this is the maximum number of characters, for numeric or decimal types this is + * precision.
    14. + *
    15. BUFFER_LENGTH is not used.
    16. + *
    17. DECIMAL_DIGITS int => the number of fractional digits
    18. + *
    19. NUM_PREC_RADIX int => Radix (typically either 10 or 2)
    20. + *
    21. NULLABLE int => is NULL allowed? + *
        + *
      • columnNoNulls - might not allow NULL values
      • + *
      • columnNullable - definitely allows NULL values
      • + *
      • columnNullableUnknown - nullability unknown
      • + *
      + *
    22. + *
    23. REMARKS String => comment describing column (may be null)
    24. + *
    25. COLUMN_DEF String => default value (may be null)
    26. + *
    27. SQL_DATA_TYPE int => unused
    28. + *
    29. SQL_DATETIME_SUB int => unused
    30. + *
    31. CHAR_OCTET_LENGTH int => for char types the maximum number of bytes in the column
    32. + *
    33. ORDINAL_POSITION int => index of column in table (starting at 1)
    34. + *
    35. IS_NULLABLE String => "NO" means column definitely does not allow NULL values; "YES" means the column might allow NULL values. An empty string + * means nobody knows.
    36. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param tableNamePattern + * a table name pattern + * @param columnNamePattern + * a column name pattern + * @return ResultSet each row is a column description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(final String catalog, final String schemaPattern, final String tableNamePattern, String columnNamePattern) + throws SQLException { + + if (columnNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + columnNamePattern = "%"; + } else { + throw SQLError.createSQLException("Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + final String colPattern = columnNamePattern; + + Field[] fields = createColumnsFields(); + + final ArrayList rows = new ArrayList(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ArrayList tableNameList = new ArrayList(); + + if (tableNamePattern == null) { + // Select from all tables + java.sql.ResultSet tables = null; + + try { + tables = getTables(catalogStr, schemaPattern, "%", new String[0]); + + while (tables.next()) { + String tableNameFromList = tables.getString("TABLE_NAME"); + tableNameList.add(tableNameFromList); + } + } finally { + if (tables != null) { + try { + tables.close(); + } catch (Exception sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + tables = null; + } + } + } else { + java.sql.ResultSet tables = null; + + try { + tables = getTables(catalogStr, schemaPattern, tableNamePattern, new String[0]); + + while (tables.next()) { + String tableNameFromList = tables.getString("TABLE_NAME"); + tableNameList.add(tableNameFromList); + } + } finally { + if (tables != null) { + try { + tables.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + tables = null; + } + } + } + + for (String tableName : tableNameList) { + + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW "); + + if (DatabaseMetaData.this.conn.versionMeetsMinimum(4, 1, 0)) { + queryBuf.append("FULL "); + } + + queryBuf.append("COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(tableName, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" LIKE "); + queryBuf.append(StringUtils.quoteIdentifier(colPattern, "'", true)); + + // Return correct ordinals if column name pattern is not '%' + // Currently, MySQL doesn't show enough data to do this, so we do it the 'hard' way...Once _SYSTEM tables are in, this should be + // much easier + boolean fixUpOrdinalsRequired = false; + Map ordinalFixUpMap = null; + + if (!colPattern.equals("%")) { + fixUpOrdinalsRequired = true; + + StringBuilder fullColumnQueryBuf = new StringBuilder("SHOW "); + + if (DatabaseMetaData.this.conn.versionMeetsMinimum(4, 1, 0)) { + fullColumnQueryBuf.append("FULL "); + } + + fullColumnQueryBuf.append("COLUMNS FROM "); + fullColumnQueryBuf.append( + StringUtils.quoteIdentifier(tableName, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + fullColumnQueryBuf.append(" FROM "); + fullColumnQueryBuf.append( + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + results = stmt.executeQuery(fullColumnQueryBuf.toString()); + + ordinalFixUpMap = new HashMap(); + + int fullOrdinalPos = 1; + + while (results.next()) { + String fullOrdColName = results.getString("Field"); + + ordinalFixUpMap.put(fullOrdColName, Integer.valueOf(fullOrdinalPos++)); + } + } + + results = stmt.executeQuery(queryBuf.toString()); + + int ordPos = 1; + + while (results.next()) { + byte[][] rowVal = new byte[24][]; + rowVal[0] = s2b(catalogStr); // TABLE_CAT + rowVal[1] = null; // TABLE_SCHEM (No schemas + // in MySQL) + + rowVal[2] = s2b(tableName); // TABLE_NAME + rowVal[3] = results.getBytes("Field"); + + TypeDescriptor typeDesc = new TypeDescriptor(results.getString("Type"), results.getString("Null")); + + rowVal[4] = Short.toString(typeDesc.dataType).getBytes(); + + // DATA_TYPE (jdbc) + rowVal[5] = s2b(typeDesc.typeName); // TYPE_NAME + // (native) + if (typeDesc.columnSize == null) { + rowVal[6] = null; + } else { + String collation = results.getString("Collation"); + int mbminlen = 1; + if (collation != null && ("TEXT".equals(typeDesc.typeName) || "TINYTEXT".equals(typeDesc.typeName) + || "MEDIUMTEXT".equals(typeDesc.typeName))) { + if (collation.indexOf("ucs2") > -1 || collation.indexOf("utf16") > -1) { + mbminlen = 2; + } else if (collation.indexOf("utf32") > -1) { + mbminlen = 4; + } + } + rowVal[6] = mbminlen == 1 ? s2b(typeDesc.columnSize.toString()) + : s2b(((Integer) (typeDesc.columnSize / mbminlen)).toString()); + } + rowVal[7] = s2b(Integer.toString(typeDesc.bufferLength)); + rowVal[8] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); + rowVal[9] = s2b(Integer.toString(typeDesc.numPrecRadix)); + rowVal[10] = s2b(Integer.toString(typeDesc.nullability)); + + // + // Doesn't always have this field, depending on version + // + // + // REMARK column + // + try { + if (DatabaseMetaData.this.conn.versionMeetsMinimum(4, 1, 0)) { + rowVal[11] = results.getBytes("Comment"); + } else { + rowVal[11] = results.getBytes("Extra"); + } + } catch (Exception E) { + rowVal[11] = new byte[0]; + } + + // COLUMN_DEF + rowVal[12] = results.getBytes("Default"); + + rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE + rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB + + if (StringUtils.indexOfIgnoreCase(typeDesc.typeName, "CHAR") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.typeName, "BLOB") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.typeName, "TEXT") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.typeName, "BINARY") != -1) { + rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH + } else { + rowVal[15] = null; + } + + // ORDINAL_POSITION + if (!fixUpOrdinalsRequired) { + rowVal[16] = Integer.toString(ordPos++).getBytes(); + } else { + String origColName = results.getString("Field"); + Integer realOrdinal = ordinalFixUpMap.get(origColName); + + if (realOrdinal != null) { + rowVal[16] = realOrdinal.toString().getBytes(); + } else { + throw SQLError.createSQLException("Can not find column in full column list to determine true ordinal position.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + rowVal[17] = s2b(typeDesc.isNullable); + + // We don't support REF or DISTINCT types + rowVal[18] = null; + rowVal[19] = null; + rowVal[20] = null; + rowVal[21] = null; + + rowVal[22] = s2b(""); + + String extra = results.getString("Extra"); + + if (extra != null) { + rowVal[22] = s2b(StringUtils.indexOfIgnoreCase(extra, "auto_increment") != -1 ? "YES" : "NO"); + rowVal[23] = s2b(StringUtils.indexOfIgnoreCase(extra, "generated") != -1 ? "YES" : "NO"); + } + + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = buildResultSet(fields, rows); + + return results; + } + + protected Field[] createColumnsFields() { + Field[] fields = new Field[24]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255); + fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32); + fields[4] = new Field("", "DATA_TYPE", Types.INTEGER, 5); + fields[5] = new Field("", "TYPE_NAME", Types.CHAR, 16); + fields[6] = new Field("", "COLUMN_SIZE", Types.INTEGER, Integer.toString(Integer.MAX_VALUE).length()); + fields[7] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10); + fields[8] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10); + fields[9] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10); + fields[10] = new Field("", "NULLABLE", Types.INTEGER, 10); + fields[11] = new Field("", "REMARKS", Types.CHAR, 0); + fields[12] = new Field("", "COLUMN_DEF", Types.CHAR, 0); + fields[13] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10); + fields[14] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10); + fields[15] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer.toString(Integer.MAX_VALUE).length()); + fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 10); + fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3); + fields[18] = new Field("", "SCOPE_CATALOG", Types.CHAR, 255); + fields[19] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 255); + fields[20] = new Field("", "SCOPE_TABLE", Types.CHAR, 255); + fields[21] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 10); + fields[22] = new Field("", "IS_AUTOINCREMENT", Types.CHAR, 3); // JDBC 4 + fields[23] = new Field("", "IS_GENERATEDCOLUMN", Types.CHAR, 3); // JDBC 4.1 + return fields; + } + + /** + * JDBC 2.0 Return the connection that produced this metadata object. + * + * @return the connection that produced this metadata object. + * @throws SQLException + * if a database error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + return this.conn; + } + + /** + * Get a description of the foreign key columns in the foreign key table + * that reference the primary key columns of the primary key table (describe + * how one table imports another's key.) This should normally return a + * single foreign key/primary key pair (most tables only import a foreign + * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + *

    + * Each foreign key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name
    6. + *
    7. PKCOLUMN_NAME String => primary key column name
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null) being exported (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null) being exported (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name being exported
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name being exported
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key identifier (may be null)
    24. + *
    25. PK_NAME String => primary key identifier (may be null)
    26. + *
    + *

    + * + * @param primaryCatalog + * a catalog name; "" retrieves those without a catalog + * @param primarySchema + * a schema name pattern; "" retrieves those without a schema + * @param primaryTable + * a table name + * @param foreignCatalog + * a catalog name; "" retrieves those without a catalog + * @param foreignSchema + * a schema name pattern; "" retrieves those without a schema + * @param foreignTable + * a table name + * @return ResultSet each row is a foreign key column description + * @throws SQLException + * if a database access error occurs + */ + public java.sql.ResultSet getCrossReference(final String primaryCatalog, final String primarySchema, final String primaryTable, final String foreignCatalog, + final String foreignSchema, final String foreignTable) throws SQLException { + if (primaryTable == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList tuples = new ArrayList(); + + if (this.conn.versionMeetsMinimum(3, 23, 0)) { + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(foreignCatalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + if (DatabaseMetaData.this.conn.versionMeetsMinimum(3, 23, 50)) { + fkresults = extractForeignKeyFromCreateTable(catalogStr, null); + } else { + StringBuilder queryBuf = new StringBuilder("SHOW TABLE STATUS FROM "); + queryBuf.append( + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + fkresults = stmt.executeQuery(queryBuf.toString()); + } + + String foreignTableWithCase = getTableNameWithCase(foreignTable); + String primaryTableWithCase = getTableNameWithCase(primaryTable); + + /* + * Parse imported foreign key information + */ + + String dummy; + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + dummy = commentTokens.nextToken(); + + // Skip InnoDB comment + } + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keys); + + int keySeq = 0; + + Iterator referencingColumns = parsedInfo.localColumnsList.iterator(); + Iterator referencedColumns = parsedInfo.referencedColumnsList.iterator(); + + while (referencingColumns.hasNext()) { + String referencingColumn = StringUtils.unQuoteIdentifier(referencingColumns.next(), + DatabaseMetaData.this.quotedId); + + // one tuple for each table between parenthesis + byte[][] tuple = new byte[14][]; + tuple[4] = ((foreignCatalog == null) ? null : s2b(foreignCatalog)); + tuple[5] = ((foreignSchema == null) ? null : s2b(foreignSchema)); + dummy = fkresults.getString("Name"); // FKTABLE_NAME + + if (dummy.compareTo(foreignTableWithCase) != 0) { + continue; + } + + tuple[6] = s2b(dummy); + + tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME + tuple[0] = ((primaryCatalog == null) ? null : s2b(primaryCatalog)); + tuple[1] = ((primarySchema == null) ? null : s2b(primarySchema)); + + // Skip foreign key if it doesn't refer to the right table + if (parsedInfo.referencedTable.compareTo(primaryTableWithCase) != 0) { + continue; + } + + tuple[2] = s2b(parsedInfo.referencedTable); // PKTABLE_NAME + tuple[3] = s2b(StringUtils.unQuoteIdentifier(referencedColumns.next(), DatabaseMetaData.this.quotedId)); // PKCOLUMN_NAME + tuple[8] = Integer.toString(keySeq).getBytes(); // KEY_SEQ + + int[] actions = getForeignKeyActions(keys); + + tuple[9] = Integer.toString(actions[1]).getBytes(); + tuple[10] = Integer.toString(actions[0]).getBytes(); + tuple[11] = null; // FK_NAME + tuple[12] = null; // PK_NAME + tuple[13] = Integer.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable).getBytes(); + tuples.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + keySeq++; + } + } + } + } + } + + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (Exception sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + java.sql.ResultSet results = buildResultSet(fields, tuples); + + return results; + } + + protected Field[] createFkMetadataFields() { + Field[] fields = new Field[14]; + fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255); + fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0); + fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255); + fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32); + fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255); + fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0); + fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255); + fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32); + fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2); + fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2); + fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2); + fields[11] = new Field("", "FK_NAME", Types.CHAR, 0); + fields[12] = new Field("", "PK_NAME", Types.CHAR, 0); + fields[13] = new Field("", "DEFERRABILITY", Types.SMALLINT, 2); + return fields; + } + + /** + * @see DatabaseMetaData#getDatabaseMajorVersion() + */ + public int getDatabaseMajorVersion() throws SQLException { + return this.conn.getServerMajorVersion(); + } + + /** + * @see DatabaseMetaData#getDatabaseMinorVersion() + */ + public int getDatabaseMinorVersion() throws SQLException { + return this.conn.getServerMinorVersion(); + } + + /** + * What's the name of this database product? + * + * @return database product name + * @throws SQLException + */ + public String getDatabaseProductName() throws SQLException { + return "MySQL"; + } + + /** + * What's the version of this database product? + * + * @return database version + * @throws SQLException + */ + public String getDatabaseProductVersion() throws SQLException { + return this.conn.getServerVersion(); + } + + /** + * What's the database's default transaction isolation level? The values are + * defined in java.sql.Connection. + * + * @return the default isolation level + * @throws SQLException + * if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException { + if (this.conn.supportsIsolationLevel()) { + return java.sql.Connection.TRANSACTION_READ_COMMITTED; + } + + return java.sql.Connection.TRANSACTION_NONE; + } + + /** + * What's this JDBC driver's major version number? + * + * @return JDBC driver major version + */ + public int getDriverMajorVersion() { + return NonRegisteringDriver.getMajorVersionInternal(); + } + + /** + * What's this JDBC driver's minor version number? + * + * @return JDBC driver minor version number + */ + public int getDriverMinorVersion() { + return NonRegisteringDriver.getMinorVersionInternal(); + } + + /** + * What's the name of this JDBC driver? + * + * @return JDBC driver name + * @throws SQLException + */ + public String getDriverName() throws SQLException { + return NonRegisteringDriver.NAME; + } + + /** + * What's the version of this JDBC driver? + * + * @return JDBC driver version + * @throws java.sql.SQLException + */ + public String getDriverVersion() throws java.sql.SQLException { + return "@MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ )"; + } + + /** + * Get a description of a foreign key columns that reference a table's + * primary key columns (the foreign keys exported by a table). They are + * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. + *

    + * Each foreign key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name
    6. + *
    7. PKCOLUMN_NAME String => primary key column name
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null) being exported (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null) being exported (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name being exported
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name being exported
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key identifier (may be null)
    24. + *
    25. PK_NAME String => primary key identifier (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a foreign key column description + * @throws SQLException + * if a database access error occurs + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, final String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList rows = new ArrayList(); + + if (this.conn.versionMeetsMinimum(3, 23, 0)) { + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + if (DatabaseMetaData.this.conn.versionMeetsMinimum(3, 23, 50)) { + // we can use 'SHOW CREATE TABLE' + + fkresults = extractForeignKeyFromCreateTable(catalogStr, null); + } else { + StringBuilder queryBuf = new StringBuilder("SHOW TABLE STATUS FROM "); + queryBuf.append( + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + fkresults = stmt.executeQuery(queryBuf.toString()); + } + + // lower-case table name might be turned on + String tableNameWithCase = getTableNameWithCase(table); + + /* + * Parse imported foreign key information + */ + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + commentTokens.nextToken(); // Skip + // InnoDB + // comment + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + getExportKeyResults(catalogStr, tableNameWithCase, keys, rows, fkresults.getString("Name")); + } + } + } + } + } + + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + java.sql.ResultSet results = buildResultSet(fields, rows); + + return results; + } + + /** + * Adds to the tuples list the exported keys of exportingTable based on the + * keysComment from the 'show table status' sql command. KeysComment is that + * part of the comment field that follows the "InnoDB free ...;" prefix. + * + * @param catalog + * the database to use + * @param exportingTable + * the table keys are being exported from + * @param keysComment + * the comment from 'show table status' + * @param tuples + * the rows to add results to + * @param fkTableName + * the foreign key table name + * @throws SQLException + * if a database access error occurs + */ + protected void getExportKeyResults(String catalog, String exportingTable, String keysComment, List tuples, String fkTableName) + throws SQLException { + getResultsImpl(catalog, exportingTable, keysComment, tuples, fkTableName, true); + } + + /** + * Get all the "extra" characters that can be used in unquoted identifier + * names (those beyond a-z, 0-9 and _). + * + * @return the string containing the extra characters + * @throws SQLException + */ + public String getExtraNameCharacters() throws SQLException { + return "#@"; + } + + /** + * Returns the DELETE and UPDATE foreign key actions from the given 'SHOW + * TABLE STATUS' string, with the DELETE action being the first item in the + * array, and the UPDATE action being the second. + * + * @param commentString + * the comment from 'SHOW TABLE STATUS' + * @return int[] [0] = delete action, [1] = update action + */ + protected int[] getForeignKeyActions(String commentString) { + int[] actions = new int[] { java.sql.DatabaseMetaData.importedKeyNoAction, java.sql.DatabaseMetaData.importedKeyNoAction }; + + int lastParenIndex = commentString.lastIndexOf(")"); + + if (lastParenIndex != (commentString.length() - 1)) { + String cascadeOptions = commentString.substring(lastParenIndex + 1).trim().toUpperCase(Locale.ENGLISH); + + actions[0] = getCascadeDeleteOption(cascadeOptions); + actions[1] = getCascadeUpdateOption(cascadeOptions); + } + + return actions; + } + + /** + * What's the string used to quote SQL identifiers? This returns a space " " + * if identifier quoting isn't supported. A JDBC compliant driver always + * uses a double quote character. + * + * @return the quoting string + * @throws SQLException + */ + public String getIdentifierQuoteString() throws SQLException { + if (this.conn.supportsQuotedIdentifiers()) { + return this.conn.useAnsiQuotedIdentifiers() ? "\"" : "`"; + } + + // only versions below 3.23.6 don't support quoted identifiers. + return " "; + } + + /** + * Get a description of the primary key columns that are referenced by a + * table's foreign key columns (the primary keys imported by a table). They + * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + *

    + * Each primary key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog being imported (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema being imported (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name being imported
    6. + *
    7. PKCOLUMN_NAME String => primary key column name being imported
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key name (may be null)
    24. + *
    25. PK_NAME String => primary key name (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a primary key column description + * @throws SQLException + * if a database access error occurs + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, final String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList rows = new ArrayList(); + + if (this.conn.versionMeetsMinimum(3, 23, 0)) { + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + if (DatabaseMetaData.this.conn.versionMeetsMinimum(3, 23, 50)) { + // we can use 'SHOW CREATE TABLE' + + fkresults = extractForeignKeyFromCreateTable(catalogStr, table); + } else { + StringBuilder queryBuf = new StringBuilder("SHOW TABLE STATUS "); + queryBuf.append(" FROM "); + queryBuf.append( + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" LIKE "); + queryBuf.append(StringUtils.quoteIdentifier(table, "'", true)); + + fkresults = stmt.executeQuery(queryBuf.toString()); + } + + /* + * Parse imported foreign key information + */ + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + commentTokens.nextToken(); // Skip InnoDB comment + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + getImportKeyResults(catalogStr, table, keys, rows); + } + } + } + } + } + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + java.sql.ResultSet results = buildResultSet(fields, rows); + + return results; + } + + /** + * Populates the tuples list with the imported keys of importingTable based + * on the keysComment from the 'show table status' sql command. KeysComment + * is that part of the comment field that follows the "InnoDB free ...;" + * prefix. + * + * @param catalog + * the database to use + * @param importingTable + * the table keys are being imported to + * @param keysComment + * the comment from 'show table status' + * @param tuples + * the rows to add results to + * @throws SQLException + * if a database access error occurs + */ + protected void getImportKeyResults(String catalog, String importingTable, String keysComment, List tuples) throws SQLException { + getResultsImpl(catalog, importingTable, keysComment, tuples, null, false); + } + + /** + * Get a description of a table's indices and statistics. They are ordered + * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + *

    + * Each index column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. NON_UNIQUE boolean => Can index values be non-unique? false when TYPE is tableIndexStatistic
    8. + *
    9. INDEX_QUALIFIER String => index catalog (may be null); null when TYPE is tableIndexStatistic
    10. + *
    11. INDEX_NAME String => index name; null when TYPE is tableIndexStatistic
    12. + *
    13. TYPE short => index type: + *
        + *
      • tableIndexStatistic - this identifies table statistics that are returned in conjuction with a table's index descriptions
      • + *
      • tableIndexClustered - this is a clustered index
      • + *
      • tableIndexHashed - this is a hashed index
      • + *
      • tableIndexOther - this is some other style of index
      • + *
      + *
    14. + *
    15. ORDINAL_POSITION short => column sequence number within index; zero when TYPE is tableIndexStatistic
    16. + *
    17. COLUMN_NAME String => column name; null when TYPE is tableIndexStatistic
    18. + *
    19. ASC_OR_DESC String => column sort sequence, "A" => ascending, "D" => descending, may be null if sort sequence is not supported; null when TYPE + * is tableIndexStatistic
    20. + *
    21. CARDINALITY int/long => When TYPE is tableIndexStatisic then this is the number of rows in the table; otherwise it is the number of unique + * values in the index.
    22. + *
    23. PAGES int/long => When TYPE is tableIndexStatisic then this is the number of pages used for the table, otherwise it is the number of pages + * used for the current index.
    24. + *
    25. FILTER_CONDITION String => Filter condition, if any. (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @param unique + * when true, return only indices for unique values; when false, + * return indices regardless of whether unique or not + * @param approximate + * when true, result is allowed to reflect approximate or out of + * data values; when false, results are requested to be accurate + * @return ResultSet each row is an index column description + * @throws SQLException + */ + public java.sql.ResultSet getIndexInfo(String catalog, String schema, final String table, final boolean unique, boolean approximate) throws SQLException { + /* + * MySQL stores index information in the following fields: Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part + */ + + Field[] fields = createIndexInfoFields(); + + final SortedMap sortedRows = new TreeMap(); + final ArrayList rows = new ArrayList(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW INDEX FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + try { + results = stmt.executeQuery(queryBuf.toString()); + } catch (SQLException sqlEx) { + int errorCode = sqlEx.getErrorCode(); + + // If SQLState is 42S02, ignore this SQLException it means the table doesn't exist.... + if (!"42S02".equals(sqlEx.getSQLState())) { + // Sometimes not mapped correctly for pre-4.1 so use error code instead. + if (errorCode != MysqlErrorNumbers.ER_NO_SUCH_TABLE) { + throw sqlEx; + } + } + } + + while (results != null && results.next()) { + byte[][] row = new byte[14][]; + row[0] = ((catalogStr == null) ? new byte[0] : s2b(catalogStr)); + row[1] = null; + row[2] = results.getBytes("Table"); + + boolean indexIsUnique = results.getInt("Non_unique") == 0; + + row[3] = (!indexIsUnique ? s2b("true") : s2b("false")); + row[4] = new byte[0]; + row[5] = results.getBytes("Key_name"); + short indexType = java.sql.DatabaseMetaData.tableIndexOther; + row[6] = Integer.toString(indexType).getBytes(); + row[7] = results.getBytes("Seq_in_index"); + row[8] = results.getBytes("Column_name"); + row[9] = results.getBytes("Collation"); + + long cardinality = results.getLong("Cardinality"); + + // Prior to JDBC 4.2, cardinality can be much larger than Integer's range, so we clamp it to conform to the API + if (!Util.isJdbc42() && cardinality > Integer.MAX_VALUE) { + cardinality = Integer.MAX_VALUE; + } + + row[10] = s2b(String.valueOf(cardinality)); + row[11] = s2b("0"); + row[12] = null; + + IndexMetaDataKey indexInfoKey = new IndexMetaDataKey(!indexIsUnique, indexType, results.getString("Key_name").toLowerCase(), + results.getShort("Seq_in_index")); + + if (unique) { + if (indexIsUnique) { + sortedRows.put(indexInfoKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + } else { + // All rows match + sortedRows.put(indexInfoKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + + Iterator sortedRowsIterator = sortedRows.values().iterator(); + while (sortedRowsIterator.hasNext()) { + rows.add(sortedRowsIterator.next()); + } + + java.sql.ResultSet indexInfo = buildResultSet(fields, rows); + + return indexInfo; + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + protected Field[] createIndexInfoFields() { + Field[] fields = new Field[13]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255); + fields[3] = new Field("", "NON_UNIQUE", Types.BOOLEAN, 4); + fields[4] = new Field("", "INDEX_QUALIFIER", Types.CHAR, 1); + fields[5] = new Field("", "INDEX_NAME", Types.CHAR, 32); + fields[6] = new Field("", "TYPE", Types.SMALLINT, 32); + fields[7] = new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5); + fields[8] = new Field("", "COLUMN_NAME", Types.CHAR, 32); + fields[9] = new Field("", "ASC_OR_DESC", Types.CHAR, 1); + if (Util.isJdbc42()) { + fields[10] = new Field("", "CARDINALITY", Types.BIGINT, 20); + fields[11] = new Field("", "PAGES", Types.BIGINT, 20); + } else { + fields[10] = new Field("", "CARDINALITY", Types.INTEGER, 20); + fields[11] = new Field("", "PAGES", Types.INTEGER, 10); + } + fields[12] = new Field("", "FILTER_CONDITION", Types.CHAR, 32); + return fields; + } + + /** + * @see DatabaseMetaData#getJDBCMajorVersion() + */ + public int getJDBCMajorVersion() throws SQLException { + return 4; + } + + /** + * @see DatabaseMetaData#getJDBCMinorVersion() + */ + public int getJDBCMinorVersion() throws SQLException { + return 0; + } + + /** + * How many hex characters can you have in an inline binary literal? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxBinaryLiteralLength() throws SQLException { + return 16777208; + } + + /** + * What's the maximum length of a catalog name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxCatalogNameLength() throws SQLException { + return 32; + } + + /** + * What's the max length for a character literal? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxCharLiteralLength() throws SQLException { + return 16777208; + } + + /** + * What's the limit on column name length? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxColumnNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns in a "GROUP BY" clause? + * + * @return max number of columns + * @throws SQLException + */ + public int getMaxColumnsInGroupBy() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns allowed in an index? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInIndex() throws SQLException { + return 16; + } + + /** + * What's the maximum number of columns in an "ORDER BY" clause? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInOrderBy() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns in a "SELECT" list? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInSelect() throws SQLException { + return 256; + } + + /** + * What's maximum number of columns in a table? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInTable() throws SQLException { + return 512; + } + + /** + * How many active connections can we have at a time to this database? + * + * @return max connections + * @throws SQLException + */ + public int getMaxConnections() throws SQLException { + return 0; + } + + /** + * What's the maximum cursor name length? + * + * @return max cursor name length in bytes + * @throws SQLException + */ + public int getMaxCursorNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum length of an index (in bytes)? + * + * @return max index length in bytes + * @throws SQLException + */ + public int getMaxIndexLength() throws SQLException { + return 256; + } + + /** + * What's the maximum length of a procedure name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxProcedureNameLength() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a single row? + * + * @return max row size in bytes + * @throws SQLException + */ + public int getMaxRowSize() throws SQLException { + return Integer.MAX_VALUE - 8; // Max buffer size - HEADER + } + + /** + * What's the maximum length allowed for a schema name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxSchemaNameLength() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a SQL statement? + * + * @return max length in bytes + * @throws SQLException + */ + public int getMaxStatementLength() throws SQLException { + return MysqlIO.getMaxBuf() - 4; // Max buffer - header + } + + /** + * How many active statements can we have open at one time to this database? + * + * @return the maximum + * @throws SQLException + */ + public int getMaxStatements() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a table name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxTableNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum number of tables in a SELECT? + * + * @return the maximum + * @throws SQLException + */ + public int getMaxTablesInSelect() throws SQLException { + return 256; + } + + /** + * What's the maximum length of a user name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxUserNameLength() throws SQLException { + return 16; + } + + /** + * Get a comma separated list of math functions. + * + * @return the list + * @throws SQLException + */ + public String getNumericFunctions() throws SQLException { + return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW," + + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE"; + } + + /** + * Get a description of a table's primary key columns. They are ordered by + * COLUMN_NAME. + *

    + * Each column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. KEY_SEQ short => sequence number within primary key
    10. + *
    11. PK_NAME String => primary key name (may be null)
    12. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a primary key column description + * @throws SQLException + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, final String table) throws SQLException { + Field[] fields = new Field[6]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255); + fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32); + fields[4] = new Field("", "KEY_SEQ", Types.SMALLINT, 5); + fields[5] = new Field("", "PK_NAME", Types.CHAR, 32); + + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + final ArrayList rows = new ArrayList(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet rs = null; + + try { + + StringBuilder queryBuf = new StringBuilder("SHOW KEYS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + + rs = stmt.executeQuery(queryBuf.toString()); + + TreeMap sortMap = new TreeMap(); + + while (rs.next()) { + String keyType = rs.getString("Key_name"); + + if (keyType != null) { + if (keyType.equalsIgnoreCase("PRIMARY") || keyType.equalsIgnoreCase("PRI")) { + byte[][] tuple = new byte[6][]; + tuple[0] = ((catalogStr == null) ? new byte[0] : s2b(catalogStr)); + tuple[1] = null; + tuple[2] = s2b(table); + + String columnName = rs.getString("Column_name"); + tuple[3] = s2b(columnName); + tuple[4] = s2b(rs.getString("Seq_in_index")); + tuple[5] = s2b(keyType); + sortMap.put(columnName, tuple); + } + } + } + + // Now pull out in column name sorted order + Iterator sortedIterator = sortMap.values().iterator(); + + while (sortedIterator.hasNext()) { + rows.add(new ByteArrayRow(sortedIterator.next(), getExceptionInterceptor())); + } + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + } + + rs = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = buildResultSet(fields, rows); + + return results; + } + + /** + * Get a description of a catalog's stored procedure parameters and result + * columns. + *

    + * Only descriptions matching the schema, procedure and parameter name criteria are returned. They are ordered by PROCEDURE_SCHEM and PROCEDURE_NAME. Within + * this, the return value, if any, is first. Next are the parameter descriptions in call order. The column descriptions follow in column number order. + *

    + *

    + * Each row in the ResultSet is a parameter description or column description with the following fields: + *

      + *
    1. PROCEDURE_CAT String => procedure catalog (may be null)
    2. + *
    3. PROCEDURE_SCHEM String => procedure schema (may be null)
    4. + *
    5. PROCEDURE_NAME String => procedure name
    6. + *
    7. COLUMN_NAME String => column/parameter name
    8. + *
    9. COLUMN_TYPE Short => kind of column/parameter: + *
        + *
      • procedureColumnUnknown - nobody knows
      • + *
      • procedureColumnIn - IN parameter
      • + *
      • procedureColumnInOut - INOUT parameter
      • + *
      • procedureColumnOut - OUT parameter
      • + *
      • procedureColumnReturn - procedure return value
      • + *
      • procedureColumnResult - result column in ResultSet
      • + *
      + *
    10. + *
    11. DATA_TYPE short => SQL type from java.sql.Types
    12. + *
    13. TYPE_NAME String => SQL type name
    14. + *
    15. PRECISION int => precision
    16. + *
    17. LENGTH int => length in bytes of data
    18. + *
    19. SCALE short => scale
    20. + *
    21. RADIX short => radix
    22. + *
    23. NULLABLE short => can it contain NULL? + *
        + *
      • procedureNoNulls - does not allow NULL values
      • + *
      • procedureNullable - allows NULL values
      • + *
      • procedureNullableUnknown - nullability unknown
      • + *
      + *
    24. + *
    25. REMARKS String => comment describing parameter/column
    26. + *
    27. COLUMN_DEF String => default value for the column (may be null)
    28. + *
    29. SQL_DATA_TYPE int => reserved for future use
    30. + *
    31. SQL_DATETIME_SUB int => reserved for future use
    32. + *
    33. CHAR_OCTET_LENGTH int => the maximum length of binary and character based columns. For any other datatype the returned value is a NULL
    34. + *
    35. ORDINAL_POSITION int => the ordinal position, starting from 1. A value of 0 is returned if this row describes the procedure's return value. + *
    36. + *
    37. IS_NULLABLE String => ISO rules are used to determine the nullability for a column. + *
        + *
      • YES --- if the parameter can include NULLs
      • + *
      • NO --- if the parameter cannot include NULLs
      • + *
      • empty string --- if the nullability for the parameter is unknown
      • + *
      + *
    38. + *
    39. SPECIFIC_NAME String => the name which uniquely identifies this procedure within its schema.
    40. + *
    + *

    + *

    + * Note: Some databases may not return the column descriptions for a procedure. + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param procedureNamePattern + * a procedure name pattern + * @param columnNamePattern + * a column name pattern + * @return ResultSet each row is a stored procedure parameter or column + * description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) + throws SQLException { + Field[] fields = createProcedureColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, procedureNamePattern, columnNamePattern, true, true); + } + + protected Field[] createProcedureColumnsFields() { + Field[] fields = new Field[20]; + + fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 512); + fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 512); + fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 512); + fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 512); + fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 64); + fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 6); + fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 64); + fields[7] = new Field("", "PRECISION", Types.INTEGER, 12); + fields[8] = new Field("", "LENGTH", Types.INTEGER, 12); + fields[9] = new Field("", "SCALE", Types.SMALLINT, 12); + fields[10] = new Field("", "RADIX", Types.SMALLINT, 6); + fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 6); + fields[12] = new Field("", "REMARKS", Types.CHAR, 512); + fields[13] = new Field("", "COLUMN_DEF", Types.CHAR, 512); + fields[14] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 12); + fields[15] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 12); + fields[16] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 12); + fields[17] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 12); + fields[18] = new Field("", "IS_NULLABLE", Types.CHAR, 512); + fields[19] = new Field("", "SPECIFIC_NAME", Types.CHAR, 512); + return fields; + } + + protected java.sql.ResultSet getProcedureOrFunctionColumns(Field[] fields, String catalog, String schemaPattern, String procedureOrFunctionNamePattern, + String columnNamePattern, boolean returnProcedures, boolean returnFunctions) throws SQLException { + + List> procsOrFuncsToExtractList = new ArrayList>(); + //Main container to be passed to getProceduresAndOrFunctions + ResultSet procsAndOrFuncsRs = null; + + if (supportsStoredProcedures()) { + try { + //getProceduresAndOrFunctions does NOT expect procedureOrFunctionNamePattern in form of DB_NAME.SP_NAME thus we need to remove it + String tmpProcedureOrFunctionNamePattern = null; + //Check if NOT a pattern first, then "sanitize" + if ((procedureOrFunctionNamePattern != null) && (!procedureOrFunctionNamePattern.equals("%"))) { + tmpProcedureOrFunctionNamePattern = StringUtils.sanitizeProcOrFuncName(procedureOrFunctionNamePattern); + } + + //Sanity check, if NamePattern is still NULL, we have a wildcard and not the name + if (tmpProcedureOrFunctionNamePattern == null) { + tmpProcedureOrFunctionNamePattern = procedureOrFunctionNamePattern; + } else { + //So we have a name to check meaning more actual processing + //Keep the Catalog parsed, maybe we'll need it at some point in the future... + String tmpCatalog = catalog; + List parseList = StringUtils.splitDBdotName(tmpProcedureOrFunctionNamePattern, tmpCatalog, this.quotedId, + this.conn.isNoBackslashEscapesSet()); + + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tmpCatalog = parseList.get(0); + tmpProcedureOrFunctionNamePattern = parseList.get(1); + } else { + //keep values as they are + } + } + + procsAndOrFuncsRs = getProceduresAndOrFunctions(createFieldMetadataForGetProcedures(), catalog, schemaPattern, + tmpProcedureOrFunctionNamePattern, returnProcedures, returnFunctions); + + boolean hasResults = false; + while (procsAndOrFuncsRs.next()) { + procsOrFuncsToExtractList.add( + new ComparableWrapper(getFullyQualifiedName(procsAndOrFuncsRs.getString(1), procsAndOrFuncsRs.getString(3)), + procsAndOrFuncsRs.getShort(8) == procedureNoResult ? PROCEDURE : FUNCTION)); + hasResults = true; + } + + // FIX for Bug#56305, allowing the code to proceed with empty fields causing NPE later + if (!hasResults) { + // throw SQLError.createSQLException( + // "User does not have access to metadata required to determine " + + // "stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" " + + // "to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.", + // SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } else { + Collections.sort(procsOrFuncsToExtractList); + } + + // Required to be sorted in name-order by JDBC spec, in 'normal' case getProcedures takes care of this for us, but if system tables are + // inaccessible, we need to sort... so just do this to be safe... + // Collections.sort(proceduresToExtractList); + } finally { + SQLException rethrowSqlEx = null; + + if (procsAndOrFuncsRs != null) { + try { + procsAndOrFuncsRs.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (rethrowSqlEx != null) { + throw rethrowSqlEx; + } + } + } + + ArrayList resultRows = new ArrayList(); + int idx = 0; + String procNameToCall = ""; + + for (ComparableWrapper procOrFunc : procsOrFuncsToExtractList) { + String procName = procOrFunc.getKey(); + ProcedureType procType = procOrFunc.getValue(); + + //Continuing from above (database_name.sp_name) + if (!" ".equals(this.quotedId)) { + idx = StringUtils.indexOfIgnoreCase(0, procName, ".", this.quotedId, this.quotedId, + this.conn.isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } else { + idx = procName.indexOf("."); + } + + if (idx > 0) { + catalog = StringUtils.unQuoteIdentifier(procName.substring(0, idx), this.quotedId); + procNameToCall = procName; // Leave as CAT.PROC, needed later + } else { + //No catalog. Not sure how to handle right now... + procNameToCall = procName; + } + getCallStmtParameterTypes(catalog, procNameToCall, procType, columnNamePattern, resultRows, fields.length == 17 /* for getFunctionColumns */); + } + + return buildResultSet(fields, resultRows); + } + + /** + * Get a description of stored procedures available in a catalog. + *

    + * Only procedure descriptions matching the schema and procedure name criteria are returned. They are ordered by PROCEDURE_SCHEM, and PROCEDURE_NAME. + *

    + *

    + * Each procedure description has the the following columns: + *

      + *
    1. PROCEDURE_CAT String => procedure catalog (may be null)
    2. + *
    3. PROCEDURE_SCHEM String => procedure schema (may be null)
    4. + *
    5. PROCEDURE_NAME String => procedure name
    6. + *
    7. reserved for future use
    8. + *
    9. reserved for future use
    10. + *
    11. reserved for future use
    12. + *
    13. REMARKS String => explanatory comment on the procedure
    14. + *
    15. PROCEDURE_TYPE short => kind of procedure: + *
        + *
      • procedureResultUnknown - May return a result
      • + *
      • procedureNoResult - Does not return a result
      • + *
      • procedureReturnsResult - Returns a result
      • + *
      + *
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param procedureNamePattern + * a procedure name pattern + * @return ResultSet each row is a procedure description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + Field[] fields = createFieldMetadataForGetProcedures(); + + return getProceduresAndOrFunctions(fields, catalog, schemaPattern, procedureNamePattern, true, true); + } + + protected Field[] createFieldMetadataForGetProcedures() { + Field[] fields = new Field[9]; + fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 255); + fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 255); + fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 255); + fields[3] = new Field("", "reserved1", Types.CHAR, 0); + fields[4] = new Field("", "reserved2", Types.CHAR, 0); + fields[5] = new Field("", "reserved3", Types.CHAR, 0); + fields[6] = new Field("", "REMARKS", Types.CHAR, 255); + fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 6); + fields[8] = new Field("", "SPECIFIC_NAME", Types.CHAR, 255); + + return fields; + } + + /** + * @param fields + * @param catalog + * @param schemaPattern + * @param procedureNamePattern + * @param returnProcedures + * @param returnFunctions + * @throws SQLException + */ + protected java.sql.ResultSet getProceduresAndOrFunctions(final Field[] fields, String catalog, String schemaPattern, String procedureNamePattern, + final boolean returnProcedures, final boolean returnFunctions) throws SQLException { + if ((procedureNamePattern == null) || (procedureNamePattern.length() == 0)) { + if (this.conn.getNullNamePatternMatchesAll()) { + procedureNamePattern = "%"; + } else { + throw SQLError.createSQLException("Procedure name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + final ArrayList procedureRows = new ArrayList(); + + if (supportsStoredProcedures()) { + final String procNamePattern = procedureNamePattern; + + final List> procedureRowsToSort = new ArrayList>(); + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + String db = catalogStr; + + ResultSet proceduresRs = null; + boolean needsClientFiltering = true; + + StringBuilder selectFromMySQLProcSQL = new StringBuilder(); + + selectFromMySQLProcSQL.append("SELECT name, type, comment FROM mysql.proc WHERE "); + if (returnProcedures && !returnFunctions) { + selectFromMySQLProcSQL.append("type = 'PROCEDURE' AND "); + } else if (!returnProcedures && returnFunctions) { + selectFromMySQLProcSQL.append("type = 'FUNCTION' AND "); + } + selectFromMySQLProcSQL.append("name LIKE ? AND db <=> ? ORDER BY name, type"); + + java.sql.PreparedStatement proceduresStmt = prepareMetaDataSafeStatement(selectFromMySQLProcSQL.toString()); + + try { + // + // Try using system tables first, as this is a little bit more efficient.... + // + if (db != null) { + if (DatabaseMetaData.this.conn.lowerCaseTableNames()) { + db = db.toLowerCase(); + } + proceduresStmt.setString(2, db); + } else { + proceduresStmt.setNull(2, Types.VARCHAR); + } + + int nameIndex = 1; + + proceduresStmt.setString(1, procNamePattern); + + try { + proceduresRs = proceduresStmt.executeQuery(); + needsClientFiltering = false; + + if (returnProcedures) { + convertToJdbcProcedureList(true, db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex); + } + + if (returnFunctions) { + convertToJdbcFunctionList(db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex, fields); + } + + } catch (SQLException sqlEx) { + nameIndex = DatabaseMetaData.this.conn.versionMeetsMinimum(5, 0, 1) ? 2 : 1; + + // System tables aren't accessible, so use 'SHOW [FUNCTION|PROCEDURE] STATUS instead. + // Functions first: + if (returnFunctions) { + proceduresStmt.close(); + + proceduresStmt = prepareMetaDataSafeStatement("SHOW FUNCTION STATUS LIKE ?"); + proceduresStmt.setString(1, procNamePattern); + proceduresRs = proceduresStmt.executeQuery(); + + convertToJdbcFunctionList(db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex, fields); + } + + // Procedures next: + if (returnProcedures) { + proceduresStmt.close(); + + proceduresStmt = prepareMetaDataSafeStatement("SHOW PROCEDURE STATUS LIKE ?"); + proceduresStmt.setString(1, procNamePattern); + proceduresRs = proceduresStmt.executeQuery(); + + convertToJdbcProcedureList(false, db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex); + } + } + + } finally { + SQLException rethrowSqlEx = null; + + if (proceduresRs != null) { + try { + proceduresRs.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (proceduresStmt != null) { + try { + proceduresStmt.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (rethrowSqlEx != null) { + throw rethrowSqlEx; + } + } + } + }.doForAll(); + + Collections.sort(procedureRowsToSort); + for (ComparableWrapper procRow : procedureRowsToSort) { + procedureRows.add(procRow.getValue()); + } + } + + return buildResultSet(fields, procedureRows); + } + + /** + * What's the database vendor's preferred term for "procedure"? + * + * @return the vendor term + * @throws SQLException + * if an error occurs (don't know why it would in this case...) + */ + public String getProcedureTerm() throws SQLException { + return "PROCEDURE"; + } + + /** + * @see DatabaseMetaData#getResultSetHoldability() + */ + public int getResultSetHoldability() throws SQLException { + return ResultSet.HOLD_CURSORS_OVER_COMMIT; + } + + private void getResultsImpl(String catalog, String table, String keysComment, List tuples, String fkTableName, boolean isExport) + throws SQLException { + + LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keysComment); + + if (isExport && !parsedInfo.referencedTable.equals(table)) { + return; + } + + if (parsedInfo.localColumnsList.size() != parsedInfo.referencedColumnsList.size()) { + throw SQLError.createSQLException("Error parsing foreign keys definition, number of local and referenced columns is not the same.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + Iterator localColumnNames = parsedInfo.localColumnsList.iterator(); + Iterator referColumnNames = parsedInfo.referencedColumnsList.iterator(); + + int keySeqIndex = 1; + + while (localColumnNames.hasNext()) { + byte[][] tuple = new byte[14][]; + String lColumnName = StringUtils.unQuoteIdentifier(localColumnNames.next(), this.quotedId); + String rColumnName = StringUtils.unQuoteIdentifier(referColumnNames.next(), this.quotedId); + tuple[FKTABLE_CAT] = ((catalog == null) ? new byte[0] : s2b(catalog)); + tuple[FKTABLE_SCHEM] = null; + tuple[FKTABLE_NAME] = s2b((isExport) ? fkTableName : table); + tuple[FKCOLUMN_NAME] = s2b(lColumnName); + tuple[PKTABLE_CAT] = s2b(parsedInfo.referencedCatalog); + tuple[PKTABLE_SCHEM] = null; + tuple[PKTABLE_NAME] = s2b((isExport) ? table : parsedInfo.referencedTable); + tuple[PKCOLUMN_NAME] = s2b(rColumnName); + tuple[KEY_SEQ] = s2b(Integer.toString(keySeqIndex++)); + + int[] actions = getForeignKeyActions(keysComment); + + tuple[UPDATE_RULE] = s2b(Integer.toString(actions[1])); + tuple[DELETE_RULE] = s2b(Integer.toString(actions[0])); + tuple[FK_NAME] = s2b(parsedInfo.constraintName); + tuple[PK_NAME] = null; // not available from show table status + tuple[DEFERRABILITY] = s2b(Integer.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable)); + tuples.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } + + /** + * Get the schema names available in this database. The results are ordered + * by schema name. + *

    + * The schema column is: + *

      + *
    1. TABLE_SCHEM String => schema name
    2. + *
    + *

    + * + * @return ResultSet each row has a single String column that is a schema + * name + * @throws SQLException + */ + public java.sql.ResultSet getSchemas() throws SQLException { + Field[] fields = new Field[2]; + fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0); + fields[1] = new Field("", "TABLE_CATALOG", java.sql.Types.CHAR, 0); + + ArrayList tuples = new ArrayList(); + java.sql.ResultSet results = buildResultSet(fields, tuples); + + return results; + } + + /** + * What's the database vendor's preferred term for "schema"? + * + * @return the vendor term + * @throws SQLException + */ + public String getSchemaTerm() throws SQLException { + return ""; + } + + /** + * This is the string that can be used to escape '_' or '%' in the string + * pattern style catalog search parameters. + *

    + * The '_' character represents any single character. + *

    + *

    + * The '%' character represents any sequence of zero or more characters. + *

    + * + * @return the string used to escape wildcard characters + * @throws SQLException + */ + public String getSearchStringEscape() throws SQLException { + return "\\"; + } + + /** + * Get a comma separated list of all a database's SQL keywords that are NOT also SQL92/SQL2003 keywords. + * + * @return the list + * @throws SQLException + */ + public String getSQLKeywords() throws SQLException { + if (mysqlKeywords != null) { + return mysqlKeywords; + } + + synchronized (DatabaseMetaData.class) { + // double check, maybe it's already set + if (mysqlKeywords != null) { + return mysqlKeywords; + } + + Set mysqlKeywordSet = new TreeSet(); + StringBuilder mysqlKeywordsBuffer = new StringBuilder(); + + Collections.addAll(mysqlKeywordSet, MYSQL_KEYWORDS); + mysqlKeywordSet.removeAll(Arrays.asList(Util.isJdbc4() ? SQL2003_KEYWORDS : SQL92_KEYWORDS)); + + for (String keyword : mysqlKeywordSet) { + mysqlKeywordsBuffer.append(",").append(keyword); + } + + mysqlKeywords = mysqlKeywordsBuffer.substring(1); + return mysqlKeywords; + } + } + + /** + * @see DatabaseMetaData#getSQLStateType() + */ + public int getSQLStateType() throws SQLException { + if (this.conn.versionMeetsMinimum(4, 1, 0)) { + return java.sql.DatabaseMetaData.sqlStateSQL99; + } + + if (this.conn.getUseSqlStateCodes()) { + return java.sql.DatabaseMetaData.sqlStateSQL99; + } + + return java.sql.DatabaseMetaData.sqlStateXOpen; + } + + /** + * Get a comma separated list of string functions. + * + * @return the list + * @throws SQLException + */ + public String getStringFunctions() throws SQLException { + return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT," + + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION," + + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING," + + "SUBSTRING_INDEX,TRIM,UCASE,UPPER"; + } + + /** + * @see DatabaseMetaData#getSuperTables(String, String, String) + */ + public java.sql.ResultSet getSuperTables(String arg0, String arg1, String arg2) throws SQLException { + Field[] fields = new Field[4]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 32); + fields[3] = new Field("", "SUPERTABLE_NAME", Types.CHAR, 32); + + return buildResultSet(fields, new ArrayList()); + } + + /** + * @see DatabaseMetaData#getSuperTypes(String, String, String) + */ + public java.sql.ResultSet getSuperTypes(String arg0, String arg1, String arg2) throws SQLException { + Field[] fields = new Field[6]; + fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32); + fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32); + fields[3] = new Field("", "SUPERTYPE_CAT", Types.CHAR, 32); + fields[4] = new Field("", "SUPERTYPE_SCHEM", Types.CHAR, 32); + fields[5] = new Field("", "SUPERTYPE_NAME", Types.CHAR, 32); + + return buildResultSet(fields, new ArrayList()); + } + + /** + * Get a comma separated list of system functions. + * + * @return the list + * @throws SQLException + */ + public String getSystemFunctions() throws SQLException { + return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION"; + } + + protected String getTableNameWithCase(String table) { + String tableNameWithCase = (this.conn.lowerCaseTableNames() ? table.toLowerCase() : table); + + return tableNameWithCase; + } + + /** + * Get a description of the access rights for each table available in a + * catalog. + *

    + * Only privileges matching the schema and table name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE. + *

    + *

    + * Each privilige description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. GRANTOR => grantor of access (may be null)
    10. + *
    11. GRANTEE String => grantee of access
    12. + *
    13. PRIVILEGE String => name of access (SELECT, INSERT, UPDATE, REFRENCES, ...)
    14. + *
    15. IS_GRANTABLE String => "YES" if grantee is permitted to grant to others; "NO" if not; null if unknown
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param tableNamePattern + * a table name pattern + * @return ResultSet each row is a table privilege description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + + if (tableNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + tableNamePattern = "%"; + } else { + throw SQLError.createSQLException("Table name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + Field[] fields = new Field[7]; + fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64); + fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1); + fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64); + fields[3] = new Field("", "GRANTOR", Types.CHAR, 77); + fields[4] = new Field("", "GRANTEE", Types.CHAR, 77); + fields[5] = new Field("", "PRIVILEGE", Types.CHAR, 64); + fields[6] = new Field("", "IS_GRANTABLE", Types.CHAR, 3); + + String grantQuery = "SELECT host,db,table_name,grantor,user,table_priv FROM mysql.tables_priv WHERE db LIKE ? AND table_name LIKE ?"; + + ResultSet results = null; + ArrayList grantRows = new ArrayList(); + PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(grantQuery); + + pStmt.setString(1, ((catalog != null) && (catalog.length() != 0)) ? catalog : "%"); + pStmt.setString(2, tableNamePattern); + + results = pStmt.executeQuery(); + + while (results.next()) { + String host = results.getString(1); + String db = results.getString(2); + String table = results.getString(3); + String grantor = results.getString(4); + String user = results.getString(5); + + if ((user == null) || (user.length() == 0)) { + user = "%"; + } + + StringBuilder fullUser = new StringBuilder(user); + + if ((host != null) && this.conn.getUseHostsInPrivileges()) { + fullUser.append("@"); + fullUser.append(host); + } + + String allPrivileges = results.getString(6); + + if (allPrivileges != null) { + allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH); + + StringTokenizer st = new StringTokenizer(allPrivileges, ","); + + while (st.hasMoreTokens()) { + String privilege = st.nextToken().trim(); + + // Loop through every column in the table + java.sql.ResultSet columnResults = null; + + try { + columnResults = getColumns(catalog, schemaPattern, table, "%"); + + while (columnResults.next()) { + byte[][] tuple = new byte[8][]; + tuple[0] = s2b(db); + tuple[1] = null; + tuple[2] = s2b(table); + + if (grantor != null) { + tuple[3] = s2b(grantor); + } else { + tuple[3] = null; + } + + tuple[4] = s2b(fullUser.toString()); + tuple[5] = s2b(privilege); + tuple[6] = null; + grantRows.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } finally { + if (columnResults != null) { + try { + columnResults.close(); + } catch (Exception ex) { + } + } + } + } + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (Exception ex) { + } + + pStmt = null; + } + } + + return buildResultSet(fields, grantRows); + } + + /** + * Get a description of tables available in a catalog. + *

    + * Only table descriptions matching the catalog, schema, table name and type criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and + * TABLE_NAME. + *

    + *

    + * Each table description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
    8. + *
    9. REMARKS String => explanatory comment on the table
    10. + *
    + *

    + *

    + * Note: Some databases may not return information for all tables. + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param tableNamePattern + * a table name pattern + * @param types + * a list of table types to include; null returns all types + * @return ResultSet each row is a table description + * @throws SQLException + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, final String[] types) throws SQLException { + + if (tableNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + tableNamePattern = "%"; + } else { + throw SQLError.createSQLException("Table name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + final SortedMap sortedRows = new TreeMap(); + final ArrayList tuples = new ArrayList(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + final String tableNamePat; + String tmpCat = ""; + + if ((catalog == null) || (catalog.length() == 0)) { + if (this.conn.getNullCatalogMeansCurrent()) { + tmpCat = this.database; + } + } else { + tmpCat = catalog; + } + + List parseList = StringUtils.splitDBdotName(tableNamePattern, tmpCat, this.quotedId, this.conn.isNoBackslashEscapesSet()); + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tableNamePat = parseList.get(1); + } else { + tableNamePat = tableNamePattern; + } + + try { + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + boolean operatingOnSystemDB = "information_schema".equalsIgnoreCase(catalogStr) || "mysql".equalsIgnoreCase(catalogStr) + || "performance_schema".equalsIgnoreCase(catalogStr); + + ResultSet results = null; + + try { + + try { + results = stmt + .executeQuery((!DatabaseMetaData.this.conn.versionMeetsMinimum(5, 0, 2) ? "SHOW TABLES FROM " : "SHOW FULL TABLES FROM ") + + StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic()) + + " LIKE " + StringUtils.quoteIdentifier(tableNamePat, "'", true)); + } catch (SQLException sqlEx) { + if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + + return; + } + + boolean shouldReportTables = false; + boolean shouldReportViews = false; + boolean shouldReportSystemTables = false; + boolean shouldReportSystemViews = false; + boolean shouldReportLocalTemporaries = false; + + if (types == null || types.length == 0) { + shouldReportTables = true; + shouldReportViews = true; + shouldReportSystemTables = true; + shouldReportSystemViews = true; + shouldReportLocalTemporaries = true; + } else { + for (int i = 0; i < types.length; i++) { + if (TableType.TABLE.equalsTo(types[i])) { + shouldReportTables = true; + + } else if (TableType.VIEW.equalsTo(types[i])) { + shouldReportViews = true; + + } else if (TableType.SYSTEM_TABLE.equalsTo(types[i])) { + shouldReportSystemTables = true; + + } else if (TableType.SYSTEM_VIEW.equalsTo(types[i])) { + shouldReportSystemViews = true; + + } else if (TableType.LOCAL_TEMPORARY.equalsTo(types[i])) { + shouldReportLocalTemporaries = true; + } + } + } + + int typeColumnIndex = 1; // MySQL 4.x returns the result set containing the single field with table names, so we always refer to first column + boolean hasTableTypes = false; + + if (DatabaseMetaData.this.conn.versionMeetsMinimum(5, 0, 2)) { + try { + // Both column names have been in use in the source tree so far.... + typeColumnIndex = results.findColumn("table_type"); + hasTableTypes = true; + } catch (SQLException sqlEx) { + + // We should probably check SQLState here, but that can change depending on the server version and user properties, however, + // we'll get a 'true' SQLException when we actually try to find the 'Type' column + // + try { + typeColumnIndex = results.findColumn("Type"); + hasTableTypes = true; + } catch (SQLException sqlEx2) { + hasTableTypes = false; + } + } + } + + while (results.next()) { + byte[][] row = new byte[10][]; + row[0] = (catalogStr == null) ? null : s2b(catalogStr); + row[1] = null; + row[2] = results.getBytes(1); + row[4] = new byte[0]; + row[5] = null; + row[6] = null; + row[7] = null; + row[8] = null; + row[9] = null; + + if (hasTableTypes) { + String tableType = results.getString(typeColumnIndex); + + switch (TableType.getTableTypeCompliantWith(tableType)) { + case TABLE: + boolean reportTable = false; + TableMetaDataKey tablesKey = null; + + if (operatingOnSystemDB && shouldReportSystemTables) { + row[3] = TableType.SYSTEM_TABLE.asBytes(); + tablesKey = new TableMetaDataKey(TableType.SYSTEM_TABLE.getName(), catalogStr, null, results.getString(1)); + reportTable = true; + + } else if (!operatingOnSystemDB && shouldReportTables) { + row[3] = TableType.TABLE.asBytes(); + tablesKey = new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)); + reportTable = true; + } + + if (reportTable) { + sortedRows.put(tablesKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case VIEW: + if (shouldReportViews) { + row[3] = TableType.VIEW.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.VIEW.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case SYSTEM_TABLE: + if (shouldReportSystemTables) { + row[3] = TableType.SYSTEM_TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.SYSTEM_TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case SYSTEM_VIEW: + if (shouldReportSystemViews) { + row[3] = TableType.SYSTEM_VIEW.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.SYSTEM_VIEW.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case LOCAL_TEMPORARY: + if (shouldReportLocalTemporaries) { + row[3] = TableType.LOCAL_TEMPORARY.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.LOCAL_TEMPORARY.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + default: + row[3] = TableType.TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + break; + } + } else { + if (shouldReportTables) { + // Pre-MySQL-5.0.1, tables only + row[3] = TableType.TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + } + } + + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + tuples.addAll(sortedRows.values()); + java.sql.ResultSet tables = buildResultSet(createTablesFields(), tuples); + + return tables; + } + + protected Field[] createTablesFields() { + Field[] fields = new Field[10]; + fields[0] = new Field("", "TABLE_CAT", java.sql.Types.VARCHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0); + fields[2] = new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255); + fields[3] = new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5); + fields[4] = new Field("", "REMARKS", java.sql.Types.VARCHAR, 0); + fields[5] = new Field("", "TYPE_CAT", java.sql.Types.VARCHAR, 0); + fields[6] = new Field("", "TYPE_SCHEM", java.sql.Types.VARCHAR, 0); + fields[7] = new Field("", "TYPE_NAME", java.sql.Types.VARCHAR, 0); + fields[8] = new Field("", "SELF_REFERENCING_COL_NAME", java.sql.Types.VARCHAR, 0); + fields[9] = new Field("", "REF_GENERATION", java.sql.Types.VARCHAR, 0); + return fields; + } + + /** + * Get the table types available in this database. The results are ordered + * by table type. + *

    + * The table type is: + *

      + *
    1. TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
    2. + *
    + *

    + * + * @return ResultSet each row has a single String column that is a table + * type + * @throws SQLException + */ + public java.sql.ResultSet getTableTypes() throws SQLException { + ArrayList tuples = new ArrayList(); + Field[] fields = new Field[] { new Field("", "TABLE_TYPE", Types.VARCHAR, 256) }; + + boolean minVersion5_0_1 = this.conn.versionMeetsMinimum(5, 0, 1); + + tuples.add(new ByteArrayRow(new byte[][] { TableType.LOCAL_TEMPORARY.asBytes() }, getExceptionInterceptor())); + tuples.add(new ByteArrayRow(new byte[][] { TableType.SYSTEM_TABLE.asBytes() }, getExceptionInterceptor())); + if (minVersion5_0_1) { + tuples.add(new ByteArrayRow(new byte[][] { TableType.SYSTEM_VIEW.asBytes() }, getExceptionInterceptor())); + } + tuples.add(new ByteArrayRow(new byte[][] { TableType.TABLE.asBytes() }, getExceptionInterceptor())); + if (minVersion5_0_1) { + tuples.add(new ByteArrayRow(new byte[][] { TableType.VIEW.asBytes() }, getExceptionInterceptor())); + } + + return buildResultSet(fields, tuples); + } + + /** + * Get a comma separated list of time and date functions. + * + * @return the list + * @throws SQLException + */ + public String getTimeDateFunctions() throws SQLException { + return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD," + + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE," + + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,SEC_TO_TIME,TIME_TO_SEC"; + } + + /** + * Get a description of all the standard SQL types supported by this + * database. They are ordered by DATA_TYPE and then by how closely the data + * type maps to the corresponding JDBC SQL type. + *

    + * Each type description has the following columns: + *

      + *
    1. TYPE_NAME String => Type name
    2. + *
    3. DATA_TYPE short => SQL data type from java.sql.Types
    4. + *
    5. PRECISION int => maximum precision
    6. + *
    7. LITERAL_PREFIX String => prefix used to quote a literal (may be null)
    8. + *
    9. LITERAL_SUFFIX String => suffix used to quote a literal (may be null)
    10. + *
    11. CREATE_PARAMS String => parameters used in creating the type (may be null)
    12. + *
    13. NULLABLE short => can you use NULL for this type? + *
        + *
      • typeNoNulls - does not allow NULL values
      • + *
      • typeNullable - allows NULL values
      • + *
      • typeNullableUnknown - nullability unknown
      • + *
      + *
    14. + *
    15. CASE_SENSITIVE boolean=> is it case sensitive?
    16. + *
    17. SEARCHABLE short => can you use "WHERE" based on this type: + *
        + *
      • typePredNone - No support
      • + *
      • typePredChar - Only supported with WHERE .. LIKE
      • + *
      • typePredBasic - Supported except for WHERE .. LIKE
      • + *
      • typeSearchable - Supported for all WHERE ..
      • + *
      + *
    18. + *
    19. UNSIGNED_ATTRIBUTE boolean => is it unsigned?
    20. + *
    21. FIXED_PREC_SCALE boolean => can it be a money value?
    22. + *
    23. AUTO_INCREMENT boolean => can it be used for an auto-increment value?
    24. + *
    25. LOCAL_TYPE_NAME String => localized version of type name (may be null)
    26. + *
    27. MINIMUM_SCALE short => minimum scale supported
    28. + *
    29. MAXIMUM_SCALE short => maximum scale supported
    30. + *
    31. SQL_DATA_TYPE int => unused
    32. + *
    33. SQL_DATETIME_SUB int => unused
    34. + *
    35. NUM_PREC_RADIX int => usually 2 or 10
    36. + *
    + *

    + * + * @return ResultSet each row is a SQL type description + * @throws SQLException + */ + /** + * Get a description of all the standard SQL types supported by this + * database. They are ordered by DATA_TYPE and then by how closely the data + * type maps to the corresponding JDBC SQL type. + *

    + * Each type description has the following columns: + *

      + *
    1. TYPE_NAME String => Type name
    2. + *
    3. DATA_TYPE short => SQL data type from java.sql.Types
    4. + *
    5. PRECISION int => maximum precision
    6. + *
    7. LITERAL_PREFIX String => prefix used to quote a literal (may be null)
    8. + *
    9. LITERAL_SUFFIX String => suffix used to quote a literal (may be null)
    10. + *
    11. CREATE_PARAMS String => parameters used in creating the type (may be null)
    12. + *
    13. NULLABLE short => can you use NULL for this type? + *
        + *
      • typeNoNulls - does not allow NULL values
      • + *
      • typeNullable - allows NULL values
      • + *
      • typeNullableUnknown - nullability unknown
      • + *
      + *
    14. + *
    15. CASE_SENSITIVE boolean=> is it case sensitive?
    16. + *
    17. SEARCHABLE short => can you use "WHERE" based on this type: + *
        + *
      • typePredNone - No support
      • + *
      • typePredChar - Only supported with WHERE .. LIKE
      • + *
      • typePredBasic - Supported except for WHERE .. LIKE
      • + *
      • typeSearchable - Supported for all WHERE ..
      • + *
      + *
    18. + *
    19. UNSIGNED_ATTRIBUTE boolean => is it unsigned?
    20. + *
    21. FIXED_PREC_SCALE boolean => can it be a money value?
    22. + *
    23. AUTO_INCREMENT boolean => can it be used for an auto-increment value?
    24. + *
    25. LOCAL_TYPE_NAME String => localized version of type name (may be null)
    26. + *
    27. MINIMUM_SCALE short => minimum scale supported
    28. + *
    29. MAXIMUM_SCALE short => maximum scale supported
    30. + *
    31. SQL_DATA_TYPE int => unused
    32. + *
    33. SQL_DATETIME_SUB int => unused
    34. + *
    35. NUM_PREC_RADIX int => usually 2 or 10
    36. + *
    + *

    + * + * @return ResultSet each row is a SQL type description + * @throws SQLException + */ + public java.sql.ResultSet getTypeInfo() throws SQLException { + Field[] fields = new Field[18]; + fields[0] = new Field("", "TYPE_NAME", Types.CHAR, 32); + fields[1] = new Field("", "DATA_TYPE", Types.INTEGER, 5); + fields[2] = new Field("", "PRECISION", Types.INTEGER, 10); + fields[3] = new Field("", "LITERAL_PREFIX", Types.CHAR, 4); + fields[4] = new Field("", "LITERAL_SUFFIX", Types.CHAR, 4); + fields[5] = new Field("", "CREATE_PARAMS", Types.CHAR, 32); + fields[6] = new Field("", "NULLABLE", Types.SMALLINT, 5); + fields[7] = new Field("", "CASE_SENSITIVE", Types.BOOLEAN, 3); + fields[8] = new Field("", "SEARCHABLE", Types.SMALLINT, 3); + fields[9] = new Field("", "UNSIGNED_ATTRIBUTE", Types.BOOLEAN, 3); + fields[10] = new Field("", "FIXED_PREC_SCALE", Types.BOOLEAN, 3); + fields[11] = new Field("", "AUTO_INCREMENT", Types.BOOLEAN, 3); + fields[12] = new Field("", "LOCAL_TYPE_NAME", Types.CHAR, 32); + fields[13] = new Field("", "MINIMUM_SCALE", Types.SMALLINT, 5); + fields[14] = new Field("", "MAXIMUM_SCALE", Types.SMALLINT, 5); + fields[15] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10); + fields[16] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10); + fields[17] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10); + + byte[][] rowVal = null; + ArrayList tuples = new ArrayList(); + + /* + * The following are ordered by java.sql.Types, and then by how closely the MySQL type matches the JDBC Type (per spec) + */ + /* + * MySQL Type: BIT (silently converted to TINYINT(1)) JDBC Type: BIT + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("BIT"); + rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("1"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("BIT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: BOOL (silently converted to TINYINT(1)) JDBC Type: BIT + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("BOOL"); + rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("1"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("BOOL"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TINYINT JDBC Type: TINYINT + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TINYINT"); + rowVal[1] = Integer.toString(java.sql.Types.TINYINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("3"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("TINYINT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("TINYINT UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.TINYINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("3"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("TINYINT UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: BIGINT JDBC Type: BIGINT + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("BIGINT"); + rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("19"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("BIGINT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("BIGINT UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("20"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("BIGINT UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: LONG VARBINARY JDBC Type: LONGVARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("LONG VARBINARY"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("16777215"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("LONG VARBINARY"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: MEDIUMBLOB JDBC Type: LONGVARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("MEDIUMBLOB"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("16777215"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("MEDIUMBLOB"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: LONGBLOB JDBC Type: LONGVARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("LONGBLOB"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes(); + + // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("LONGBLOB"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: BLOB JDBC Type: LONGVARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("BLOB"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("65535"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("BLOB"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TINYBLOB JDBC Type: LONGVARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TINYBLOB"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("TINYBLOB"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: VARBINARY (sliently converted to VARCHAR(M) BINARY) JDBC + * Type: VARBINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("VARBINARY"); + rowVal[1] = Integer.toString(java.sql.Types.VARBINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b(this.conn.versionMeetsMinimum(5, 0, 3) ? "65535" : "255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b("(M)"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("VARBINARY"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: BINARY (silently converted to CHAR(M) BINARY) JDBC Type: + * BINARY + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("BINARY"); + rowVal[1] = Integer.toString(java.sql.Types.BINARY).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b("(M)"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("BINARY"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: LONG VARCHAR JDBC Type: LONGVARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("LONG VARCHAR"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("16777215"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("LONG VARCHAR"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: MEDIUMTEXT JDBC Type: LONGVARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("MEDIUMTEXT"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("16777215"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("MEDIUMTEXT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: LONGTEXT JDBC Type: LONGVARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("LONGTEXT"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes(); + + // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("LONGTEXT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TEXT JDBC Type: LONGVARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TEXT"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("65535"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("TEXT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TINYTEXT JDBC Type: LONGVARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TINYTEXT"); + rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("TINYTEXT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: CHAR JDBC Type: CHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("CHAR"); + rowVal[1] = Integer.toString(java.sql.Types.CHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b("(M)"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("CHAR"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + // The maximum number of digits for DECIMAL or NUMERIC is 65 (64 from MySQL 5.0.3 to 5.0.5). + + int decimalPrecision = 254; + + if (this.conn.versionMeetsMinimum(5, 0, 3)) { + if (this.conn.versionMeetsMinimum(5, 0, 6)) { + decimalPrecision = 65; + } else { + decimalPrecision = 64; + } + } + + /* + * MySQL Type: NUMERIC (silently converted to DECIMAL) JDBC Type: NUMERIC + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("NUMERIC"); + rowVal[1] = Integer.toString(java.sql.Types.NUMERIC).getBytes(); + + // JDBC Data type + rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("NUMERIC"); // Locale Type Name + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: DECIMAL JDBC Type: DECIMAL + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("DECIMAL"); + rowVal[1] = Integer.toString(java.sql.Types.DECIMAL).getBytes(); + + // JDBC Data type + rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("DECIMAL"); // Locale Type Name + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: INTEGER JDBC Type: INTEGER + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("INTEGER"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("10"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("INTEGER"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("INTEGER UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("10"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("INTEGER UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: INT JDBC Type: INTEGER + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("INT"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("10"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("INT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("INT UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("10"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("INT UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: MEDIUMINT JDBC Type: INTEGER + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("MEDIUMINT"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("7"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("MEDIUMINT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("MEDIUMINT UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("8"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("MEDIUMINT UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: SMALLINT JDBC Type: SMALLINT + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("SMALLINT"); + rowVal[1] = Integer.toString(java.sql.Types.SMALLINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("5"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("SMALLINT"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + rowVal = new byte[18][]; + rowVal[0] = s2b("SMALLINT UNSIGNED"); + rowVal[1] = Integer.toString(java.sql.Types.SMALLINT).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("5"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("true"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("SMALLINT UNSIGNED"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: FLOAT JDBC Type: REAL (this is the SINGLE PERCISION + * floating point type) + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("FLOAT"); + rowVal[1] = Integer.toString(java.sql.Types.REAL).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("10"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("FLOAT"); // Locale Type Name + rowVal[13] = s2b("-38"); // Minimum Scale + rowVal[14] = s2b("38"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: DOUBLE JDBC Type: DOUBLE + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("DOUBLE"); + rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("17"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("DOUBLE"); // Locale Type Name + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: DOUBLE PRECISION JDBC Type: DOUBLE + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("DOUBLE PRECISION"); + rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("17"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("DOUBLE PRECISION"); // Locale Type Name + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: REAL (does not map to Types.REAL) JDBC Type: DOUBLE + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("REAL"); + rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("17"); // Precision + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + rowVal[5] = s2b("[(M,D)] [ZEROFILL]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("true"); // Auto Increment + rowVal[12] = s2b("REAL"); // Locale Type Name + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: VARCHAR JDBC Type: VARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("VARCHAR"); + rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b(this.conn.versionMeetsMinimum(5, 0, 3) ? "65535" : "255"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b("(M)"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("VARCHAR"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: ENUM JDBC Type: VARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("ENUM"); + rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("65535"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("ENUM"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: SET JDBC Type: VARCHAR + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("SET"); + rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("64"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("SET"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: DATE JDBC Type: DATE + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("DATE"); + rowVal[1] = Integer.toString(java.sql.Types.DATE).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("0"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("DATE"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TIME JDBC Type: TIME + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TIME"); + rowVal[1] = Integer.toString(java.sql.Types.TIME).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("0"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("TIME"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: DATETIME JDBC Type: TIMESTAMP + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("DATETIME"); + rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("0"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b(""); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("DATETIME"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + /* + * MySQL Type: TIMESTAMP JDBC Type: TIMESTAMP + */ + rowVal = new byte[18][]; + rowVal[0] = s2b("TIMESTAMP"); + rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes(); + + // JDBC Data type + rowVal[2] = s2b("0"); // Precision + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + rowVal[5] = s2b("[(M)]"); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); + + // Nullable + rowVal[7] = s2b("false"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); + + // Searchable + rowVal[9] = s2b("false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b("TIMESTAMP"); // Locale Type Name + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + + return buildResultSet(fields, tuples); + } + + /** + * JDBC 2.0 Get a description of the user-defined types defined in a + * particular schema. Schema specific UDTs may have type JAVA_OBJECT, + * STRUCT, or DISTINCT. + *

    + * Only types matching the catalog, schema, type name and type criteria are returned. They are ordered by DATA_TYPE, TYPE_SCHEM and TYPE_NAME. The type name + * parameter may be a fully qualified name. In this case, the catalog and schemaPattern parameters are ignored. + *

    + *

    + * Each type description has the following columns: + *

      + *
    1. TYPE_CAT String => the type's catalog (may be null)
    2. + *
    3. TYPE_SCHEM String => type's schema (may be null)
    4. + *
    5. TYPE_NAME String => type name
    6. + *
    7. CLASS_NAME String => Java class name
    8. + *
    9. DATA_TYPE String => type value defined in java.sql.Types. One of JAVA_OBJECT, STRUCT, or DISTINCT
    10. + *
    11. REMARKS String => explanatory comment on the type
    12. + *
    + *

    + *

    + * Note: If the driver does not support UDTs then an empty result set is returned. + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog; null + * means drop catalog name from the selection criteria + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param typeNamePattern + * a type name pattern; may be a fully qualified name + * @param types + * a list of user-named types to include (JAVA_OBJECT, STRUCT, or + * DISTINCT); null returns all types + * @return ResultSet - each row is a type description + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { + Field[] fields = new Field[7]; + fields[0] = new Field("", "TYPE_CAT", Types.VARCHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", Types.VARCHAR, 32); + fields[2] = new Field("", "TYPE_NAME", Types.VARCHAR, 32); + fields[3] = new Field("", "CLASS_NAME", Types.VARCHAR, 32); + fields[4] = new Field("", "DATA_TYPE", Types.INTEGER, 10); + fields[5] = new Field("", "REMARKS", Types.VARCHAR, 32); + fields[6] = new Field("", "BASE_TYPE", Types.SMALLINT, 10); + + ArrayList tuples = new ArrayList(); + + return buildResultSet(fields, tuples); + } + + /** + * What's the url for this database? + * + * @return the url or null if it can't be generated + * @throws SQLException + */ + public String getURL() throws SQLException { + return this.conn.getURL(); + } + + /** + * What's our user name as known to the database? + * + * @return our database user name + * @throws SQLException + */ + public String getUserName() throws SQLException { + if (this.conn.getUseHostsInPrivileges()) { + Statement stmt = null; + ResultSet rs = null; + + try { + stmt = this.conn.getMetadataSafeStatement(); + + rs = stmt.executeQuery("SELECT USER()"); + rs.next(); + + return rs.getString(1); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + AssertionFailedException.shouldNotHappen(ex); + } + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ex) { + AssertionFailedException.shouldNotHappen(ex); + } + + stmt = null; + } + } + } + + return this.conn.getUser(); + } + + /** + * Get a description of a table's columns that are automatically updated + * when any value in a row is updated. They are unordered. + *

    + * Each column description has the following columns: + *

      + *
    1. SCOPE short => is not used
    2. + *
    3. COLUMN_NAME String => column name
    4. + *
    5. DATA_TYPE short => SQL data type from java.sql.Types
    6. + *
    7. TYPE_NAME String => Data source dependent type name
    8. + *
    9. COLUMN_SIZE int => precision
    10. + *
    11. BUFFER_LENGTH int => length of column value in bytes
    12. + *
    13. DECIMAL_DIGITS short => scale
    14. + *
    15. PSEUDO_COLUMN short => is this a pseudo column like an Oracle ROWID + *
        + *
      • versionColumnUnknown - may or may not be pseudo column
      • + *
      • versionColumnNotPseudo - is NOT a pseudo column
      • + *
      • versionColumnPseudo - is a pseudo column
      • + *
      + *
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a column description + * @throws SQLException + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, final String table) throws SQLException { + + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + Field[] fields = new Field[8]; + fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5); + fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32); + fields[2] = new Field("", "DATA_TYPE", Types.INTEGER, 5); + fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 16); + fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 16); + fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 16); + fields[6] = new Field("", "DECIMAL_DIGITS", Types.SMALLINT, 16); + fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5); + + final ArrayList rows = new ArrayList(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet results = null; + boolean with_where = DatabaseMetaData.this.conn.versionMeetsMinimum(5, 0, 0); + + try { + StringBuilder whereBuf = new StringBuilder(" Extra LIKE '%on update CURRENT_TIMESTAMP%'"); + List rsFields = new ArrayList(); + + // for versions prior to 5.1.23 we can get "on update CURRENT_TIMESTAMP" + // only from SHOW CREATE TABLE + if (!DatabaseMetaData.this.conn.versionMeetsMinimum(5, 1, 23)) { + + whereBuf = new StringBuilder(); + boolean firstTime = true; + + String query = new StringBuilder("SHOW CREATE TABLE ").append(getFullyQualifiedName(catalogStr, table)).toString(); + + results = stmt.executeQuery(query); + while (results.next()) { + String createTableString = results.getString(2); + StringTokenizer lineTokenizer = new StringTokenizer(createTableString, "\n"); + + while (lineTokenizer.hasMoreTokens()) { + String line = lineTokenizer.nextToken().trim(); + if (StringUtils.indexOfIgnoreCase(line, "on update CURRENT_TIMESTAMP") > -1) { + boolean usingBackTicks = true; + int beginPos = line.indexOf(DatabaseMetaData.this.quotedId); + + if (beginPos == -1) { + beginPos = line.indexOf("\""); + usingBackTicks = false; + } + + if (beginPos != -1) { + int endPos = -1; + + if (usingBackTicks) { + endPos = line.indexOf(DatabaseMetaData.this.quotedId, beginPos + 1); + } else { + endPos = line.indexOf("\"", beginPos + 1); + } + + if (endPos != -1) { + if (with_where) { + if (!firstTime) { + whereBuf.append(" or"); + } else { + firstTime = false; + } + whereBuf.append(" Field='"); + whereBuf.append(line.substring(beginPos + 1, endPos)); + whereBuf.append("'"); + } else { + rsFields.add(line.substring(beginPos + 1, endPos)); + } + } + } + } + } + } + } + + if (whereBuf.length() > 0 || rsFields.size() > 0) { + StringBuilder queryBuf = new StringBuilder("SHOW COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.conn.getPedantic())); + if (with_where) { + queryBuf.append(" WHERE"); + queryBuf.append(whereBuf.toString()); + } + + results = stmt.executeQuery(queryBuf.toString()); + + while (results.next()) { + if (with_where || rsFields.contains(results.getString("Field"))) { + TypeDescriptor typeDesc = new TypeDescriptor(results.getString("Type"), results.getString("Null")); + byte[][] rowVal = new byte[8][]; + // SCOPE is not used + rowVal[0] = null; + // COLUMN_NAME + rowVal[1] = results.getBytes("Field"); + // DATA_TYPE + rowVal[2] = Short.toString(typeDesc.dataType).getBytes(); + // TYPE_NAME + rowVal[3] = s2b(typeDesc.typeName); + // COLUMN_SIZE + rowVal[4] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString()); + // BUFFER_LENGTH + rowVal[5] = s2b(Integer.toString(typeDesc.bufferLength)); + // DECIMAL_DIGITS + rowVal[6] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); + // PSEUDO_COLUMN + rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.versionColumnNotPseudo).getBytes(); + + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } + } + } catch (SQLException sqlEx) { + if (!SQLError.SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + return buildResultSet(fields, rows); + } + + /** + * JDBC 2.0 Determine whether or not a visible row insert can be detected by + * calling ResultSet.rowInserted(). + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean insertsAreDetected(int type) throws SQLException { + return false; + } + + /** + * Does a catalog appear at the start of a qualified table name? (Otherwise + * it appears at the end) + * + * @return true if it appears at the start + * @throws SQLException + */ + public boolean isCatalogAtStart() throws SQLException { + return true; + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @throws SQLException + */ + public boolean isReadOnly() throws SQLException { + return false; + } + + /** + * @see DatabaseMetaData#locatorsUpdateCopy() + */ + public boolean locatorsUpdateCopy() throws SQLException { + return !this.conn.getEmulateLocators(); + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A JDBC + * compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean nullPlusNonNullIsNull() throws SQLException { + return true; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedAtEnd() throws SQLException { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedAtStart() throws SQLException { + return (this.conn.versionMeetsMinimum(4, 0, 2) && !this.conn.versionMeetsMinimum(4, 0, 11)); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedHigh() throws SQLException { + return false; + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedLow() throws SQLException { + return !nullsAreSortedHigh(); + } + + /** + * @param type + * @throws SQLException + */ + public boolean othersDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean othersInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether changes made by others are visible. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are visible for the result set type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean othersUpdatesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean ownDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean ownInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether a result set's own changes visible. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are visible for the result set type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean ownUpdatesAreVisible(int type) throws SQLException { + return false; + } + + protected LocalAndReferencedColumns parseTableStatusIntoLocalAndReferencedColumns(String keysComment) throws SQLException { + // keys will equal something like this: (parent_service_id child_service_id) REFER ds/subservices(parent_service_id child_service_id) + // + // simple-columned keys: (m) REFER airline/tt(a) + // + // multi-columned keys : (m n) REFER airline/vv(a b) + // + // parse of the string into three phases: + // 1: parse the opening parentheses to determine how many results there will be + // 2: read in the schema name/table name + // 3: parse the closing parentheses + + String columnsDelimitter = ","; // what version did this change in? + + int indexOfOpenParenLocalColumns = StringUtils.indexOfIgnoreCase(0, keysComment, "(", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfOpenParenLocalColumns == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find start of local columns list.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String constraintName = StringUtils.unQuoteIdentifier(keysComment.substring(0, indexOfOpenParenLocalColumns).trim(), this.quotedId); + keysComment = keysComment.substring(indexOfOpenParenLocalColumns, keysComment.length()); + + String keysCommentTrimmed = keysComment.trim(); + + int indexOfCloseParenLocalColumns = StringUtils.indexOfIgnoreCase(0, keysCommentTrimmed, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCloseParenLocalColumns == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find end of local columns list.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String localColumnNamesString = keysCommentTrimmed.substring(1, indexOfCloseParenLocalColumns); + + int indexOfRefer = StringUtils.indexOfIgnoreCase(0, keysCommentTrimmed, "REFER ", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfRefer == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find start of referenced tables list.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + int indexOfOpenParenReferCol = StringUtils.indexOfIgnoreCase(indexOfRefer, keysCommentTrimmed, "(", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__MRK_COM_WS); + + if (indexOfOpenParenReferCol == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find start of referenced columns list.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referCatalogTableString = keysCommentTrimmed.substring(indexOfRefer + "REFER ".length(), indexOfOpenParenReferCol); + + int indexOfSlash = StringUtils.indexOfIgnoreCase(0, referCatalogTableString, "/", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__MRK_COM_WS); + + if (indexOfSlash == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find name of referenced catalog.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referCatalog = StringUtils.unQuoteIdentifier(referCatalogTableString.substring(0, indexOfSlash), this.quotedId); + String referTable = StringUtils.unQuoteIdentifier(referCatalogTableString.substring(indexOfSlash + 1).trim(), this.quotedId); + + int indexOfCloseParenRefer = StringUtils.indexOfIgnoreCase(indexOfOpenParenReferCol, keysCommentTrimmed, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCloseParenRefer == -1) { + throw SQLError.createSQLException("Error parsing foreign keys definition, couldn't find end of referenced columns list.", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referColumnNamesString = keysCommentTrimmed.substring(indexOfOpenParenReferCol + 1, indexOfCloseParenRefer); + + List referColumnsList = StringUtils.split(referColumnNamesString, columnsDelimitter, this.quotedId, this.quotedId, false); + List localColumnsList = StringUtils.split(localColumnNamesString, columnsDelimitter, this.quotedId, this.quotedId, false); + + return new LocalAndReferencedColumns(localColumnsList, referColumnsList, constraintName, referCatalog, referTable); + } + + /** + * Converts the given string to bytes, using the connection's character + * encoding, or if not available, the JVM default encoding. + * + * @param s + */ + protected byte[] s2b(String s) throws SQLException { + if (s == null) { + return null; + } + + return StringUtils.getBytes(s, this.conn.getCharacterSetMetadata(), this.conn.getServerCharset(), this.conn.parserKnowsUnicode(), this.conn, + getExceptionInterceptor()); + } + + /** + * Does the database store mixed case unquoted SQL identifiers in lower + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesLowerCaseIdentifiers() throws SQLException { + return this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case quoted SQL identifiers in lower case? + * A JDBC compliant driver will always return false. + * + * @return true if so + * @throws SQLException + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case unquoted SQL identifiers in mixed + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesMixedCaseIdentifiers() throws SQLException { + return !this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case quoted SQL identifiers in mixed case? + * A JDBC compliant driver will always return false. + * + * @return true if so + * @throws SQLException + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return !this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case unquoted SQL identifiers in upper + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesUpperCaseIdentifiers() throws SQLException { + return false; + } + + /** + * Does the database store mixed case quoted SQL identifiers in upper case? + * A JDBC compliant driver will always return true. + * + * @return true if so + * @throws SQLException + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return true; // not actually true, but required by JDBC spec!? + } + + /** + * Is "ALTER TABLE" with add column supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException { + return true; + } + + /** + * Is "ALTER TABLE" with drop column supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException { + return true; + } + + /** + * Is the ANSI92 entry level SQL grammar supported? All JDBC compliant + * drivers must return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return true; + } + + /** + * Is the ANSI92 full SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92FullSQL() throws SQLException { + return false; + } + + /** + * Is the ANSI92 intermediate SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Return true if the driver supports batch updates, else return + * false. + * + * @throws SQLException + */ + public boolean supportsBatchUpdates() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException { + // Servers before 3.22 could not do this + return this.conn.versionMeetsMinimum(3, 22, 0); + } + + /** + * Can a catalog name be used in a index definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + // Servers before 3.22 could not do this + return this.conn.versionMeetsMinimum(3, 22, 0); + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + // Servers before 3.22 could not do this + return this.conn.versionMeetsMinimum(3, 22, 0); + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + // Servers before 3.22 could not do this + return this.conn.versionMeetsMinimum(3, 22, 0); + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + // Servers before 3.22 could not do this + return this.conn.versionMeetsMinimum(3, 22, 0); + } + + /** + * Is column aliasing supported? + *

    + * If so, the SQL AS clause can be used to provide names for computed columns or to provide alias names for columns as required. A JDBC compliant driver + * always returns true. + *

    + * + * @return true if so + * @throws SQLException + */ + public boolean supportsColumnAliasing() throws SQLException { + return true; + } + + /** + * Is the CONVERT function between SQL types supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsConvert() throws SQLException { + return false; + } + + /** + * Is CONVERT between the given SQL types supported? + * + * @param fromType + * the type to convert from + * @param toType + * the type to convert to + * @return true if so + * @throws SQLException + * if an error occurs + * @see Types + */ + public boolean supportsConvert(int fromType, int toType) throws SQLException { + switch (fromType) { + /* + * The char/binary types can be converted to pretty much anything. + */ + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + + switch (toType) { + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + case java.sql.Types.OTHER: + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + return true; + + default: + return false; + } + + /* + * We don't handle the BIT type yet. + */ + case java.sql.Types.BIT: + return false; + + /* + * The numeric types. Basically they can convert among themselves, and with char/binary types. + */ + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + + switch (toType) { + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* MySQL doesn't support a NULL type. */ + case java.sql.Types.NULL: + return false; + + /* + * With this driver, this will always be a serialized object, so the char/binary types will work. + */ + case java.sql.Types.OTHER: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* Dates can be converted to char/binary types. */ + case java.sql.Types.DATE: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* Time can be converted to char/binary types */ + case java.sql.Types.TIME: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* + * Timestamp can be converted to char/binary types and date/time types (with loss of precision). + */ + case java.sql.Types.TIMESTAMP: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + case java.sql.Types.TIME: + case java.sql.Types.DATE: + return true; + + default: + return false; + } + + /* We shouldn't get here! */ + default: + return false; // not sure + } + } + + /** + * Is the ODBC Core SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCoreSQLGrammar() throws SQLException { + return true; + } + + /** + * Are correlated subqueries supported? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCorrelatedSubqueries() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Are both data definition and data manipulation statements within a + * transaction supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return false; + } + + /** + * Are only data manipulation statements within a transaction supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return false; + } + + /** + * If table correlation names are supported, are they restricted to be + * different from the names of the tables? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return true; + } + + /** + * Are expressions in "ORDER BY" lists supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsExpressionsInOrderBy() throws SQLException { + return true; + } + + /** + * Is the ODBC Extended SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsExtendedSQLGrammar() throws SQLException { + return false; + } + + /** + * Are full nested outer joins supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsFullOuterJoins() throws SQLException { + return false; + } + + /** + * JDBC 3.0 + */ + public boolean supportsGetGeneratedKeys() { + return true; + } + + /** + * Is some form of "GROUP BY" clause supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupBy() throws SQLException { + return true; + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided it + * specifies all the columns in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupByBeyondSelect() throws SQLException { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupByUnrelated() throws SQLException { + return true; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + if (!this.conn.getOverrideSupportsIntegrityEnhancementFacility()) { + return false; + } + + return true; + } + + /** + * Is the escape character in "LIKE" clauses supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsLikeEscapeClause() throws SQLException { + return true; + } + + /** + * Is there limited support for outer joins? (This will be true if + * supportFullOuterJoins is true.) + * + * @return true if so + * @throws SQLException + */ + public boolean supportsLimitedOuterJoins() throws SQLException { + return true; + } + + /** + * Is the ODBC Minimum SQL grammar supported? All JDBC compliant drivers + * must return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMinimumSQLGrammar() throws SQLException { + return true; + } + + /** + * Does the database support mixed case unquoted SQL identifiers? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException { + return !this.conn.lowerCaseTableNames(); + } + + /** + * Does the database support mixed case quoted SQL identifiers? A JDBC + * compliant driver will always return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return !this.conn.lowerCaseTableNames(); + } + + /** + * @see DatabaseMetaData#supportsMultipleOpenResults() + */ + public boolean supportsMultipleOpenResults() throws SQLException { + return true; + } + + /** + * Are multiple ResultSets from a single execute supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMultipleResultSets() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Can we have multiple transactions open at once (on different + * connections)? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMultipleTransactions() throws SQLException { + return true; + } + + /** + * @see DatabaseMetaData#supportsNamedParameters() + */ + public boolean supportsNamedParameters() throws SQLException { + return false; + } + + /** + * Can columns be defined as non-nullable? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsNonNullableColumns() throws SQLException { + return true; + } + + /** + * Can cursors remain open across commits? + * + * @return true if so + * @throws SQLException + * if a database access error occurs + * @see Connection#disableAutoClose + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return false; + } + + /** + * Can cursors remain open across rollbacks? + * + * @return true if so + * @throws SQLException + * if an error occurs + * @see Connection#disableAutoClose + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return false; + } + + /** + * Can statements remain open across commits? + * + * @return true if so + * @throws SQLException + * if an error occurs + * @see Connection#disableAutoClose + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return false; + } + + /** + * Can statements remain open across rollbacks? + * + * @return true if so + * @throws SQLException + * if an error occurs + * @see Connection#disableAutoClose + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return false; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsOrderByUnrelated() throws SQLException { + return false; + } + + /** + * Is some form of outer join supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsOuterJoins() throws SQLException { + return true; + } + + /** + * Is positioned DELETE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsPositionedDelete() throws SQLException { + return false; + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsPositionedUpdate() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Does the database support the concurrency type in combination + * with the given result set type? + * + * @param type + * defined in java.sql.ResultSet + * @param concurrency + * type defined in java.sql.ResultSet + * @return true if so + * @exception SQLException + * if a database-access error occurs. + * @see Connection + */ + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + switch (type) { + case ResultSet.TYPE_SCROLL_INSENSITIVE: + if ((concurrency == ResultSet.CONCUR_READ_ONLY) || (concurrency == ResultSet.CONCUR_UPDATABLE)) { + return true; + } + throw SQLError.createSQLException("Illegal arguments to supportsResultSetConcurrency()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + case ResultSet.TYPE_FORWARD_ONLY: + if ((concurrency == ResultSet.CONCUR_READ_ONLY) || (concurrency == ResultSet.CONCUR_UPDATABLE)) { + return true; + } + throw SQLError.createSQLException("Illegal arguments to supportsResultSetConcurrency()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + case ResultSet.TYPE_SCROLL_SENSITIVE: + return false; + default: + throw SQLError.createSQLException("Illegal arguments to supportsResultSetConcurrency()", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + } + + /** + * @see DatabaseMetaData#supportsResultSetHoldability(int) + */ + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT); + } + + /** + * JDBC 2.0 Does the database support the given result set type? + * + * @param type + * defined in java.sql.ResultSet + * @return true if so + * @exception SQLException + * if a database-access error occurs. + * @see Connection + */ + public boolean supportsResultSetType(int type) throws SQLException { + return (type == ResultSet.TYPE_SCROLL_INSENSITIVE); + } + + /** + * @see DatabaseMetaData#supportsSavepoints() + */ + public boolean supportsSavepoints() throws SQLException { + + return (this.conn.versionMeetsMinimum(4, 0, 14) || this.conn.versionMeetsMinimum(4, 1, 1)); + } + + /** + * Can a schema name be used in a data manipulation statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInDataManipulation() throws SQLException { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a table definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException { + return false; + } + + /** + * Is SELECT for UPDATE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSelectForUpdate() throws SQLException { + return this.conn.versionMeetsMinimum(4, 0, 0); + } + + /** + * @see DatabaseMetaData#supportsStatementPooling() + */ + public boolean supportsStatementPooling() throws SQLException { + return false; + } + + /** + * Are stored procedure calls using the stored procedure escape syntax + * supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsStoredProcedures() throws SQLException { + return this.conn.versionMeetsMinimum(5, 0, 0); + } + + /** + * Are subqueries in comparison expressions supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInComparisons() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Are subqueries in exists expressions supported? A JDBC compliant driver + * always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInExists() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Are subqueries in "in" statements supported? A JDBC compliant driver + * always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInIns() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Are subqueries in quantified expressions supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return this.conn.versionMeetsMinimum(4, 1, 0); + } + + /** + * Are table correlation names supported? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsTableCorrelationNames() throws SQLException { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * + * @param level + * the values are defined in java.sql.Connection + * @return true if so + * @throws SQLException + * if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + if (this.conn.supportsIsolationLevel()) { + switch (level) { + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: + case java.sql.Connection.TRANSACTION_REPEATABLE_READ: + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + return true; + + default: + return false; + } + } + + return false; + } + + /** + * Are transactions supported? If not, commit is a noop and the isolation + * level is TRANSACTION_NONE. + * + * @return true if transactions are supported + * @throws SQLException + */ + public boolean supportsTransactions() throws SQLException { + return this.conn.supportsTransactions(); + } + + /** + * Is SQL UNION supported? A JDBC compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsUnion() throws SQLException { + return this.conn.versionMeetsMinimum(4, 0, 0); + } + + /** + * Is SQL UNION ALL supported? A JDBC compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsUnionAll() throws SQLException { + return this.conn.versionMeetsMinimum(4, 0, 0); + } + + /** + * JDBC 2.0 Determine whether or not a visible row update can be detected by + * calling ResultSet.rowUpdated(). + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean updatesAreDetected(int type) throws SQLException { + return false; + } + + /** + * Does the database use a file for each table? + * + * @return true if the database uses a local file for each table + * @throws SQLException + */ + public boolean usesLocalFilePerTable() throws SQLException { + return false; + } + + /** + * Does the database store tables in a local file? + * + * @return true if so + * @throws SQLException + */ + public boolean usesLocalFiles() throws SQLException { + return false; + } + + // + // JDBC-4.0 functions that aren't reliant on Java6 + // + + /** + * Retrieves a list of the client info properties that the driver supports. The result set contains the following + * columns + *

    + *

      + *
    1. NAME String=> The name of the client info property
      + *
    2. MAX_LEN int=> The maximum length of the value for the property
      + *
    3. DEFAULT_VALUE String=> The default value of the property
      + *
    4. DESCRIPTION String=> A description of the property. This will typically contain information as to where this property is stored in the + * database. + *
    + *

    + * The ResultSet is sorted by the NAME column + *

    + * + * @return A ResultSet object; each row is a supported client info property + *

    + * @exception SQLException + * if a database access error occurs + *

    + * @since 1.6 + */ + public ResultSet getClientInfoProperties() throws SQLException { + // We don't have any built-ins, we actually support whatever the client wants to provide, however we don't have a way to express this with the interface + // given + Field[] fields = new Field[4]; + fields[0] = new Field("", "NAME", Types.VARCHAR, 255); + fields[1] = new Field("", "MAX_LEN", Types.INTEGER, 10); + fields[2] = new Field("", "DEFAULT_VALUE", Types.VARCHAR, 255); + fields[3] = new Field("", "DESCRIPTION", Types.VARCHAR, 255); + + return buildResultSet(fields, new ArrayList(), this.conn); + } + + /** + * Retrieves a description of the given catalog's system or user + * function parameters and return type. + * + * @see java.sql.DatabaseMetaData#getFunctionColumns(String, String, String, String) + * @since 1.6 + */ + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + Field[] fields = createFunctionColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, functionNamePattern, columnNamePattern, false, true); + } + + protected Field[] createFunctionColumnsFields() { + Field[] fields = { new Field("", "FUNCTION_CAT", Types.VARCHAR, 512), new Field("", "FUNCTION_SCHEM", Types.VARCHAR, 512), + new Field("", "FUNCTION_NAME", Types.VARCHAR, 512), new Field("", "COLUMN_NAME", Types.VARCHAR, 512), + new Field("", "COLUMN_TYPE", Types.VARCHAR, 64), new Field("", "DATA_TYPE", Types.SMALLINT, 6), new Field("", "TYPE_NAME", Types.VARCHAR, 64), + new Field("", "PRECISION", Types.INTEGER, 12), new Field("", "LENGTH", Types.INTEGER, 12), new Field("", "SCALE", Types.SMALLINT, 12), + new Field("", "RADIX", Types.SMALLINT, 6), new Field("", "NULLABLE", Types.SMALLINT, 6), new Field("", "REMARKS", Types.VARCHAR, 512), + new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32), new Field("", "ORDINAL_POSITION", Types.INTEGER, 32), + new Field("", "IS_NULLABLE", Types.VARCHAR, 12), new Field("", "SPECIFIC_NAME", Types.VARCHAR, 64) }; + return fields; + } + + /** + * Retrieves a description of the system and user functions available + * in the given catalog. + *

    + * Only system and user function descriptions matching the schema and function name criteria are returned. They are ordered by FUNCTION_CAT, + * FUNCTION_SCHEM, FUNCTION_NAME and SPECIFIC_ NAME. + * + *

    + * Each function description has the the following columns: + *

      + *
    1. FUNCTION_CAT String => function catalog (may be null) + *
    2. FUNCTION_SCHEM String => function schema (may be null) + *
    3. FUNCTION_NAME String => function name. This is the name used to invoke the function + *
    4. REMARKS String => explanatory comment on the function + *
    5. FUNCTION_TYPE short => kind of function: + *
        + *
      • functionResultUnknown - Cannot determine if a return value or table will be returned + *
      • functionNoTable- Does not return a table + *
      • functionReturnsTable - Returns a table + *
      + *
    6. SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user specified, or DBMS generated, name + * that may be different then the FUNCTION_NAME for example with overload functions + *
    + *

    + * A user may not have permission to execute any of the functions that are returned by getFunctions + * + * @param catalog + * a catalog name; must match the catalog name as it + * is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow + * the search + * @param schemaPattern + * a schema name pattern; must match the schema name + * as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to + * narrow + * the search + * @param functionNamePattern + * a function name pattern; must match the + * function name as it is stored in the database + * @return ResultSet - each row is a function description + * @exception SQLException + * if a database access error occurs + * @see #getSearchStringEscape + * @since 1.6 + */ + public java.sql.ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + Field[] fields = new Field[6]; + + fields[0] = new Field("", "FUNCTION_CAT", Types.CHAR, 255); + fields[1] = new Field("", "FUNCTION_SCHEM", Types.CHAR, 255); + fields[2] = new Field("", "FUNCTION_NAME", Types.CHAR, 255); + fields[3] = new Field("", "REMARKS", Types.CHAR, 255); + fields[4] = new Field("", "FUNCTION_TYPE", Types.SMALLINT, 6); + fields[5] = new Field("", "SPECIFIC_NAME", Types.CHAR, 255); + + return getProceduresAndOrFunctions(fields, catalog, schemaPattern, functionNamePattern, false, true); + } + + public boolean providesQueryObjectGenerator() throws SQLException { + return false; + } + + /** + * @param catalog + * @param schemaPattern + * @throws SQLException + */ + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + Field[] fields = { new Field("", "TABLE_SCHEM", Types.VARCHAR, 255), new Field("", "TABLE_CATALOG", Types.VARCHAR, 255) }; + + return buildResultSet(fields, new ArrayList()); + } + + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return true; + } + + /** + * Get a prepared statement to query information_schema tables. + * + * @return PreparedStatement + * @throws SQLException + */ + protected java.sql.PreparedStatement prepareMetaDataSafeStatement(String sql) throws SQLException { + // Can't use server-side here as we coerce a lot of types to match the spec. + java.sql.PreparedStatement pStmt = this.conn.clientPrepareStatement(sql); + + if (pStmt.getMaxRows() != 0) { + pStmt.setMaxRows(0); + } + + ((com.mysql.jdbc.Statement) pStmt).setHoldResultsOpenOverClose(true); + + return pStmt; + } + + /** + * JDBC-4.1 + * + * @param catalog + * @param schemaPattern + * @param tableNamePattern + * @param columnNamePattern + * @throws SQLException + */ + public java.sql.ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + Field[] fields = { new Field("", "TABLE_CAT", Types.VARCHAR, 512), new Field("", "TABLE_SCHEM", Types.VARCHAR, 512), + new Field("", "TABLE_NAME", Types.VARCHAR, 512), new Field("", "COLUMN_NAME", Types.VARCHAR, 512), + new Field("", "DATA_TYPE", Types.INTEGER, 12), new Field("", "COLUMN_SIZE", Types.INTEGER, 12), + new Field("", "DECIMAL_DIGITS", Types.INTEGER, 12), new Field("", "NUM_PREC_RADIX", Types.INTEGER, 12), + new Field("", "COLUMN_USAGE", Types.VARCHAR, 512), new Field("", "REMARKS", Types.VARCHAR, 512), + new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 12), new Field("", "IS_NULLABLE", Types.VARCHAR, 512) }; + + return buildResultSet(fields, new ArrayList()); + } + + // JDBC-4.1 + public boolean generatedKeyAlwaysReturned() throws SQLException { + return true; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java new file mode 100644 index 0000000..4e096f4 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java @@ -0,0 +1,1566 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +/** + * DatabaseMetaData implementation that uses INFORMATION_SCHEMA available in MySQL-5.0 and newer. + */ +public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData { + + protected enum JDBC4FunctionConstant { + // COLUMN_TYPE values + FUNCTION_COLUMN_UNKNOWN, FUNCTION_COLUMN_IN, FUNCTION_COLUMN_INOUT, FUNCTION_COLUMN_OUT, FUNCTION_COLUMN_RETURN, FUNCTION_COLUMN_RESULT, + // NULLABLE values + FUNCTION_NO_NULLS, FUNCTION_NULLABLE, FUNCTION_NULLABLE_UNKNOWN; + } + + private boolean hasReferentialConstraintsView; + private final boolean hasParametersView; + + protected DatabaseMetaDataUsingInfoSchema(MySQLConnection connToSet, String databaseToSet) throws SQLException { + super(connToSet, databaseToSet); + + this.hasReferentialConstraintsView = this.conn.versionMeetsMinimum(5, 1, 10); + + ResultSet rs = null; + + try { + rs = super.getTables("INFORMATION_SCHEMA", null, "PARAMETERS", new String[0]); + + this.hasParametersView = rs.next(); + } finally { + if (rs != null) { + rs.close(); + } + } + } + + protected ResultSet executeMetadataQuery(java.sql.PreparedStatement pStmt) throws SQLException { + ResultSet rs = pStmt.executeQuery(); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).setOwningStatement(null); + + return rs; + } + + /** + * Get a description of the access rights for a table's columns. + *

    + * Only privileges matching the column name criteria are returned. They are ordered by COLUMN_NAME and PRIVILEGE. + *

    + *

    + * Each privilige description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. GRANTOR => grantor of access (may be null)
    10. + *
    11. GRANTEE String => grantee of access
    12. + *
    13. PRIVILEGE String => name of access (SELECT, INSERT, UPDATE, REFRENCES, ...)
    14. + *
    15. IS_GRANTABLE String => "YES" if grantee is permitted to grant to others; "NO" if not; null if unknown
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name; "" retrieves those without a schema + * @param table + * a table name + * @param columnNamePattern + * a column name pattern + * @return ResultSet each row is a column privilege description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + @Override + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + if (columnNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + columnNamePattern = "%"; + } else { + throw SQLError.createSQLException("Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME," + + "COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE " + + "TABLE_SCHEMA LIKE ? AND TABLE_NAME =? AND COLUMN_NAME LIKE ? ORDER BY COLUMN_NAME, PRIVILEGE_TYPE"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + pStmt.setString(3, columnNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 64), + new Field("", "TABLE_SCHEM", Types.CHAR, 1), new Field("", "TABLE_NAME", Types.CHAR, 64), new Field("", "COLUMN_NAME", Types.CHAR, 64), + new Field("", "GRANTOR", Types.CHAR, 77), new Field("", "GRANTEE", Types.CHAR, 77), new Field("", "PRIVILEGE", Types.CHAR, 64), + new Field("", "IS_GRANTABLE", Types.CHAR, 3) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of table columns available in a catalog. + *

    + * Only column descriptions matching the catalog, schema, table and column name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME and + * ORDINAL_POSITION. + *

    + *

    + * Each column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. DATA_TYPE short => SQL type from java.sql.Types
    10. + *
    11. TYPE_NAME String => Data source dependent type name
    12. + *
    13. COLUMN_SIZE int => column size. For char or date types this is the maximum number of characters, for numeric or decimal types this is + * precision.
    14. + *
    15. BUFFER_LENGTH is not used.
    16. + *
    17. DECIMAL_DIGITS int => the number of fractional digits
    18. + *
    19. NUM_PREC_RADIX int => Radix (typically either 10 or 2)
    20. + *
    21. NULLABLE int => is NULL allowed? + *
        + *
      • columnNoNulls - might not allow NULL values
      • + *
      • columnNullable - definitely allows NULL values
      • + *
      • columnNullableUnknown - nullability unknown
      • + *
      + *
    22. + *
    23. REMARKS String => comment describing column (may be null)
    24. + *
    25. COLUMN_DEF String => default value (may be null)
    26. + *
    27. SQL_DATA_TYPE int => unused
    28. + *
    29. SQL_DATETIME_SUB int => unused
    30. + *
    31. CHAR_OCTET_LENGTH int => for char types the maximum number of bytes in the column
    32. + *
    33. ORDINAL_POSITION int => index of column in table (starting at 1)
    34. + *
    35. IS_NULLABLE String => "NO" means column definitely does not allow NULL values; "YES" means the column might allow NULL values. An empty string + * means nobody knows.
    36. + *
    + *

    + */ + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableName, String columnNamePattern) throws SQLException { + if (columnNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + columnNamePattern = "%"; + } else { + throw SQLError.createSQLException("Column name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME,"); + MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); + + sqlBuf.append(" AS DATA_TYPE, "); + + if (this.conn.getCapitalizeTypeNames()) { + sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 AND LOCATE('set', DATA_TYPE) <> 1 AND " + + "LOCATE('enum', DATA_TYPE) <> 1 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END) AS TYPE_NAME,"); + } else { + sqlBuf.append("CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 AND LOCATE('set', DATA_TYPE) <> 1 AND " + + "LOCATE('enum', DATA_TYPE) <> 1 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END AS TYPE_NAME,"); + } + + sqlBuf.append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19 " + + "WHEN LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS COLUMN_SIZE, " + MysqlIO.getMaxBuf() + + " AS BUFFER_LENGTH," + "NUMERIC_SCALE AS DECIMAL_DIGITS," + "10 AS NUM_PREC_RADIX," + "CASE WHEN IS_NULLABLE='NO' THEN " + columnNoNulls + + " ELSE CASE WHEN IS_NULLABLE='YES' THEN " + columnNullable + " ELSE " + columnNullableUnknown + " END END AS NULLABLE," + + "COLUMN_COMMENT AS REMARKS," + "COLUMN_DEFAULT AS COLUMN_DEF," + "0 AS SQL_DATA_TYPE," + "0 AS SQL_DATETIME_SUB," + + "CASE WHEN CHARACTER_OCTET_LENGTH > " + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + + " ELSE CHARACTER_OCTET_LENGTH END AS CHAR_OCTET_LENGTH," + "ORDINAL_POSITION," + "IS_NULLABLE," + "NULL AS SCOPE_CATALOG," + + "NULL AS SCOPE_SCHEMA," + "NULL AS SCOPE_TABLE," + "NULL AS SOURCE_DATA_TYPE," + + "IF (EXTRA LIKE '%auto_increment%','YES','NO') AS IS_AUTOINCREMENT, " + + "IF (EXTRA LIKE '%GENERATED%','YES','NO') AS IS_GENERATEDCOLUMN FROM INFORMATION_SCHEMA.COLUMNS WHERE "); + + final boolean operatingOnInformationSchema = "information_schema".equalsIgnoreCase(catalog); + + if (catalog != null) { + if ((operatingOnInformationSchema) + || ((StringUtils.indexOfIgnoreCase(0, catalog, "%") == -1) && (StringUtils.indexOfIgnoreCase(0, catalog, "_") == -1))) { + sqlBuf.append("TABLE_SCHEMA = ? AND "); + } else { + sqlBuf.append("TABLE_SCHEMA LIKE ? AND "); + } + + } else { + sqlBuf.append("TABLE_SCHEMA LIKE ? AND "); + } + + if (tableName != null) { + if ((StringUtils.indexOfIgnoreCase(0, tableName, "%") == -1) && (StringUtils.indexOfIgnoreCase(0, tableName, "_") == -1)) { + sqlBuf.append("TABLE_NAME = ? AND "); + } else { + sqlBuf.append("TABLE_NAME LIKE ? AND "); + } + + } else { + sqlBuf.append("TABLE_NAME LIKE ? AND "); + } + + if ((StringUtils.indexOfIgnoreCase(0, columnNamePattern, "%") == -1) && (StringUtils.indexOfIgnoreCase(0, columnNamePattern, "_") == -1)) { + sqlBuf.append("COLUMN_NAME = ? "); + } else { + sqlBuf.append("COLUMN_NAME LIKE ? "); + } + sqlBuf.append("ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, tableName); + pStmt.setString(3, columnNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createColumnsFields()); + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of the foreign key columns in the foreign key table + * that reference the primary key columns of the primary key table (describe + * how one table imports another's key.) This should normally return a + * single foreign key/primary key pair (most tables only import a foreign + * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + *

    + * Each foreign key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name
    6. + *
    7. PKCOLUMN_NAME String => primary key column name
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null) being exported (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null) being exported (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name being exported
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name being exported
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key identifier (may be null)
    24. + *
    25. PK_NAME String => primary key identifier (may be null)
    26. + *
    + *

    + * + * @param primaryCatalog + * a catalog name; "" retrieves those without a catalog + * @param primarySchema + * a schema name pattern; "" retrieves those without a schema + * @param primaryTable + * a table name + * @param foreignCatalog + * a catalog name; "" retrieves those without a catalog + * @param foreignSchema + * a schema name pattern; "" retrieves those without a schema + * @param foreignTable + * a table name + * @return ResultSet each row is a foreign key column description + * @throws SQLException + * if a database access error occurs + */ + @Override + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, + String foreignTable) throws SQLException { + if (primaryTable == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (primaryCatalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + primaryCatalog = this.database; + } + } + + if (foreignCatalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + foreignCatalog = this.database; + } + } + + String sql = "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,NULL AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," + + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME, " + + "A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = A.REFERENCED_TABLE_NAME AND" + + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN " + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " + + "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) " + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + + "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? " + + "AND A.TABLE_SCHEMA LIKE ? AND A.TABLE_NAME=? ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + if (primaryCatalog != null) { + pStmt.setString(1, primaryCatalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, primaryTable); + + if (foreignCatalog != null) { + pStmt.setString(3, foreignCatalog); + } else { + pStmt.setString(3, "%"); + } + + pStmt.setString(4, foreignTable); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of a foreign key columns that reference a table's + * primary key columns (the foreign keys exported by a table). They are + * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. + *

    + * Each foreign key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name
    6. + *
    7. PKCOLUMN_NAME String => primary key column name
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null) being exported (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null) being exported (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name being exported
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name being exported
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key identifier (may be null)
    24. + *
    25. PK_NAME String => primary key identifier (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a foreign key column description + * @throws SQLException + * if a database access error occurs + * @see #getImportedKeys + */ + @Override + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + // TODO: Can't determine actions using INFORMATION_SCHEMA yet... + + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + //CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION + + String sql = "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT, NULL AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, " + + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME," + + "A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = A.REFERENCED_TABLE_NAME AND" + + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN " + "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B " + + "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) " + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + + "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? " + "ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + + } + + private String generateOptionalRefContraintsJoin() { + return ((this.hasReferentialConstraintsView) ? "JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R ON (R.CONSTRAINT_NAME = B.CONSTRAINT_NAME " + + "AND R.TABLE_NAME = B.TABLE_NAME AND R.CONSTRAINT_SCHEMA = B.TABLE_SCHEMA) " : ""); + } + + private String generateDeleteRuleClause() { + return ((this.hasReferentialConstraintsView) ? "CASE WHEN R.DELETE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + + " WHEN R.DELETE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull) + " WHEN R.DELETE_RULE='SET DEFAULT' THEN " + + String.valueOf(importedKeySetDefault) + " WHEN R.DELETE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + + " WHEN R.DELETE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END " + : String.valueOf(importedKeyRestrict)); + } + + private String generateUpdateRuleClause() { + return ((this.hasReferentialConstraintsView) ? "CASE WHEN R.UPDATE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + + " WHEN R.UPDATE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull) + " WHEN R.UPDATE_RULE='SET DEFAULT' THEN " + + String.valueOf(importedKeySetDefault) + " WHEN R.UPDATE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + + " WHEN R.UPDATE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END " + : String.valueOf(importedKeyRestrict)); + } + + /** + * Get a description of the primary key columns that are referenced by a + * table's foreign key columns (the primary keys imported by a table). They + * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + *

    + * Each primary key column description has the following columns: + *

      + *
    1. PKTABLE_CAT String => primary key table catalog being imported (may be null)
    2. + *
    3. PKTABLE_SCHEM String => primary key table schema being imported (may be null)
    4. + *
    5. PKTABLE_NAME String => primary key table name being imported
    6. + *
    7. PKCOLUMN_NAME String => primary key column name being imported
    8. + *
    9. FKTABLE_CAT String => foreign key table catalog (may be null)
    10. + *
    11. FKTABLE_SCHEM String => foreign key table schema (may be null)
    12. + *
    13. FKTABLE_NAME String => foreign key table name
    14. + *
    15. FKCOLUMN_NAME String => foreign key column name
    16. + *
    17. KEY_SEQ short => sequence number within foreign key
    18. + *
    19. UPDATE_RULE short => What happens to foreign key when primary is updated: + *
        + *
      • importedKeyCascade - change imported key to agree with primary key update
      • + *
      • importedKeyRestrict - do not allow update of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been updated
      • + *
      + *
    20. + *
    21. DELETE_RULE short => What happens to the foreign key when primary is deleted. + *
        + *
      • importedKeyCascade - delete rows that import a deleted key
      • + *
      • importedKeyRestrict - do not allow delete of primary key if it has been imported
      • + *
      • importedKeySetNull - change imported key to NULL if its primary key has been deleted
      • + *
      + *
    22. + *
    23. FK_NAME String => foreign key name (may be null)
    24. + *
    25. PK_NAME String => primary key name (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a primary key column description + * @throws SQLException + * if a database access error occurs + * @see #getExportedKeys + */ + @Override + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + String sql = "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT, NULL AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," + + "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME, " + + "A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ," + generateUpdateRuleClause() + " AS UPDATE_RULE," + + generateDeleteRuleClause() + " AS DELETE_RULE," + "A.CONSTRAINT_NAME AS FK_NAME," + "(SELECT CONSTRAINT_NAME FROM" + + " INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + " WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA AND" + " TABLE_NAME = A.REFERENCED_TABLE_NAME AND" + + " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)" + " AS PK_NAME," + importedKeyNotDeferrable + " AS DEFERRABILITY " + "FROM " + + "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A " + "JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING " + "(CONSTRAINT_NAME, TABLE_NAME) " + + generateOptionalRefContraintsJoin() + "WHERE " + "B.CONSTRAINT_TYPE = 'FOREIGN KEY' " + "AND A.TABLE_SCHEMA LIKE ? " + "AND A.TABLE_NAME=? " + + "AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL " + "ORDER BY A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, A.ORDINAL_POSITION"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of a table's indices and statistics. They are ordered + * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + *

    + * Each index column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. NON_UNIQUE boolean => Can index values be non-unique? false when TYPE is tableIndexStatistic
    8. + *
    9. INDEX_QUALIFIER String => index catalog (may be null); null when TYPE is tableIndexStatistic
    10. + *
    11. INDEX_NAME String => index name; null when TYPE is tableIndexStatistic
    12. + *
    13. TYPE short => index type: + *
        + *
      • tableIndexStatistic - this identifies table statistics that are returned in conjuction with a table's index descriptions
      • + *
      • tableIndexClustered - this is a clustered index
      • + *
      • tableIndexHashed - this is a hashed index
      • + *
      • tableIndexOther - this is some other style of index
      • + *
      + *
    14. + *
    15. ORDINAL_POSITION short => column sequence number within index; zero when TYPE is tableIndexStatistic
    16. + *
    17. COLUMN_NAME String => column name; null when TYPE is tableIndexStatistic
    18. + *
    19. ASC_OR_DESC String => column sort sequence, "A" => ascending, "D" => descending, may be null if sort sequence is not supported; null when TYPE + * is tableIndexStatistic
    20. + *
    21. CARDINALITY int => When TYPE is tableIndexStatisic then this is the number of rows in the table; otherwise it is the number of unique values + * in the index.
    22. + *
    23. PAGES int => When TYPE is tableIndexStatisic then this is the number of pages used for the table, otherwise it is the number of pages used for + * the current index.
    24. + *
    25. FILTER_CONDITION String => Filter condition, if any. (may be null)
    26. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @param unique + * when true, return only indices for unique values; when false, + * return indices regardless of whether unique or not + * @param approximate + * when true, result is allowed to reflect approximate or out of + * data values; when false, results are requested to be accurate + * @return ResultSet each row is an index column description + * @throws SQLException + */ + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, NON_UNIQUE,"); + sqlBuf.append("TABLE_SCHEMA AS INDEX_QUALIFIER, INDEX_NAME," + tableIndexOther + " AS TYPE, SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME,"); + sqlBuf.append("COLLATION AS ASC_OR_DESC, CARDINALITY, NULL AS PAGES, NULL AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE "); + sqlBuf.append("TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ?"); + + if (unique) { + sqlBuf.append(" AND NON_UNIQUE=0 "); + } + + sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); + + java.sql.PreparedStatement pStmt = null; + + try { + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createIndexInfoFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of a table's primary key columns. They are ordered by + * COLUMN_NAME. + *

    + * Each column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. COLUMN_NAME String => column name
    8. + *
    9. KEY_SEQ short => sequence number within primary key
    10. + *
    11. PK_NAME String => primary key name (may be null)
    12. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schema + * a schema name pattern; "" retrieves those without a schema + * @param table + * a table name + * @return ResultSet each row is a primary key column description + * @throws SQLException + */ + @Override + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " + + "COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ, 'PRIMARY' AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS " + + "WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "TABLE_CAT", Types.CHAR, 255), + new Field("", "TABLE_SCHEM", Types.CHAR, 0), new Field("", "TABLE_NAME", Types.CHAR, 255), new Field("", "COLUMN_NAME", Types.CHAR, 32), + new Field("", "KEY_SEQ", Types.SMALLINT, 5), new Field("", "PK_NAME", Types.CHAR, 32) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Get a description of stored procedures available in a catalog. + *

    + * Only procedure descriptions matching the schema and procedure name criteria are returned. They are ordered by PROCEDURE_SCHEM, and PROCEDURE_NAME. + *

    + *

    + * Each procedure description has the the following columns: + *

      + *
    1. PROCEDURE_CAT String => procedure catalog (may be null)
    2. + *
    3. PROCEDURE_SCHEM String => procedure schema (may be null)
    4. + *
    5. PROCEDURE_NAME String => procedure name
    6. + *
    7. reserved for future use
    8. + *
    9. reserved for future use
    10. + *
    11. reserved for future use
    12. + *
    13. REMARKS String => explanatory comment on the procedure
    14. + *
    15. PROCEDURE_TYPE short => kind of procedure: + *
        + *
      • procedureResultUnknown - May return a result
      • + *
      • procedureNoResult - Does not return a result
      • + *
      • procedureReturnsResult - Returns a result
      • + *
      + *
    16. + *
    + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param procedureNamePattern + * a procedure name pattern + * @return ResultSet each row is a procedure description + * @throws SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + + if ((procedureNamePattern == null) || (procedureNamePattern.length() == 0)) { + if (this.conn.getNullNamePatternMatchesAll()) { + procedureNamePattern = "%"; + } else { + throw SQLError.createSQLException("Procedure name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + String db = null; + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + db = this.database; + } + } else { + db = catalog; + } + + String sql = "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, ROUTINE_NAME AS PROCEDURE_NAME, NULL AS RESERVED_1, " + + "NULL AS RESERVED_2, NULL AS RESERVED_3, ROUTINE_COMMENT AS REMARKS, CASE WHEN ROUTINE_TYPE = 'PROCEDURE' THEN " + procedureNoResult + + " WHEN ROUTINE_TYPE='FUNCTION' THEN " + procedureReturnsResult + " ELSE " + procedureResultUnknown + + " END AS PROCEDURE_TYPE, ROUTINE_NAME AS SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE " + getRoutineTypeConditionForGetProcedures() + + "ROUTINE_SCHEMA LIKE ? AND ROUTINE_NAME LIKE ? ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (db != null) { + pStmt.setString(1, db); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, procedureNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createFieldMetadataForGetProcedures()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Returns a condition to be injected in the query that returns metadata for procedures only. Overridden by + * subclasses when needed. When not empty must end with "AND ". + * + * @return String with the condition to be injected. + */ + protected String getRoutineTypeConditionForGetProcedures() { + return ""; + } + + /** + * Retrieves a description of the given catalog's stored procedure parameter + * and result columns. + * + *

    + * Only descriptions matching the schema, procedure and parameter name criteria are returned. They are ordered by PROCEDURE_SCHEM and PROCEDURE_NAME. Within + * this, the return value, if any, is first. Next are the parameter descriptions in call order. The column descriptions follow in column number order. + * + *

    + * Each row in the ResultSet is a parameter description or column description with the following fields: + *

      + *
    1. PROCEDURE_CAT String => procedure catalog (may be null) + *
    2. PROCEDURE_SCHEM String => procedure schema (may be null) + *
    3. PROCEDURE_NAME String => procedure name + *
    4. COLUMN_NAME String => column/parameter name + *
    5. COLUMN_TYPE Short => kind of column/parameter: + *
        + *
      • procedureColumnUnknown - nobody knows + *
      • procedureColumnIn - IN parameter + *
      • procedureColumnInOut - INOUT parameter + *
      • procedureColumnOut - OUT parameter + *
      • procedureColumnReturn - procedure return value + *
      • procedureColumnResult - result column in ResultSet + *
      + *
    6. DATA_TYPE int => SQL type from java.sql.Types + *
    7. TYPE_NAME String => SQL type name, for a UDT type the type name is fully qualified + *
    8. PRECISION int => precision + *
    9. LENGTH int => length in bytes of data + *
    10. SCALE short => scale + *
    11. RADIX short => radix + *
    12. NULLABLE short => can it contain NULL. + *
        + *
      • procedureNoNulls - does not allow NULL values + *
      • procedureNullable - allows NULL values + *
      • procedureNullableUnknown - nullability unknown + *
      + *
    13. REMARKS String => comment describing parameter/column + *
    + * + *

    + * Note: Some databases may not return the column descriptions for a procedure. Additional columns beyond REMARKS can be defined by the database. + * + * @param catalog + * a catalog name; must match the catalog name as it + * is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow + * the search + * @param schemaPattern + * a schema name pattern; must match the schema name + * as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to + * narrow + * the search + * @param procedureNamePattern + * a procedure name pattern; must match the + * procedure name as it is stored in the database + * @param columnNamePattern + * a column name pattern; must match the column name + * as it is stored in the database + * @return ResultSet - each row describes a stored procedure parameter or + * column + * @exception SQLException + * if a database access error occurs + * @see #getSearchStringEscape + */ + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { + if (!this.hasParametersView) { + return getProcedureColumnsNoISParametersView(catalog, schemaPattern, procedureNamePattern, columnNamePattern); + } + + if ((procedureNamePattern == null) || (procedureNamePattern.length() == 0)) { + if (this.conn.getNullNamePatternMatchesAll()) { + procedureNamePattern = "%"; + } else { + throw SQLError.createSQLException("Procedure name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + String db = null; + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + db = this.database; + } + } else { + db = catalog; + } + + // Here's what we get from MySQL ... + // SPECIFIC_CATALOG NULL + // SPECIFIC_SCHEMA db17 + // SPECIFIC_NAME p + // ORDINAL_POSITION 1 + // PARAMETER_MODE OUT + // PARAMETER_NAME a + // DATA_TYPE int + // CHARACTER_MAXIMUM_LENGTH NULL + // CHARACTER_OCTET_LENGTH NULL + // CHARACTER_SET_NAME NULL + // COLLATION_NAME NULL + // NUMERIC_PRECISION 10 + // NUMERIC_SCALE 0 + // DTD_IDENTIFIER int(11) + + StringBuilder sqlBuf = new StringBuilder("SELECT SPECIFIC_SCHEMA AS PROCEDURE_CAT, NULL AS `PROCEDURE_SCHEM`, " + + "SPECIFIC_NAME AS `PROCEDURE_NAME`, IFNULL(PARAMETER_NAME, '') AS `COLUMN_NAME`, CASE WHEN PARAMETER_MODE = 'IN' THEN " + procedureColumnIn + + " WHEN PARAMETER_MODE = 'OUT' THEN " + procedureColumnOut + " WHEN PARAMETER_MODE = 'INOUT' THEN " + procedureColumnInOut + + " WHEN ORDINAL_POSITION = 0 THEN " + procedureColumnReturn + " ELSE " + procedureColumnUnknown + " END AS `COLUMN_TYPE`, "); + + //DATA_TYPE + MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); + + sqlBuf.append(" AS `DATA_TYPE`, "); + + // TYPE_NAME + if (this.conn.getCapitalizeTypeNames()) { + sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', DATA_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') " + + "ELSE DATA_TYPE END) AS `TYPE_NAME`,"); + } else { + sqlBuf.append("CASE WHEN LOCATE('unsigned', DATA_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') " + + "ELSE DATA_TYPE END AS `TYPE_NAME`,"); + } + + // PRECISION int => precision + sqlBuf.append("NUMERIC_PRECISION AS `PRECISION`, "); + // LENGTH int => length in bytes of data + sqlBuf.append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19 " + + "WHEN LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS LENGTH, "); + + // SCALE short => scale + sqlBuf.append("NUMERIC_SCALE AS `SCALE`, "); + // RADIX short => radix + sqlBuf.append("10 AS RADIX,"); + sqlBuf.append(procedureNullable + " AS `NULLABLE`, NULL AS `REMARKS`, NULL AS `COLUMN_DEF`, NULL AS `SQL_DATA_TYPE`, " + + "NULL AS `SQL_DATETIME_SUB`, CHARACTER_OCTET_LENGTH AS `CHAR_OCTET_LENGTH`, ORDINAL_POSITION, 'YES' AS `IS_NULLABLE`, " + + "SPECIFIC_NAME FROM INFORMATION_SCHEMA.PARAMETERS WHERE " + getRoutineTypeConditionForGetProcedureColumns() + + "SPECIFIC_SCHEMA LIKE ? AND SPECIFIC_NAME LIKE ? AND (PARAMETER_NAME LIKE ? OR PARAMETER_NAME IS NULL) " + + "ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ROUTINE_TYPE, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + if (db != null) { + pStmt.setString(1, db); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, procedureNamePattern); + pStmt.setString(3, columnNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createProcedureColumnsFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Redirects to another implementation of #getProcedureColumns. Subclasses may need to override this method. + * + * @see getProcedureColumns + */ + protected ResultSet getProcedureColumnsNoISParametersView(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) + throws SQLException { + return super.getProcedureColumns(catalog, schemaPattern, procedureNamePattern, columnNamePattern); + } + + /** + * Returns a condition to be injected in the query that returns metadata for procedure columns only. Overridden by + * subclasses when needed. When not empty must end with "AND ". + * + * @return String with the condition to be injected. + */ + protected String getRoutineTypeConditionForGetProcedureColumns() { + return ""; + } + + /** + * Get a description of tables available in a catalog. + *

    + * Only table descriptions matching the catalog, schema, table name and type criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and + * TABLE_NAME. + *

    + *

    + * Each table description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null)
    2. + *
    3. TABLE_SCHEM String => table schema (may be null)
    4. + *
    5. TABLE_NAME String => table name
    6. + *
    7. TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
    8. + *
    9. REMARKS String => explanatory comment on the table
    10. + *
    + *

    + *

    + * Note: Some databases may not return information for all tables. + *

    + * + * @param catalog + * a catalog name; "" retrieves those without a catalog + * @param schemaPattern + * a schema name pattern; "" retrieves those without a schema + * @param tableNamePattern + * a table name pattern + * @param types + * a list of table types to include; null returns all types + * @return ResultSet each row is a table description + * @throws SQLException + * @see #getSearchStringEscape + */ + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + if (tableNamePattern == null) { + if (this.conn.getNullNamePatternMatchesAll()) { + tableNamePattern = "%"; + } else { + throw SQLError.createSQLException("Table name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + final String tableNamePat; + String tmpCat = ""; + + if ((catalog == null) || (catalog.length() == 0)) { + if (this.conn.getNullCatalogMeansCurrent()) { + tmpCat = this.database; + } + } else { + tmpCat = catalog; + } + + List parseList = StringUtils.splitDBdotName(tableNamePattern, tmpCat, this.quotedId, this.conn.isNoBackslashEscapesSet()); + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tableNamePat = parseList.get(1); + } else { + tableNamePat = tableNamePattern; + } + + java.sql.PreparedStatement pStmt = null; + + String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " + + "CASE WHEN TABLE_TYPE='BASE TABLE' THEN CASE WHEN TABLE_SCHEMA = 'mysql' OR TABLE_SCHEMA = 'performance_schema' THEN 'SYSTEM TABLE' " + + "ELSE 'TABLE' END WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, " + + "TABLE_COMMENT AS REMARKS, NULL AS TYPE_CAT, NULL AS TYPE_SCHEM, NULL AS TYPE_NAME, NULL AS SELF_REFERENCING_COL_NAME, " + + "NULL AS REF_GENERATION FROM INFORMATION_SCHEMA.TABLES WHERE "; + + final boolean operatingOnInformationSchema = "information_schema".equalsIgnoreCase(catalog); + if (catalog != null) { + if ((operatingOnInformationSchema) + || ((StringUtils.indexOfIgnoreCase(0, catalog, "%") == -1) && (StringUtils.indexOfIgnoreCase(0, catalog, "_") == -1))) { + sql += "TABLE_SCHEMA = ? "; + } else { + sql += "TABLE_SCHEMA LIKE ? "; + } + + } else { + sql += "TABLE_SCHEMA LIKE ? "; + } + + if (tableNamePat != null) { + if ((StringUtils.indexOfIgnoreCase(0, tableNamePat, "%") == -1) && (StringUtils.indexOfIgnoreCase(0, tableNamePat, "_") == -1)) { + sql += "AND TABLE_NAME = ? "; + } else { + sql += "AND TABLE_NAME LIKE ? "; + } + + } else { + sql += "AND TABLE_NAME LIKE ? "; + } + sql = sql + "HAVING TABLE_TYPE IN (?,?,?,?,?) "; + sql = sql + "ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"; + try { + pStmt = prepareMetaDataSafeStatement(sql); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, tableNamePat); + + // This overloading of IN (...) allows us to cache this prepared statement + if (types == null || types.length == 0) { + TableType[] tableTypes = TableType.values(); + for (int i = 0; i < 5; i++) { + pStmt.setString(3 + i, tableTypes[i].getName()); + } + } else { + for (int i = 0; i < 5; i++) { + pStmt.setNull(3 + i, Types.VARCHAR); + } + + int idx = 3; + for (int i = 0; i < types.length; i++) { + TableType tableType = TableType.getTableTypeEqualTo(types[i]); + if (tableType != TableType.UNKNOWN) { + pStmt.setString(idx++, tableType.getName()); + } + } + } + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createTablesFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + public boolean gethasParametersView() { + return this.hasParametersView; + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + catalog = this.database; + } + } + + if (table == null) { + throw SQLError.createSQLException("Table not specified.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + StringBuilder sqlBuf = new StringBuilder("SELECT NULL AS SCOPE, COLUMN_NAME, "); + + MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); + sqlBuf.append(" AS DATA_TYPE, "); + + sqlBuf.append("COLUMN_TYPE AS TYPE_NAME, "); + sqlBuf.append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 " + + "WHEN LCASE(DATA_TYPE)='datetime' THEN 19 WHEN LCASE(DATA_TYPE)='timestamp' THEN 19 " + + "WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + Integer.MAX_VALUE + " THEN " + + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS COLUMN_SIZE, "); + sqlBuf.append(MysqlIO.getMaxBuf() + " AS BUFFER_LENGTH,NUMERIC_SCALE AS DECIMAL_DIGITS, " + + Integer.toString(java.sql.DatabaseMetaData.versionColumnNotPseudo) + " AS PSEUDO_COLUMN FROM INFORMATION_SCHEMA.COLUMNS " + + "WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND EXTRA LIKE '%on update CURRENT_TIMESTAMP%'"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, table); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] { new Field("", "SCOPE", Types.SMALLINT, 5), + new Field("", "COLUMN_NAME", Types.CHAR, 32), new Field("", "DATA_TYPE", Types.INTEGER, 5), new Field("", "TYPE_NAME", Types.CHAR, 16), + new Field("", "COLUMN_SIZE", Types.INTEGER, 16), new Field("", "BUFFER_LENGTH", Types.INTEGER, 16), + new Field("", "DECIMAL_DIGITS", Types.SMALLINT, 16), new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + // + // JDBC-4.0 functions that aren't reliant on Java6 + // + + /** + * Retrieves a description of the given catalog's system or user + * function parameters and return type. + * + *

    + * Only descriptions matching the schema, function and parameter name criteria are returned. They are ordered by FUNCTION_CAT, + * FUNCTION_SCHEM, FUNCTION_NAME and SPECIFIC_ NAME. Within this, the return value, if any, is first. Next are the + * parameter descriptions in call order. The column descriptions follow in column number order. + * + *

    + * Each row in the ResultSet is a parameter description, column description or return type description with the following fields: + *

      + *
    1. FUNCTION_CAT String => function catalog (may be null) + *
    2. FUNCTION_SCHEM String => function schema (may be null) + *
    3. FUNCTION_NAME String => function name. This is the name used to invoke the function + *
    4. COLUMN_NAME String => column/parameter name + *
    5. COLUMN_TYPE Short => kind of column/parameter: + *
        + *
      • functionColumnUnknown - nobody knows + *
      • functionColumnIn - IN parameter + *
      • functionColumnInOut - INOUT parameter + *
      • functionColumnOut - OUT parameter + *
      • functionColumnReturn - function return value + *
      • functionColumnResult - Indicates that the parameter or column is a column in the ResultSet + *
      + *
    6. DATA_TYPE int => SQL type from java.sql.Types + *
    7. TYPE_NAME String => SQL type name, for a UDT type the type name is fully qualified + *
    8. PRECISION int => precision + *
    9. LENGTH int => length in bytes of data + *
    10. SCALE short => scale - null is returned for data types where SCALE is not applicable. + *
    11. RADIX short => radix + *
    12. NULLABLE short => can it contain NULL. + *
        + *
      • functionNoNulls - does not allow NULL values + *
      • functionNullable - allows NULL values + *
      • functionNullableUnknown - nullability unknown + *
      + *
    13. REMARKS String => comment describing column/parameter + *
    14. CHAR_OCTET_LENGTH int => the maximum length of binary and character based parameters or columns. For any other datatype the returned value is + * a NULL + *
    15. ORDINAL_POSITION int => the ordinal position, starting from 1, for the input and output parameters. A value of 0 is returned if this row + * describes the function's return value. For result set columns, it is the ordinal position of the column in the result set starting from 1. + *
    16. IS_NULLABLE String => ISO rules are used to determine the nullability for a parameter or column. + *
        + *
      • YES --- if the parameter or column can include NULLs + *
      • NO --- if the parameter or column cannot include NULLs + *
      • empty string --- if the nullability for the parameter or column is unknown + *
      + *
    17. SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user specified, or DBMS generated, name + * that may be different then the FUNCTION_NAME for example with overload functions + *
    + * + *

    + * The PRECISION column represents the specified column size for the given parameter or column. For numeric data, this is the maximum precision. For + * character data, this is the length in characters. For datetime datatypes, this is the length in characters of the String representation (assuming the + * maximum allowed precision of the fractional seconds component). For binary data, this is the length in bytes. For the ROWID datatype, this is the length + * in bytes. Null is returned for data types where the column size is not applicable. + * + * @param catalog + * a catalog name; must match the catalog name as it + * is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow + * the search + * @param schemaPattern + * a schema name pattern; must match the schema name + * as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to + * narrow + * the search + * @param functionNamePattern + * a procedure name pattern; must match the + * function name as it is stored in the database + * @param columnNamePattern + * a parameter name pattern; must match the + * parameter or column name as it is stored in the database + * @return ResultSet - each row describes a + * user function parameter, column or return type + * + * @exception SQLException + * if a database access error occurs + * @see #getSearchStringEscape + * @since 1.6 + */ + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + if (!this.hasParametersView) { + return super.getFunctionColumns(catalog, schemaPattern, functionNamePattern, columnNamePattern); + } + + if ((functionNamePattern == null) || (functionNamePattern.length() == 0)) { + if (this.conn.getNullNamePatternMatchesAll()) { + functionNamePattern = "%"; + } else { + throw SQLError.createSQLException("Procedure name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + String db = null; + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + db = this.database; + } + } else { + db = catalog; + } + + // FUNCTION_CAT + // FUNCTION_SCHEM + // FUNCTION_NAME + // COLUMN_NAME + // COLUMN_TYPE + StringBuilder sqlBuf = new StringBuilder("SELECT SPECIFIC_SCHEMA AS FUNCTION_CAT, NULL AS `FUNCTION_SCHEM`, SPECIFIC_NAME AS `FUNCTION_NAME`, "); + sqlBuf.append("IFNULL(PARAMETER_NAME, '') AS `COLUMN_NAME`, CASE WHEN PARAMETER_MODE = 'IN' THEN "); + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_COLUMN_IN)); + sqlBuf.append(" WHEN PARAMETER_MODE = 'OUT' THEN "); + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_COLUMN_OUT)); + sqlBuf.append(" WHEN PARAMETER_MODE = 'INOUT' THEN "); + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_COLUMN_INOUT)); + sqlBuf.append(" WHEN ORDINAL_POSITION = 0 THEN "); + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_COLUMN_RETURN)); + sqlBuf.append(" ELSE "); + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_COLUMN_UNKNOWN)); + sqlBuf.append(" END AS `COLUMN_TYPE`, "); + + //DATA_TYPE + MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE"); + + sqlBuf.append(" AS `DATA_TYPE`, "); + + // TYPE_NAME + if (this.conn.getCapitalizeTypeNames()) { + sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', DATA_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') " + + "ELSE DATA_TYPE END) AS `TYPE_NAME`,"); + } else { + sqlBuf.append("CASE WHEN LOCATE('unsigned', DATA_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') " + + "ELSE DATA_TYPE END AS `TYPE_NAME`,"); + } + + // PRECISION int => precision + sqlBuf.append("NUMERIC_PRECISION AS `PRECISION`, "); + // LENGTH int => length in bytes of data + sqlBuf.append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19 WHEN " + + "LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS LENGTH, "); + + // SCALE short => scale + sqlBuf.append("NUMERIC_SCALE AS `SCALE`, "); + // RADIX short => radix + sqlBuf.append("10 AS RADIX,"); + // NULLABLE + // REMARKS + // CHAR_OCTET_LENGTH * + // ORDINAL_POSITION * + // IS_NULLABLE * + // SPECIFIC_NAME * + sqlBuf.append(getJDBC4FunctionConstant(JDBC4FunctionConstant.FUNCTION_NULLABLE) + " AS `NULLABLE`, NULL AS `REMARKS`, " + + "CHARACTER_OCTET_LENGTH AS `CHAR_OCTET_LENGTH`, ORDINAL_POSITION, 'YES' AS `IS_NULLABLE`, SPECIFIC_NAME " + + "FROM INFORMATION_SCHEMA.PARAMETERS WHERE " + + "SPECIFIC_SCHEMA LIKE ? AND SPECIFIC_NAME LIKE ? AND (PARAMETER_NAME LIKE ? OR PARAMETER_NAME IS NULL) " + + "AND ROUTINE_TYPE='FUNCTION' ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + if (db != null) { + pStmt.setString(1, db); + } else { + pStmt.setString(1, "%"); + } + + pStmt.setString(2, functionNamePattern); + pStmt.setString(3, columnNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(createFunctionColumnsFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Getter to JDBC4 DatabaseMetaData.function* constants. + * This method must be overridden by JDBC4 subclasses. this implementation should never be called. + * + * @param constant + * the constant id from DatabaseMetaData fields to return. + * + * @return 0 + */ + protected int getJDBC4FunctionConstant(JDBC4FunctionConstant constant) { + return 0; + } + + /** + * Retrieves a description of the system and user functions available + * in the given catalog. + *

    + * Only system and user function descriptions matching the schema and function name criteria are returned. They are ordered by FUNCTION_CAT, + * FUNCTION_SCHEM, FUNCTION_NAME and SPECIFIC_ NAME. + * + *

    + * Each function description has the the following columns: + *

      + *
    1. FUNCTION_CAT String => function catalog (may be null) + *
    2. FUNCTION_SCHEM String => function schema (may be null) + *
    3. FUNCTION_NAME String => function name. This is the name used to invoke the function + *
    4. REMARKS String => explanatory comment on the function + *
    5. FUNCTION_TYPE short => kind of function: + *
        + *
      • functionResultUnknown - Cannot determine if a return value or table will be returned + *
      • functionNoTable- Does not return a table + *
      • functionReturnsTable - Returns a table + *
      + *
    6. SPECIFIC_NAME String => the name which uniquely identifies this function within its schema. This is a user specified, or DBMS generated, name + * that may be different then the FUNCTION_NAME for example with overload functions + *
    + *

    + * A user may not have permission to execute any of the functions that are returned by getFunctions + * + * @param catalog + * a catalog name; must match the catalog name as it + * is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow + * the search + * @param schemaPattern + * a schema name pattern; must match the schema name + * as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to + * narrow + * the search + * @param functionNamePattern + * a function name pattern; must match the + * function name as it is stored in the database + * @return ResultSet - each row is a function description + * @exception SQLException + * if a database access error occurs + * @see #getSearchStringEscape + * @since 1.6 + */ + @Override + public java.sql.ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + + if ((functionNamePattern == null) || (functionNamePattern.length() == 0)) { + if (this.conn.getNullNamePatternMatchesAll()) { + functionNamePattern = "%"; + } else { + throw SQLError.createSQLException("Function name pattern can not be NULL or empty.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + String db = null; + + if (catalog == null) { + if (this.conn.getNullCatalogMeansCurrent()) { + db = this.database; + } + } else { + db = catalog; + } + + String sql = "SELECT ROUTINE_SCHEMA AS FUNCTION_CAT, NULL AS FUNCTION_SCHEM, ROUTINE_NAME AS FUNCTION_NAME, ROUTINE_COMMENT AS REMARKS, " + + getJDBC4FunctionNoTableConstant() + " AS FUNCTION_TYPE, ROUTINE_NAME AS SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES " + + "WHERE ROUTINE_TYPE LIKE 'FUNCTION' AND ROUTINE_SCHEMA LIKE ? AND " + + "ROUTINE_NAME LIKE ? ORDER BY FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_NAME, SPECIFIC_NAME"; + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sql); + + pStmt.setString(1, db != null ? db : "%"); + pStmt.setString(2, functionNamePattern); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.jdbc.ResultSetInternalMethods) rs) + .redefineFieldsForDBMD(new Field[] { new Field("", "FUNCTION_CAT", Types.CHAR, 255), new Field("", "FUNCTION_SCHEM", Types.CHAR, 255), + new Field("", "FUNCTION_NAME", Types.CHAR, 255), new Field("", "REMARKS", Types.CHAR, 255), + new Field("", "FUNCTION_TYPE", Types.SMALLINT, 6), new Field("", "SPECIFIC_NAME", Types.CHAR, 255) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Getter to JDBC4 DatabaseMetaData.functionNoTable constant. + * This method must be overridden by JDBC4 subclasses. this implementation should never be called. + * + * @return 0 + */ + @Override + protected int getJDBC4FunctionNoTableConstant() { + return 0; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DocsConnectionPropsHelper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DocsConnectionPropsHelper.java new file mode 100644 index 0000000..76c0cb1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/DocsConnectionPropsHelper.java @@ -0,0 +1,33 @@ +/* + Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +public class DocsConnectionPropsHelper extends ConnectionPropertiesImpl { + + static final long serialVersionUID = -1580779062220390294L; + + public static void main(String[] args) throws Exception { + System.out.println(new DocsConnectionPropsHelper().exposeAsXml()); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Driver.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Driver.java new file mode 100644 index 0000000..fc065e1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Driver.java @@ -0,0 +1,64 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface + * + *

    + * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to + * connect to the target URL. + * + *

    + * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast + * quantities of supporting code. + * + *

    + * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a + * driver by doing Class.forName("foo.bah.Driver") + */ +public class Driver extends NonRegisteringDriver implements java.sql.Driver { + // + // Register ourselves with the DriverManager + // + static { + try { + java.sql.DriverManager.registerDriver(new Driver()); + } catch (SQLException E) { + throw new RuntimeException("Can't register driver!"); + } + } + + /** + * Construct a new driver and register it with DriverManager + * + * @throws SQLException + * if a database error occurs. + */ + public Driver() throws SQLException { + // Required for Class.forName().newInstance() + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessor.java new file mode 100644 index 0000000..3df8ac0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessor.java @@ -0,0 +1,621 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** + * EscapeProcessor performs all escape code processing as outlined in the JDBC spec by JavaSoft. + */ +class EscapeProcessor { + private static Map JDBC_CONVERT_TO_MYSQL_TYPE_MAP; + + private static Map JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP; + + static { + Map tempMap = new HashMap(); + + tempMap.put("BIGINT", "0 + ?"); + tempMap.put("BINARY", "BINARY"); + tempMap.put("BIT", "0 + ?"); + tempMap.put("CHAR", "CHAR"); + tempMap.put("DATE", "DATE"); + tempMap.put("DECIMAL", "0.0 + ?"); + tempMap.put("DOUBLE", "0.0 + ?"); + tempMap.put("FLOAT", "0.0 + ?"); + tempMap.put("INTEGER", "0 + ?"); + tempMap.put("LONGVARBINARY", "BINARY"); + tempMap.put("LONGVARCHAR", "CONCAT(?)"); + tempMap.put("REAL", "0.0 + ?"); + tempMap.put("SMALLINT", "CONCAT(?)"); + tempMap.put("TIME", "TIME"); + tempMap.put("TIMESTAMP", "DATETIME"); + tempMap.put("TINYINT", "CONCAT(?)"); + tempMap.put("VARBINARY", "BINARY"); + tempMap.put("VARCHAR", "CONCAT(?)"); + + JDBC_CONVERT_TO_MYSQL_TYPE_MAP = Collections.unmodifiableMap(tempMap); + + tempMap = new HashMap(JDBC_CONVERT_TO_MYSQL_TYPE_MAP); + + tempMap.put("BINARY", "CONCAT(?)"); + tempMap.put("CHAR", "CONCAT(?)"); + tempMap.remove("DATE"); + tempMap.put("LONGVARBINARY", "CONCAT(?)"); + tempMap.remove("TIME"); + tempMap.remove("TIMESTAMP"); + tempMap.put("VARBINARY", "CONCAT(?)"); + + JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP = Collections.unmodifiableMap(tempMap); + + } + + /** + * Escape process one string + * + * @param sql + * the SQL to escape process. + * + * @return the SQL after it has been escape processed. + * + * @throws java.sql.SQLException + * @throws SQLException + */ + public static final Object escapeSQL(String sql, boolean serverSupportsConvertFn, MySQLConnection conn) throws java.sql.SQLException { + boolean replaceEscapeSequence = false; + String escapeSequence = null; + + if (sql == null) { + return null; + } + + /* + * Short circuit this code if we don't have a matching pair of "{}". - Suggested by Ryan Gustafason + */ + int beginBrace = sql.indexOf('{'); + int nextEndBrace = (beginBrace == -1) ? (-1) : sql.indexOf('}', beginBrace); + + if (nextEndBrace == -1) { + return sql; + } + + StringBuilder newSql = new StringBuilder(); + + EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql); + + byte usesVariables = StatementImpl.USES_VARIABLES_FALSE; + boolean callingStoredFunction = false; + + while (escapeTokenizer.hasMoreTokens()) { + String token = escapeTokenizer.nextToken(); + + if (token.length() != 0) { + if (token.charAt(0) == '{') { // It's an escape code + + if (!token.endsWith("}")) { + throw SQLError.createSQLException("Not a valid escape sequence: " + token, conn.getExceptionInterceptor()); + } + + if (token.length() > 2) { + int nestedBrace = token.indexOf('{', 2); + + if (nestedBrace != -1) { + StringBuilder buf = new StringBuilder(token.substring(0, 1)); + + Object remainingResults = escapeSQL(token.substring(1, token.length() - 1), serverSupportsConvertFn, conn); + + String remaining = null; + + if (remainingResults instanceof String) { + remaining = (String) remainingResults; + } else { + remaining = ((EscapeProcessorResult) remainingResults).escapedSql; + + if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) { + usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables; + } + } + + buf.append(remaining); + + buf.append('}'); + + token = buf.toString(); + } + } + + // nested escape code + // Compare to tokens with _no_ whitespace + String collapsedToken = removeWhitespace(token); + + /* + * Process the escape code + */ + if (StringUtils.startsWithIgnoreCase(collapsedToken, "{escape")) { + try { + StringTokenizer st = new StringTokenizer(token, " '"); + st.nextToken(); // eat the "escape" token + escapeSequence = st.nextToken(); + + if (escapeSequence.length() < 3) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + escapeSequence = escapeSequence.substring(1, escapeSequence.length() - 1); + replaceEscapeSequence = true; + } + } catch (java.util.NoSuchElementException e) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{fn")) { + int startPos = token.toLowerCase().indexOf("fn ") + 3; + int endPos = token.length() - 1; // no } + + String fnToken = token.substring(startPos, endPos); + + // We need to handle 'convert' by ourselves + + if (StringUtils.startsWithIgnoreCaseAndWs(fnToken, "convert")) { + newSql.append(processConvertToken(fnToken, serverSupportsConvertFn, conn)); + } else { + // just pass functions right to the DB + newSql.append(fnToken); + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{d")) { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, " -"); + String year4 = st.nextToken(); + String month2 = st.nextToken(); + String day2 = st.nextToken(); + String dateString = "'" + year4 + "-" + month2 + "-" + day2 + "'"; + newSql.append(dateString); + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException("Syntax error for DATE escape sequence '" + argument + "'", "42000", + conn.getExceptionInterceptor()); + } + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{ts")) { + processTimestampToken(conn, newSql, token); + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{t")) { + processTimeToken(conn, newSql, token); + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{call") || StringUtils.startsWithIgnoreCase(collapsedToken, "{?=call")) { + + int startPos = StringUtils.indexOfIgnoreCase(token, "CALL") + 5; + int endPos = token.length() - 1; + + if (StringUtils.startsWithIgnoreCase(collapsedToken, "{?=call")) { + callingStoredFunction = true; + newSql.append("SELECT "); + newSql.append(token.substring(startPos, endPos)); + } else { + callingStoredFunction = false; + newSql.append("CALL "); + newSql.append(token.substring(startPos, endPos)); + } + + for (int i = endPos - 1; i >= startPos; i--) { + char c = token.charAt(i); + + if (Character.isWhitespace(c)) { + continue; + } + + if (c != ')') { + newSql.append("()"); // handle no-parenthesis no-arg call not supported by MySQL parser + } + + break; + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{oj")) { + // MySQL already handles this escape sequence because of ODBC. Cool. + newSql.append(token); + } else { + // not an escape code, just part of the query + newSql.append(token); + } + } else { + newSql.append(token); // it's just part of the query + } + } + } + + String escapedSql = newSql.toString(); + + // + // FIXME: Let MySQL do this, however requires lightweight parsing of statement + // + if (replaceEscapeSequence) { + String currentSql = escapedSql; + + while (currentSql.indexOf(escapeSequence) != -1) { + int escapePos = currentSql.indexOf(escapeSequence); + String lhs = currentSql.substring(0, escapePos); + String rhs = currentSql.substring(escapePos + 1, currentSql.length()); + currentSql = lhs + "\\" + rhs; + } + + escapedSql = currentSql; + } + + EscapeProcessorResult epr = new EscapeProcessorResult(); + epr.escapedSql = escapedSql; + epr.callingStoredFunction = callingStoredFunction; + + if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) { + if (escapeTokenizer.sawVariableUse()) { + epr.usesVariables = StatementImpl.USES_VARIABLES_TRUE; + } else { + epr.usesVariables = StatementImpl.USES_VARIABLES_FALSE; + } + } + + return epr; + } + + private static void processTimeToken(MySQLConnection conn, StringBuilder newSql, String token) throws SQLException { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, " :."); + String hour = st.nextToken(); + String minute = st.nextToken(); + String second = st.nextToken(); + + boolean serverSupportsFractionalSecond = false; + String fractionalSecond = ""; + + if (st.hasMoreTokens()) { + if (conn.versionMeetsMinimum(5, 6, 4)) { + serverSupportsFractionalSecond = true; + fractionalSecond = "." + st.nextToken(); + } + } + + if (!conn.getUseTimezone() || !conn.getUseLegacyDatetimeCode()) { + newSql.append("'"); + newSql.append(hour); + newSql.append(":"); + newSql.append(minute); + newSql.append(":"); + newSql.append(second); + newSql.append(fractionalSecond); + newSql.append("'"); + } else { + Calendar sessionCalendar = conn.getCalendarInstanceForSessionOrNew(); + + try { + int hourInt = Integer.parseInt(hour); + int minuteInt = Integer.parseInt(minute); + int secondInt = Integer.parseInt(second); + + Time toBeAdjusted = TimeUtil.fastTimeCreate(sessionCalendar, hourInt, minuteInt, secondInt, conn.getExceptionInterceptor()); + + Time inServerTimezone = TimeUtil.changeTimezone(conn, sessionCalendar, null, toBeAdjusted, sessionCalendar.getTimeZone(), + conn.getServerTimezoneTZ(), false); + + newSql.append("'"); + newSql.append(inServerTimezone.toString()); + + if (serverSupportsFractionalSecond) { + newSql.append(fractionalSecond); + } + + newSql.append("'"); + + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" + token + "'.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + conn.getExceptionInterceptor()); + } + } + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException("Syntax error for escape sequence '" + argument + "'", "42000", conn.getExceptionInterceptor()); + } + } + } + + private static void processTimestampToken(MySQLConnection conn, StringBuilder newSql, String token) throws SQLException { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + if (!conn.getUseLegacyDatetimeCode()) { + Timestamp ts = Timestamp.valueOf(argument); + SimpleDateFormat tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); + + tsdf.setTimeZone(conn.getServerTimezoneTZ()); + + newSql.append(tsdf.format(ts)); + + if (ts.getNanos() > 0 && conn.versionMeetsMinimum(5, 6, 4)) { + newSql.append('.'); + newSql.append(TimeUtil.formatNanos(ts.getNanos(), true, true)); + } + + newSql.append('\''); + + } else { + StringTokenizer st = new StringTokenizer(argument, " .-:"); + try { + String year4 = st.nextToken(); + String month2 = st.nextToken(); + String day2 = st.nextToken(); + String hour = st.nextToken(); + String minute = st.nextToken(); + String second = st.nextToken(); + + boolean serverSupportsFractionalSecond = false; + String fractionalSecond = ""; + if (st.hasMoreTokens()) { + if (conn.versionMeetsMinimum(5, 6, 4)) { + serverSupportsFractionalSecond = true; + fractionalSecond = "." + st.nextToken(); + } + } + + /* + * Use the full format because number format will not work for "between" clauses. + * + * Ref. Mysql Docs + * + * You can specify DATETIME, DATE and TIMESTAMP values using any of a common set of formats: + * + * As a string in either 'YYYY-MM-DD HH:MM:SS' or 'YY-MM-DD HH:MM:SS' format. + * + * Thanks to Craig Longman for pointing out this bug + */ + + if (!conn.getUseTimezone() && !conn.getUseJDBCCompliantTimezoneShift()) { + newSql.append("'").append(year4).append("-").append(month2).append("-").append(day2).append(" ").append(hour).append(":") + .append(minute).append(":").append(second).append(fractionalSecond).append("'"); + } else { + Calendar sessionCalendar = conn.getCalendarInstanceForSessionOrNew(); + + try { + int year4Int = Integer.parseInt(year4); + int month2Int = Integer.parseInt(month2); + int day2Int = Integer.parseInt(day2); + int hourInt = Integer.parseInt(hour); + int minuteInt = Integer.parseInt(minute); + int secondInt = Integer.parseInt(second); + + boolean useGmtMillis = conn.getUseGmtMillisForDatetimes(); + + Timestamp toBeAdjusted = TimeUtil.fastTimestampCreate(useGmtMillis, + useGmtMillis ? Calendar.getInstance(TimeZone.getTimeZone("GMT")) : null, sessionCalendar, year4Int, month2Int, day2Int, + hourInt, minuteInt, secondInt, 0); + + Timestamp inServerTimezone = TimeUtil.changeTimezone(conn, sessionCalendar, null, toBeAdjusted, sessionCalendar.getTimeZone(), + conn.getServerTimezoneTZ(), false); + + newSql.append("'"); + + String timezoneLiteral = inServerTimezone.toString(); + + int indexOfDot = timezoneLiteral.indexOf("."); + + if (indexOfDot != -1) { + timezoneLiteral = timezoneLiteral.substring(0, indexOfDot); + } + + newSql.append(timezoneLiteral); + + if (serverSupportsFractionalSecond) { + newSql.append(fractionalSecond); + } + newSql.append("'"); + + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" + token + "'.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, conn.getExceptionInterceptor()); + } + } + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException("Syntax error for TIMESTAMP escape sequence '" + argument + "'", "42000", + conn.getExceptionInterceptor()); + } + } + } catch (IllegalArgumentException illegalArgumentException) { + SQLException sqlEx = SQLError.createSQLException("Syntax error for TIMESTAMP escape sequence '" + argument + "'", "42000", + conn.getExceptionInterceptor()); + sqlEx.initCause(illegalArgumentException); + + throw sqlEx; + } + } + } + + /** + * Re-writes {fn convert (expr, type)} as cast(expr AS type) + * + * @param functionToken + * @throws SQLException + */ + private static String processConvertToken(String functionToken, boolean serverSupportsConvertFn, MySQLConnection conn) throws SQLException { + // The JDBC spec requires these types: + // + // BIGINT + // BINARY + // BIT + // CHAR + // DATE + // DECIMAL + // DOUBLE + // FLOAT + // INTEGER + // LONGVARBINARY + // LONGVARCHAR + // REAL + // SMALLINT + // TIME + // TIMESTAMP + // TINYINT + // VARBINARY + // VARCHAR + + // MySQL supports these types: + // + // BINARY + // CHAR + // DATE + // DATETIME + // SIGNED (integer) + // UNSIGNED (integer) + // TIME + + int firstIndexOfParen = functionToken.indexOf("("); + + if (firstIndexOfParen == -1) { + throw SQLError.createSQLException( + "Syntax error while processing {fn convert (... , ...)} token, missing opening parenthesis in token '" + functionToken + "'.", + SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor()); + } + + int indexOfComma = functionToken.lastIndexOf(","); + + if (indexOfComma == -1) { + throw SQLError.createSQLException("Syntax error while processing {fn convert (... , ...)} token, missing comma in token '" + functionToken + "'.", + SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor()); + } + + int indexOfCloseParen = functionToken.indexOf(')', indexOfComma); + + if (indexOfCloseParen == -1) { + throw SQLError.createSQLException( + "Syntax error while processing {fn convert (... , ...)} token, missing closing parenthesis in token '" + functionToken + "'.", + SQLError.SQL_STATE_SYNTAX_ERROR, conn.getExceptionInterceptor()); + + } + + String expression = functionToken.substring(firstIndexOfParen + 1, indexOfComma); + String type = functionToken.substring(indexOfComma + 1, indexOfCloseParen); + + String newType = null; + + String trimmedType = type.trim(); + + if (StringUtils.startsWithIgnoreCase(trimmedType, "SQL_")) { + trimmedType = trimmedType.substring(4, trimmedType.length()); + } + + if (serverSupportsConvertFn) { + newType = JDBC_CONVERT_TO_MYSQL_TYPE_MAP.get(trimmedType.toUpperCase(Locale.ENGLISH)); + } else { + newType = JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP.get(trimmedType.toUpperCase(Locale.ENGLISH)); + + // We need a 'special' check here to give a better error message. If we're in this block, the version of MySQL we're connected to doesn't support + // CAST/CONVERT, so we can't re-write some data type conversions (date,time,timestamp, datetime) + + if (newType == null) { + throw SQLError.createSQLException( + "Can't find conversion re-write for type '" + type + "' that is applicable for this server version while processing escape tokens.", + SQLError.SQL_STATE_GENERAL_ERROR, conn.getExceptionInterceptor()); + } + } + + if (newType == null) { + throw SQLError.createSQLException("Unsupported conversion type '" + type.trim() + "' found while processing escape token.", + SQLError.SQL_STATE_GENERAL_ERROR, conn.getExceptionInterceptor()); + } + + int replaceIndex = newType.indexOf("?"); + + if (replaceIndex != -1) { + StringBuilder convertRewrite = new StringBuilder(newType.substring(0, replaceIndex)); + convertRewrite.append(expression); + convertRewrite.append(newType.substring(replaceIndex + 1, newType.length())); + + return convertRewrite.toString(); + } + + StringBuilder castRewrite = new StringBuilder("CAST("); + castRewrite.append(expression); + castRewrite.append(" AS "); + castRewrite.append(newType); + castRewrite.append(")"); + + return castRewrite.toString(); + + } + + /** + * Removes all whitespace from the given String. We use this to make escape + * token comparison white-space ignorant. + * + * @param toCollapse + * the string to remove the whitespace from + * + * @return a string with _no_ whitespace. + */ + private static String removeWhitespace(String toCollapse) { + if (toCollapse == null) { + return null; + } + + int length = toCollapse.length(); + + StringBuilder collapsed = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + char c = toCollapse.charAt(i); + + if (!Character.isWhitespace(c)) { + collapsed.append(c); + } + } + + return collapsed.toString(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessorResult.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessorResult.java new file mode 100644 index 0000000..c0f39d4 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeProcessorResult.java @@ -0,0 +1,36 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Wraps output from EscapeProcessor, to help prevent multiple passes over the query string, to detect characters such as '@' (defining/using a variable), + * which are used further up the call stack to handle failover. + */ +class EscapeProcessorResult { + boolean callingStoredFunction = false; + + String escapedSql; + + byte usesVariables = StatementImpl.USES_VARIABLES_FALSE; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeTokenizer.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeTokenizer.java new file mode 100644 index 0000000..7db35bb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/EscapeTokenizer.java @@ -0,0 +1,184 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * EscapeTokenizer breaks up an SQL statement into SQL and escape code parts. + */ +public class EscapeTokenizer { + private static final char CHR_ESCAPE = '\\'; + private static final char CHR_SGL_QUOTE = '\''; + private static final char CHR_DBL_QUOTE = '"'; + private static final char CHR_LF = '\n'; + private static final char CHR_CR = '\r'; + private static final char CHR_COMMENT = '-'; + private static final char CHR_BEGIN_TOKEN = '{'; + private static final char CHR_END_TOKEN = '}'; + private static final char CHR_VARIABLE = '@'; + + private String source = null; + private int sourceLength = 0; + private int pos = 0; + + private boolean emittingEscapeCode = false; + private boolean sawVariableUse = false; + private int bracesLevel = 0; + private boolean inQuotes = false; + private char quoteChar = 0; + + /** + * Creates a new EscapeTokenizer object. + * + * @param source + * the string to tokenize + */ + public EscapeTokenizer(String source) { + this.source = source; + this.sourceLength = source.length(); + this.pos = 0; + } + + /** + * Does this tokenizer have more tokens available? + * + * @return if this tokenizer has more tokens available + */ + public synchronized boolean hasMoreTokens() { + return (this.pos < this.sourceLength); + } + + /** + * Returns the next token + * + * @return the next token. + */ + public synchronized String nextToken() { + StringBuilder tokenBuf = new StringBuilder(); + boolean backslashEscape = false; + + if (this.emittingEscapeCode) { + // Previous token ended at the beginning of an escape code, so this token must start with '{' + tokenBuf.append("{"); + this.emittingEscapeCode = false; + } + + for (; this.pos < this.sourceLength; this.pos++) { + char c = this.source.charAt(this.pos); + + // process escape char: (\) + if (c == CHR_ESCAPE) { + tokenBuf.append(c); + backslashEscape = !backslashEscape; + continue; + } + + // process quotes: ('|") + if ((c == CHR_SGL_QUOTE || c == CHR_DBL_QUOTE) && !backslashEscape) { + tokenBuf.append(c); + if (this.inQuotes) { + if (c == this.quoteChar) { + // look ahead for doubled quote + if ((this.pos + 1 < this.sourceLength) && (this.source.charAt(this.pos + 1) == this.quoteChar)) { + tokenBuf.append(c); + this.pos++; // consume following char '\'' or '"' + } else { + this.inQuotes = false; + } + } + } else { + this.inQuotes = true; + this.quoteChar = c; + } + continue; + } + + // process new line: (\n|\r) + if ((c == CHR_LF) || (c == CHR_CR)) { + tokenBuf.append(c); + backslashEscape = false; + continue; + } + + if (!this.inQuotes && !backslashEscape) { + // process comments: (--) + if (c == CHR_COMMENT) { + tokenBuf.append(c); + // look ahead for double hyphen + if ((this.pos + 1 < this.sourceLength) && (this.source.charAt(this.pos + 1) == CHR_COMMENT)) { + // consume following chars until new line or end of string + while (++this.pos < this.sourceLength && c != CHR_LF && c != CHR_CR) { + c = this.source.charAt(this.pos); + tokenBuf.append(c); + } + this.pos--; + } + continue; + } + + // process begin token: ({) + if (c == CHR_BEGIN_TOKEN) { + this.bracesLevel++; + if (this.bracesLevel == 1) { + this.emittingEscapeCode = true; + this.pos++; // consume char '{' before returning + return tokenBuf.toString(); + } + tokenBuf.append(c); + continue; + } + + // process end token: (}) + if (c == CHR_END_TOKEN) { + tokenBuf.append(c); + this.bracesLevel--; + if (this.bracesLevel == 0) { + this.pos++; // consume char '}' before returning + return tokenBuf.toString(); + } + continue; + } + + // detect variable usage: (@) + if (c == CHR_VARIABLE) { + this.sawVariableUse = true; + } + } + + tokenBuf.append(c); + backslashEscape = false; + } + + return tokenBuf.toString(); + } + + /** + * Returns true if a variable reference was found. Note that this information isn't accurate until finishing to + * process all tokens from source String. It also can't be used as per token basis. + * + * @return true if a variable reference was found. + */ + boolean sawVariableUse() { + return this.sawVariableUse; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExceptionInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExceptionInterceptor.java new file mode 100644 index 0000000..ab10c3f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExceptionInterceptor.java @@ -0,0 +1,30 @@ +/* + Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface ExceptionInterceptor extends Extension { + public abstract SQLException interceptException(SQLException sqlEx, Connection conn); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExportControlled.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExportControlled.java new file mode 100644 index 0000000..2468dc1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ExportControlled.java @@ -0,0 +1,382 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.security.KeyFactory; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import javax.crypto.Cipher; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import com.mysql.jdbc.util.Base64Decoder; + +/** + * Holds functionality that falls under export-control regulations. + */ +public class ExportControlled { + private static final String SQL_STATE_BAD_SSL_PARAMS = "08000"; + + protected static boolean enabled() { + // we may wish to un-static-ify this class this static method call may be removed entirely by the compiler + return true; + } + + /** + * Converts the socket being used in the given MysqlIO to an SSLSocket by + * performing the SSL/TLS handshake. + * + * @param mysqlIO + * the MysqlIO instance containing the socket to convert to an + * SSLSocket. + * + * @throws CommunicationsException + * if the handshake fails, or if this distribution of + * Connector/J doesn't contain the SSL crypto hooks needed to + * perform the handshake. + */ + protected static void transformSocketToSSLSocket(MysqlIO mysqlIO) throws SQLException { + SocketFactory sslFact = new StandardSSLSocketFactory(getSSLSocketFactoryDefaultOrConfigured(mysqlIO), mysqlIO.socketFactory, mysqlIO.mysqlConnection); + + try { + mysqlIO.mysqlConnection = sslFact.connect(mysqlIO.host, mysqlIO.port, null); + + List allowedProtocols = new ArrayList(); + List supportedProtocols = Arrays.asList(((SSLSocket) mysqlIO.mysqlConnection).getSupportedProtocols()); + for (String protocol : (mysqlIO.versionMeetsMinimum(5, 6, 0) && Util.isEnterpriseEdition(mysqlIO.getServerVersion()) + ? new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } : new String[] { "TLSv1.1", "TLSv1" })) { + if (supportedProtocols.contains(protocol)) { + allowedProtocols.add(protocol); + } + } + ((SSLSocket) mysqlIO.mysqlConnection).setEnabledProtocols(allowedProtocols.toArray(new String[0])); + + // check allowed cipher suites + String enabledSSLCipherSuites = mysqlIO.connection.getEnabledSSLCipherSuites(); + boolean overrideCiphers = enabledSSLCipherSuites != null && enabledSSLCipherSuites.length() > 0; + + List allowedCiphers = null; + if (overrideCiphers) { + // If "enabledSSLCipherSuites" is set we just check that JVM allows provided values, + // we don't disable DH algorithm, that allows c/J to deal with custom server builds with different security restrictions + allowedCiphers = new ArrayList(); + List availableCiphers = Arrays.asList(((SSLSocket) mysqlIO.mysqlConnection).getEnabledCipherSuites()); + for (String cipher : enabledSSLCipherSuites.split("\\s*,\\s*")) { + if (availableCiphers.contains(cipher)) { + allowedCiphers.add(cipher); + } + } + + } else { + // If we don't override ciphers, then we check for known restrictions + boolean disableDHAlgorithm = false; + if (mysqlIO.versionMeetsMinimum(5, 5, 45) && !mysqlIO.versionMeetsMinimum(5, 6, 0) + || mysqlIO.versionMeetsMinimum(5, 6, 26) && !mysqlIO.versionMeetsMinimum(5, 7, 0) || mysqlIO.versionMeetsMinimum(5, 7, 6)) { + // Workaround for JVM bug http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6521495 + // Starting from 5.5.45, 5.6.26 and 5.7.6 server the key length used for creating Diffie-Hellman keys has been + // increased from 512 to 2048 bits, while JVMs affected by this bug allow only range from 512 to 1024 (inclusive). + // Bug is fixed in Java 8. + if (Util.getJVMVersion() < 8) { + disableDHAlgorithm = true; + } + } else if (Util.getJVMVersion() >= 8) { // TODO check later for Java 9 behavior + // Java 8 default java.security contains jdk.tls.disabledAlgorithms=DH keySize < 768 + // That causes handshake failures with older MySQL servers, eg 5.6.11. Thus we have to disable DH for them when running on Java 8+ + disableDHAlgorithm = true; + } + + if (disableDHAlgorithm) { + allowedCiphers = new ArrayList(); + for (String cipher : ((SSLSocket) mysqlIO.mysqlConnection).getEnabledCipherSuites()) { + if (!(disableDHAlgorithm && (cipher.indexOf("_DHE_") > -1 || cipher.indexOf("_DH_") > -1))) { + allowedCiphers.add(cipher); + } + } + } + } + + // if some ciphers were filtered into allowedCiphers + if (allowedCiphers != null) { + ((SSLSocket) mysqlIO.mysqlConnection).setEnabledCipherSuites(allowedCiphers.toArray(new String[0])); + } + + ((SSLSocket) mysqlIO.mysqlConnection).startHandshake(); + + if (mysqlIO.connection.getUseUnbufferedInput()) { + mysqlIO.mysqlInput = mysqlIO.mysqlConnection.getInputStream(); + } else { + mysqlIO.mysqlInput = new BufferedInputStream(mysqlIO.mysqlConnection.getInputStream(), 16384); + } + + mysqlIO.mysqlOutput = new BufferedOutputStream(mysqlIO.mysqlConnection.getOutputStream(), 16384); + + mysqlIO.mysqlOutput.flush(); + + mysqlIO.socketFactory = sslFact; + + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(mysqlIO.connection, mysqlIO.getLastPacketSentTimeMs(), mysqlIO.getLastPacketReceivedTimeMs(), ioEx, + mysqlIO.getExceptionInterceptor()); + } + } + + /** + * Implementation of internal socket factory to wrap the SSL socket. + */ + public static class StandardSSLSocketFactory implements SocketFactory, SocketMetadata { + private SSLSocket rawSocket = null; + private final SSLSocketFactory sslFact; + private final SocketFactory existingSocketFactory; + private final Socket existingSocket; + + public StandardSSLSocketFactory(SSLSocketFactory sslFact, SocketFactory existingSocketFactory, Socket existingSocket) { + this.sslFact = sslFact; + this.existingSocketFactory = existingSocketFactory; + this.existingSocket = existingSocket; + } + + public Socket afterHandshake() throws SocketException, IOException { + this.existingSocketFactory.afterHandshake(); + return this.rawSocket; + } + + public Socket beforeHandshake() throws SocketException, IOException { + return this.rawSocket; + } + + public Socket connect(String host, int portNumber, Properties props) throws SocketException, IOException { + this.rawSocket = (SSLSocket) this.sslFact.createSocket(this.existingSocket, host, portNumber, true); + return this.rawSocket; + } + + public boolean isLocallyConnected(ConnectionImpl conn) throws SQLException { + return SocketMetadata.Helper.isLocallyConnected(conn); + } + + } + + private ExportControlled() { /* prevent instantiation */ + } + + private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(MysqlIO mysqlIO) throws SQLException { + String clientCertificateKeyStoreUrl = mysqlIO.connection.getClientCertificateKeyStoreUrl(); + String trustCertificateKeyStoreUrl = mysqlIO.connection.getTrustCertificateKeyStoreUrl(); + String clientCertificateKeyStoreType = mysqlIO.connection.getClientCertificateKeyStoreType(); + String clientCertificateKeyStorePassword = mysqlIO.connection.getClientCertificateKeyStorePassword(); + String trustCertificateKeyStoreType = mysqlIO.connection.getTrustCertificateKeyStoreType(); + String trustCertificateKeyStorePassword = mysqlIO.connection.getTrustCertificateKeyStorePassword(); + + if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) && StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) { + if (mysqlIO.connection.getVerifyServerCertificate()) { + return (SSLSocketFactory) SSLSocketFactory.getDefault(); + } + } + + TrustManagerFactory tmf = null; + KeyManagerFactory kmf = null; + + try { + tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + } catch (NoSuchAlgorithmException nsae) { + throw SQLError.createSQLException( + "Default algorithm definitions for TrustManager and/or KeyManager are invalid. Check java security properties file.", + SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); + } + + if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) { + InputStream ksIS = null; + try { + if (!StringUtils.isNullOrEmpty(clientCertificateKeyStoreType)) { + KeyStore clientKeyStore = KeyStore.getInstance(clientCertificateKeyStoreType); + URL ksURL = new URL(clientCertificateKeyStoreUrl); + char[] password = (clientCertificateKeyStorePassword == null) ? new char[0] : clientCertificateKeyStorePassword.toCharArray(); + ksIS = ksURL.openStream(); + clientKeyStore.load(ksIS, password); + kmf.init(clientKeyStore, password); + } + } catch (UnrecoverableKeyException uke) { + throw SQLError.createSQLException("Could not recover keys from client keystore. Check password?", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (NoSuchAlgorithmException nsae) { + throw SQLError.createSQLException("Unsupported keystore algorithm [" + nsae.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (KeyStoreException kse) { + throw SQLError.createSQLException("Could not create KeyStore instance [" + kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (CertificateException nsae) { + throw SQLError.createSQLException("Could not load client" + clientCertificateKeyStoreType + " keystore from " + clientCertificateKeyStoreUrl, + mysqlIO.getExceptionInterceptor()); + } catch (MalformedURLException mue) { + throw SQLError.createSQLException(clientCertificateKeyStoreUrl + " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (IOException ioe) { + SQLException sqlEx = SQLError.createSQLException("Cannot open " + clientCertificateKeyStoreUrl + " [" + ioe.getMessage() + "]", + SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); + sqlEx.initCause(ioe); + + throw sqlEx; + } finally { + if (ksIS != null) { + try { + ksIS.close(); + } catch (IOException e) { + // can't close input stream, but keystore can be properly initialized so we shouldn't throw this exception + } + } + } + } + + if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) { + + InputStream ksIS = null; + try { + if (!StringUtils.isNullOrEmpty(trustCertificateKeyStoreType)) { + KeyStore trustKeyStore = KeyStore.getInstance(trustCertificateKeyStoreType); + URL ksURL = new URL(trustCertificateKeyStoreUrl); + + char[] password = (trustCertificateKeyStorePassword == null) ? new char[0] : trustCertificateKeyStorePassword.toCharArray(); + ksIS = ksURL.openStream(); + trustKeyStore.load(ksIS, password); + tmf.init(trustKeyStore); + } + } catch (NoSuchAlgorithmException nsae) { + throw SQLError.createSQLException("Unsupported keystore algorithm [" + nsae.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (KeyStoreException kse) { + throw SQLError.createSQLException("Could not create KeyStore instance [" + kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (CertificateException nsae) { + throw SQLError.createSQLException("Could not load trust" + trustCertificateKeyStoreType + " keystore from " + trustCertificateKeyStoreUrl, + SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); + } catch (MalformedURLException mue) { + throw SQLError.createSQLException(trustCertificateKeyStoreUrl + " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } catch (IOException ioe) { + SQLException sqlEx = SQLError.createSQLException("Cannot open " + trustCertificateKeyStoreUrl + " [" + ioe.getMessage() + "]", + SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); + + sqlEx.initCause(ioe); + + throw sqlEx; + } finally { + if (ksIS != null) { + try { + ksIS.close(); + } catch (IOException e) { + // can't close input stream, but keystore can be properly initialized so we shouldn't throw this exception + } + } + } + } + + SSLContext sslContext = null; + + try { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl) ? null : kmf.getKeyManagers(), + mysqlIO.connection.getVerifyServerCertificate() ? tmf.getTrustManagers() : new X509TrustManager[] { new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] chain, String authType) { + // return without complaint + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // return without complaint + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } }, null); + + return sslContext.getSocketFactory(); + } catch (NoSuchAlgorithmException nsae) { + throw SQLError.createSQLException("TLS is not a valid SSL protocol.", SQL_STATE_BAD_SSL_PARAMS, 0, false, mysqlIO.getExceptionInterceptor()); + } catch (KeyManagementException kme) { + throw SQLError.createSQLException("KeyManagementException: " + kme.getMessage(), SQL_STATE_BAD_SSL_PARAMS, 0, false, + mysqlIO.getExceptionInterceptor()); + } + } + + public static boolean isSSLEstablished(MysqlIO mysqlIO) { + return SSLSocket.class.isAssignableFrom(mysqlIO.mysqlConnection.getClass()); + } + + public static RSAPublicKey decodeRSAPublicKey(String key, ExceptionInterceptor interceptor) throws SQLException { + + try { + if (key == null) { + throw new SQLException("key parameter is null"); + } + + int offset = key.indexOf("\n") + 1; + int len = key.indexOf("-----END PUBLIC KEY-----") - offset; + + // TODO: use standard decoders with Java 6+ + byte[] certificateData = Base64Decoder.decode(key.getBytes(), offset, len); + + X509EncodedKeySpec spec = new X509EncodedKeySpec(certificateData); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return (RSAPublicKey) kf.generatePublic(spec); + } catch (Exception ex) { + throw SQLError.createSQLException("Unable to decode public key", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + } + } + + public static byte[] encryptWithRSAPublicKey(byte[] source, RSAPublicKey key, ExceptionInterceptor interceptor) throws SQLException { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(source); + } catch (Exception ex) { + throw SQLError.createSQLException(ex.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + } + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Extension.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Extension.java new file mode 100644 index 0000000..c6df1e8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Extension.java @@ -0,0 +1,59 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +public interface Extension { + + /** + * Called once per connection that wants to use the extension + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param conn + * the connection for which this extension is being created + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. Extension properties are not exposed via + * accessor/mutator methods on DataSources. + * + * @throws SQLException + * should be thrown if the the Extension + * can not initialize itself. + */ + + public abstract void init(Connection conn, Properties props) throws SQLException; + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + public abstract void destroy(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/FailoverConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/FailoverConnectionProxy.java new file mode 100644 index 0000000..3f76b4a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/FailoverConnectionProxy.java @@ -0,0 +1,510 @@ +/* + Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; + +/** + * A proxy for a dynamic com.mysql.jdbc.Connection implementation that provides failover features for list of hosts. Connection switching occurs on + * communications related exceptions and/or user defined settings, namely when one of the conditions set in 'secondsBeforeRetryMaster' or + * 'queriesBeforeRetryMaster' is met. + */ +public class FailoverConnectionProxy extends MultiHostConnectionProxy { + private static final String METHOD_SET_READ_ONLY = "setReadOnly"; + private static final String METHOD_SET_AUTO_COMMIT = "setAutoCommit"; + private static final String METHOD_COMMIT = "commit"; + private static final String METHOD_ROLLBACK = "rollback"; + + private static final int NO_CONNECTION_INDEX = -1; + private static final int DEFAULT_PRIMARY_HOST_INDEX = 0; + + private int secondsBeforeRetryPrimaryHost; + private long queriesBeforeRetryPrimaryHost; + private boolean failoverReadOnly; + private int retriesAllDown; + + private int currentHostIndex = NO_CONNECTION_INDEX; + private int primaryHostIndex = DEFAULT_PRIMARY_HOST_INDEX; + private Boolean explicitlyReadOnly = null; + private boolean explicitlyAutoCommit = true; + + private boolean enableFallBackToPrimaryHost = true; + private long primaryHostFailTimeMillis = 0; + private long queriesIssuedSinceFailover = 0; + + private static Class[] INTERFACES_TO_PROXY; + + static { + if (Util.isJdbc4()) { + try { + INTERFACES_TO_PROXY = new Class[] { Class.forName("com.mysql.jdbc.JDBC4MySQLConnection") }; + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + INTERFACES_TO_PROXY = new Class[] { MySQLConnection.class }; + } + } + + /** + * Proxy class to intercept and deal with errors that may occur in any object bound to the current connection. + * Additionally intercepts query executions and triggers an execution count on the outer class. + */ + class FailoverJdbcInterfaceProxy extends JdbcInterfaceProxy { + FailoverJdbcInterfaceProxy(Object toInvokeOn) { + super(toInvokeOn); + } + + @SuppressWarnings("synthetic-access") + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + boolean isExecute = methodName.startsWith("execute"); + + if (FailoverConnectionProxy.this.connectedToSecondaryHost() && isExecute) { + FailoverConnectionProxy.this.incrementQueriesIssuedSinceFailover(); + } + + Object result = super.invoke(proxy, method, args); + + if (FailoverConnectionProxy.this.explicitlyAutoCommit && isExecute && readyToFallBackToPrimaryHost()) { + // Fall back to primary host at transaction boundary + fallBackToPrimaryIfAvailable(); + } + + return result; + } + } + + public static Connection createProxyInstance(List hosts, Properties props) throws SQLException { + FailoverConnectionProxy connProxy = new FailoverConnectionProxy(hosts, props); + + return (Connection) java.lang.reflect.Proxy.newProxyInstance(Connection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy); + } + + /** + * Instantiates a new FailoverConnectionProxy for the given list of hosts and connection properties. + * + * @param hosts + * The lists of hosts available to switch on. + * @param props + * The properties to be used in new internal connections. + */ + private FailoverConnectionProxy(List hosts, Properties props) throws SQLException { + super(hosts, props); + + ConnectionPropertiesImpl connProps = new ConnectionPropertiesImpl(); + connProps.initializeProperties(props); + + this.secondsBeforeRetryPrimaryHost = connProps.getSecondsBeforeRetryMaster(); + this.queriesBeforeRetryPrimaryHost = connProps.getQueriesBeforeRetryMaster(); + this.failoverReadOnly = connProps.getFailOverReadOnly(); + this.retriesAllDown = connProps.getRetriesAllDown(); + + this.enableFallBackToPrimaryHost = this.secondsBeforeRetryPrimaryHost > 0 || this.queriesBeforeRetryPrimaryHost > 0; + + pickNewConnection(); + + this.explicitlyAutoCommit = this.currentConnection.getAutoCommit(); + } + + /* + * Gets locally bound instances of FailoverJdbcInterfaceProxy. + * + * @see com.mysql.jdbc.MultiHostConnectionProxy#getNewJdbcInterfaceProxy(java.lang.Object) + */ + @Override + JdbcInterfaceProxy getNewJdbcInterfaceProxy(Object toProxy) { + return new FailoverJdbcInterfaceProxy(toProxy); + } + + /* + * Local implementation for the connection switch exception checker. + * + * @see com.mysql.jdbc.MultiHostConnectionProxy#shouldExceptionTriggerConnectionSwitch(java.lang.Throwable) + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + if (!(t instanceof SQLException)) { + return false; + } + + String sqlState = ((SQLException) t).getSQLState(); + if (sqlState != null) { + if (sqlState.startsWith("08")) { + // connection error + return true; + } + } + + // Always handle CommunicationsException + if (t instanceof CommunicationsException) { + return true; + } + + return false; + } + + /** + * Checks if current connection is to a master host. + */ + @Override + boolean isMasterConnection() { + return connectedToPrimaryHost(); + } + + /* + * Local implementation for the new connection picker. + * + * @see com.mysql.jdbc.MultiHostConnectionProxy#pickNewConnection() + */ + @Override + synchronized void pickNewConnection() throws SQLException { + if (this.isClosed && this.closedExplicitly) { + return; + } + + if (!isConnected() || readyToFallBackToPrimaryHost()) { + try { + connectTo(this.primaryHostIndex); + } catch (SQLException e) { + resetAutoFallBackCounters(); + failOver(this.primaryHostIndex); + } + } else { + failOver(); + } + } + + /** + * Creates a new connection instance for host pointed out by the given host index. + * + * @param hostIndex + * The host index in the global hosts list. + * @return + * The new connection instance. + */ + synchronized ConnectionImpl createConnectionForHostIndex(int hostIndex) throws SQLException { + return createConnectionForHost(this.hostList.get(hostIndex)); + } + + /** + * Connects this dynamic failover connection proxy to the host pointed out by the given host index. + * + * @param hostIndex + * The host index in the global hosts list. + */ + private synchronized void connectTo(int hostIndex) throws SQLException { + try { + switchCurrentConnectionTo(hostIndex, createConnectionForHostIndex(hostIndex)); + } catch (SQLException e) { + if (this.currentConnection != null) { + StringBuilder msg = new StringBuilder("Connection to ").append(isPrimaryHostIndex(hostIndex) ? "primary" : "secondary").append(" host '") + .append(this.hostList.get(hostIndex)).append("' failed"); + this.currentConnection.getLog().logWarn(msg.toString(), e); + } + throw e; + } + } + + /** + * Replaces the previous underlying connection by the connection given. State from previous connection, if any, is synchronized with the new one. + * + * @param hostIndex + * The host index in the global hosts list that matches the given connection. + * @param connection + * The connection instance to switch to. + */ + private synchronized void switchCurrentConnectionTo(int hostIndex, MySQLConnection connection) throws SQLException { + invalidateCurrentConnection(); + + boolean readOnly; + if (isPrimaryHostIndex(hostIndex)) { + readOnly = this.explicitlyReadOnly == null ? false : this.explicitlyReadOnly; + } else if (this.failoverReadOnly) { + readOnly = true; + } else if (this.explicitlyReadOnly != null) { + readOnly = this.explicitlyReadOnly; + } else if (this.currentConnection != null) { + readOnly = this.currentConnection.isReadOnly(); + } else { + readOnly = false; + } + syncSessionState(this.currentConnection, connection, readOnly); + this.currentConnection = connection; + this.currentHostIndex = hostIndex; + } + + /** + * Initiates a default failover procedure starting at the current connection host index. + */ + private synchronized void failOver() throws SQLException { + failOver(this.currentHostIndex); + } + + /** + * Initiates a default failover procedure starting at the given host index. + * This process tries to connect, sequentially, to the next host in the list. The primary host may or may not be excluded from the connection attempts. + * + * @param failedHostIdx + * The host index where to start from. First connection attempt will be the next one. + */ + private synchronized void failOver(int failedHostIdx) throws SQLException { + int prevHostIndex = this.currentHostIndex; + int nextHostIndex = nextHost(failedHostIdx, false); + int firstHostIndexTried = nextHostIndex; + + SQLException lastExceptionCaught = null; + int attempts = 0; + boolean gotConnection = false; + boolean firstConnOrPassedByPrimaryHost = prevHostIndex == NO_CONNECTION_INDEX || isPrimaryHostIndex(prevHostIndex); + do { + try { + firstConnOrPassedByPrimaryHost = firstConnOrPassedByPrimaryHost || isPrimaryHostIndex(nextHostIndex); + + connectTo(nextHostIndex); + + if (firstConnOrPassedByPrimaryHost && connectedToSecondaryHost()) { + resetAutoFallBackCounters(); + } + gotConnection = true; + + } catch (SQLException e) { + lastExceptionCaught = e; + + if (shouldExceptionTriggerConnectionSwitch(e)) { + int newNextHostIndex = nextHost(nextHostIndex, attempts > 0); + + if (newNextHostIndex == firstHostIndexTried && newNextHostIndex == (newNextHostIndex = nextHost(nextHostIndex, true))) { // Full turn + attempts++; + + try { + Thread.sleep(250); + } catch (InterruptedException ie) { + } + } + + nextHostIndex = newNextHostIndex; + + } else { + throw e; + } + } + } while (attempts < this.retriesAllDown && !gotConnection); + + if (!gotConnection) { + throw lastExceptionCaught; + } + } + + /** + * Falls back to primary host or keep current connection if primary not available. + */ + synchronized void fallBackToPrimaryIfAvailable() { + MySQLConnection connection = null; + try { + connection = createConnectionForHostIndex(this.primaryHostIndex); + switchCurrentConnectionTo(this.primaryHostIndex, connection); + } catch (SQLException e1) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e2) { + } + } + // Keep current connection and reset counters + resetAutoFallBackCounters(); + } + } + + /** + * Gets the next host on the hosts list. Uses a round-robin algorithm to find the next element, but it may skip the index for the primary host. + * General rules to include the primary host are: + * - not currently connected to any host. + * - primary host is vouched (usually because connection to all secondary hosts has failed). + * - conditions to fall back to primary host are met (or they are disabled). + * + * @param currHostIdx + * Current host index. + * @param vouchForPrimaryHost + * Allows to return the primary host index even if the usual required conditions aren't met. + */ + private int nextHost(int currHostIdx, boolean vouchForPrimaryHost) { + int nextHostIdx = (currHostIdx + 1) % this.hostList.size(); + if (isPrimaryHostIndex(nextHostIdx) && isConnected() && !vouchForPrimaryHost && this.enableFallBackToPrimaryHost && !readyToFallBackToPrimaryHost()) { + // Skip primary host, assume this.hostList.size() >= 2 + nextHostIdx = nextHost(nextHostIdx, vouchForPrimaryHost); + } + return nextHostIdx; + } + + /** + * Increments counter for query executions. + */ + synchronized void incrementQueriesIssuedSinceFailover() { + this.queriesIssuedSinceFailover++; + } + + /** + * Checks if at least one of the required conditions to fall back to primary host is met, which is determined by the properties 'queriesBeforeRetryMaster' + * and 'secondsBeforeRetryMaster'. + */ + synchronized boolean readyToFallBackToPrimaryHost() { + return this.enableFallBackToPrimaryHost && connectedToSecondaryHost() && (secondsBeforeRetryPrimaryHostIsMet() || queriesBeforeRetryPrimaryHostIsMet()); + } + + /** + * Checks if there is a underlying connection for this proxy. + */ + synchronized boolean isConnected() { + return this.currentHostIndex != NO_CONNECTION_INDEX; + } + + /** + * Checks if the given host index points to the primary host. + * + * @param hostIndex + * The host index in the global hosts list. + */ + synchronized boolean isPrimaryHostIndex(int hostIndex) { + return hostIndex == this.primaryHostIndex; + } + + /** + * Checks if this proxy is using the primary host in the underlying connection. + */ + synchronized boolean connectedToPrimaryHost() { + return isPrimaryHostIndex(this.currentHostIndex); + } + + /** + * Checks if this proxy is using a secondary host in the underlying connection. + */ + synchronized boolean connectedToSecondaryHost() { + return this.currentHostIndex >= 0 && !isPrimaryHostIndex(this.currentHostIndex); + } + + /** + * Checks the condition set by the property 'secondsBeforeRetryMaster'. + */ + private synchronized boolean secondsBeforeRetryPrimaryHostIsMet() { + return this.secondsBeforeRetryPrimaryHost > 0 && Util.secondsSinceMillis(this.primaryHostFailTimeMillis) >= this.secondsBeforeRetryPrimaryHost; + } + + /** + * Checks the condition set by the property 'queriesBeforeRetryMaster'. + */ + private synchronized boolean queriesBeforeRetryPrimaryHostIsMet() { + return this.queriesBeforeRetryPrimaryHost > 0 && this.queriesIssuedSinceFailover >= this.queriesBeforeRetryPrimaryHost; + } + + /** + * Resets auto-fall back counters. + */ + private synchronized void resetAutoFallBackCounters() { + this.primaryHostFailTimeMillis = System.currentTimeMillis(); + this.queriesIssuedSinceFailover = 0; + } + + /** + * Closes current connection. + */ + @Override + synchronized void doClose() throws SQLException { + this.currentConnection.close(); + } + + /** + * Aborts current connection. + */ + @Override + synchronized void doAbortInternal() throws SQLException { + this.currentConnection.abortInternal(); + } + + /** + * Aborts current connection using the given executor. + */ + @Override + synchronized void doAbort(Executor executor) throws SQLException { + this.currentConnection.abort(executor); + } + + /* + * Local method invocation handling for this proxy. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (METHOD_SET_READ_ONLY.equals(methodName)) { + this.explicitlyReadOnly = (Boolean) args[0]; + if (this.failoverReadOnly && connectedToSecondaryHost()) { + return null; + } + } + + if (this.isClosed && !allowedOnClosedConnection(method)) { + if (this.autoReconnect && !this.closedExplicitly) { + this.currentHostIndex = NO_CONNECTION_INDEX; // Act as if this is the first connection but let it sync with the previous one. + pickNewConnection(); + this.isClosed = false; + this.closedReason = null; + } else { + String reason = "No operations allowed after connection closed."; + if (this.closedReason != null) { + reason += (" " + this.closedReason); + } + throw SQLError.createSQLException(reason, SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null /* no access to a interceptor here... */); + } + } + + Object result = null; + + try { + result = method.invoke(this.thisAsConnection, args); + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } + + if (METHOD_SET_AUTO_COMMIT.equals(methodName)) { + this.explicitlyAutoCommit = (Boolean) args[0]; + } + + if ((this.explicitlyAutoCommit || METHOD_COMMIT.equals(methodName) || METHOD_ROLLBACK.equals(methodName)) && readyToFallBackToPrimaryHost()) { + // Fall back to primary host at transaction boundary + fallBackToPrimaryIfAvailable(); + } + + return result; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Field.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Field.java new file mode 100644 index 0000000..21aeb50 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Field.java @@ -0,0 +1,907 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.sql.Types; +import java.util.regex.PatternSyntaxException; + +/** + * Field is a class used to describe fields in a ResultSet + */ +public class Field { + + private static final int AUTO_INCREMENT_FLAG = 512; + + private static final int NO_CHARSET_INFO = -1; + + private byte[] buffer; + + private int collationIndex = 0; + + private String encoding = null; + + private int colDecimals; + + private short colFlag; + + private String collationName = null; + + private MySQLConnection connection = null; + + private String databaseName = null; + + private int databaseNameLength = -1; + + // database name info + private int databaseNameStart = -1; + + protected int defaultValueLength = -1; + + // default value info - from COM_LIST_FIELDS execution + protected int defaultValueStart = -1; + + private String fullName = null; + + private String fullOriginalName = null; + + private boolean isImplicitTempTable = false; + + private long length; // Internal length of the field; + + private int mysqlType = -1; // the MySQL type + + private String name; // The Field name + + private int nameLength; + + private int nameStart; + + private String originalColumnName = null; + + private int originalColumnNameLength = -1; + + // column name info (before aliasing) + private int originalColumnNameStart = -1; + + private String originalTableName = null; + + private int originalTableNameLength = -1; + + // table name info (before aliasing) + private int originalTableNameStart = -1; + + private int precisionAdjustFactor = 0; + + private int sqlType = -1; // the java.sql.Type + + private String tableName; // The Name of the Table + + private int tableNameLength; + + private int tableNameStart; + + private boolean useOldNameMetadata = false; + + private boolean isSingleBit; + + private int maxBytesPerChar; + + private final boolean valueNeedsQuoting; + + /** + * Constructor used when communicating with 4.1 and newer servers + */ + Field(MySQLConnection conn, byte[] buffer, int databaseNameStart, int databaseNameLength, int tableNameStart, int tableNameLength, + int originalTableNameStart, int originalTableNameLength, int nameStart, int nameLength, int originalColumnNameStart, int originalColumnNameLength, + long length, int mysqlType, short colFlag, int colDecimals, int defaultValueStart, int defaultValueLength, int charsetIndex) throws SQLException { + this.connection = conn; + this.buffer = buffer; + this.nameStart = nameStart; + this.nameLength = nameLength; + this.tableNameStart = tableNameStart; + this.tableNameLength = tableNameLength; + this.length = length; + this.colFlag = colFlag; + this.colDecimals = colDecimals; + this.mysqlType = mysqlType; + + // 4.1 field info... + this.databaseNameStart = databaseNameStart; + this.databaseNameLength = databaseNameLength; + + this.originalTableNameStart = originalTableNameStart; + this.originalTableNameLength = originalTableNameLength; + + this.originalColumnNameStart = originalColumnNameStart; + this.originalColumnNameLength = originalColumnNameLength; + + this.defaultValueStart = defaultValueStart; + this.defaultValueLength = defaultValueLength; + + // If we're not running 4.1 or newer, use the connection's charset + this.collationIndex = charsetIndex; + + // Map MySqlTypes to java.sql Types + this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType); + + checkForImplicitTemporaryTable(); + // Re-map to 'real' blob type, if we're a BLOB + boolean isFromFunction = this.originalTableNameLength == 0; + + if (this.mysqlType == MysqlDefs.FIELD_TYPE_BLOB) { + if (this.connection.getBlobsAreStrings() || (this.connection.getFunctionsNeverReturnBlobs() && isFromFunction)) { + this.sqlType = Types.VARCHAR; + this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR; + } else if (this.collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary || !this.connection.versionMeetsMinimum(4, 1, 0)) { + if (this.connection.getUseBlobToStoreUTF8OutsideBMP() && shouldSetupForUtf8StringInBlob()) { + setupForUtf8StringInBlob(); + } else { + setBlobTypeBasedOnLength(); + this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType); + } + } else { + // *TEXT masquerading as blob + this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING; + this.sqlType = Types.LONGVARCHAR; + } + } + + if (this.sqlType == Types.TINYINT && this.length == 1 && this.connection.getTinyInt1isBit()) { + // Adjust for pseudo-boolean + if (conn.getTinyInt1isBit()) { + if (conn.getTransformedBitIsBoolean()) { + this.sqlType = Types.BOOLEAN; + } else { + this.sqlType = Types.BIT; + } + } + + } + + if (!isNativeNumericType() && !isNativeDateTimeType()) { + this.encoding = this.connection.getEncodingForIndex(this.collationIndex); + + // ucs2, utf16, and utf32 cannot be used as a client character set, but if it was received from server under some circumstances we can parse them as + // utf16 + if ("UnicodeBig".equals(this.encoding)) { + this.encoding = "UTF-16"; + } + + // MySQL encodes JSON data with utf8mb4. + if (this.mysqlType == MysqlDefs.FIELD_TYPE_JSON) { + this.encoding = "UTF-8"; + } + + // Handle VARBINARY/BINARY (server doesn't have a different type for this + + boolean isBinary = isBinary(); + + if (this.connection.versionMeetsMinimum(4, 1, 0) && this.mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING && isBinary + && this.collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary) { + if (this.connection.getFunctionsNeverReturnBlobs() && isFromFunction) { + this.sqlType = Types.VARCHAR; + this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR; + } else if (this.isOpaqueBinary()) { + this.sqlType = Types.VARBINARY; + } + } + + if (this.connection.versionMeetsMinimum(4, 1, 0) && this.mysqlType == MysqlDefs.FIELD_TYPE_STRING && isBinary + && this.collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary) { + // + // Okay, this is a hack, but there's currently no way to easily distinguish something like DATE_FORMAT( ..) from the "BINARY" column type, other + // than looking at the original column name. + // + + if (isOpaqueBinary() && !this.connection.getBlobsAreStrings()) { + this.sqlType = Types.BINARY; + } + } + + if (this.mysqlType == MysqlDefs.FIELD_TYPE_BIT) { + this.isSingleBit = this.length == 0 + || this.length == 1 && (this.connection.versionMeetsMinimum(5, 0, 21) || this.connection.versionMeetsMinimum(5, 1, 10)); + + if (!this.isSingleBit) { + this.colFlag |= 128; // Pretend this is a full binary(128) and blob(16) so that this field is de-serializable. + this.colFlag |= 16; + isBinary = true; + } + } + + // + // Handle TEXT type (special case), Fix proposed by Peter McKeown + // + if ((this.sqlType == java.sql.Types.LONGVARBINARY) && !isBinary) { + this.sqlType = java.sql.Types.LONGVARCHAR; + } else if ((this.sqlType == java.sql.Types.VARBINARY) && !isBinary) { + this.sqlType = java.sql.Types.VARCHAR; + } + } else { + this.encoding = "US-ASCII"; + } + + // + // Handle odd values for 'M' for floating point/decimal numbers + // + if (!isUnsigned()) { + switch (this.mysqlType) { + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + this.precisionAdjustFactor = -1; + + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + case MysqlDefs.FIELD_TYPE_FLOAT: + this.precisionAdjustFactor = 1; + + break; + } + } else { + switch (this.mysqlType) { + case MysqlDefs.FIELD_TYPE_DOUBLE: + case MysqlDefs.FIELD_TYPE_FLOAT: + this.precisionAdjustFactor = 1; + + break; + } + } + this.valueNeedsQuoting = determineNeedsQuoting(); + } + + private boolean shouldSetupForUtf8StringInBlob() throws SQLException { + String includePattern = this.connection.getUtf8OutsideBmpIncludedColumnNamePattern(); + String excludePattern = this.connection.getUtf8OutsideBmpExcludedColumnNamePattern(); + + if (excludePattern != null && !StringUtils.isEmptyOrWhitespaceOnly(excludePattern)) { + try { + if (getOriginalName().matches(excludePattern)) { + if (includePattern != null && !StringUtils.isEmptyOrWhitespaceOnly(includePattern)) { + try { + if (getOriginalName().matches(includePattern)) { + return true; + } + } catch (PatternSyntaxException pse) { + SQLException sqlEx = SQLError.createSQLException("Illegal regex specified for \"utf8OutsideBmpIncludedColumnNamePattern\"", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.connection.getExceptionInterceptor()); + + if (!this.connection.getParanoid()) { + sqlEx.initCause(pse); + } + + throw sqlEx; + } + } + + return false; + } + } catch (PatternSyntaxException pse) { + SQLException sqlEx = SQLError.createSQLException("Illegal regex specified for \"utf8OutsideBmpExcludedColumnNamePattern\"", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.connection.getExceptionInterceptor()); + + if (!this.connection.getParanoid()) { + sqlEx.initCause(pse); + } + + throw sqlEx; + } + } + + return true; + } + + private void setupForUtf8StringInBlob() { + if (this.length == MysqlDefs.LENGTH_TINYBLOB || this.length == MysqlDefs.LENGTH_BLOB) { + this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR; + this.sqlType = Types.VARCHAR; + } else { + this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING; + this.sqlType = Types.LONGVARCHAR; + } + + this.collationIndex = CharsetMapping.MYSQL_COLLATION_INDEX_utf8; + } + + /** + * Constructor used when communicating with pre 4.1 servers + */ + Field(MySQLConnection conn, byte[] buffer, int nameStart, int nameLength, int tableNameStart, int tableNameLength, int length, int mysqlType, short colFlag, + int colDecimals) throws SQLException { + this(conn, buffer, -1, -1, tableNameStart, tableNameLength, -1, -1, nameStart, nameLength, -1, -1, length, mysqlType, colFlag, colDecimals, -1, -1, + NO_CHARSET_INFO); + } + + /** + * Constructor used by DatabaseMetaData methods. + */ + Field(String tableName, String columnName, int jdbcType, int length) { + this.tableName = tableName; + this.name = columnName; + this.length = length; + this.sqlType = jdbcType; + this.colFlag = 0; + this.colDecimals = 0; + this.valueNeedsQuoting = determineNeedsQuoting(); + } + + /** + * Used by prepared statements to re-use result set data conversion methods + * when generating bound parmeter retrieval instance for statement + * interceptors. + * + * @param tableName + * not used + * @param columnName + * not used + * @param charsetIndex + * the MySQL collation/character set index + * @param jdbcType + * from java.sql.Types + * @param length + * length in characters or bytes (for BINARY data). + */ + Field(String tableName, String columnName, int charsetIndex, int jdbcType, int length) { + this.tableName = tableName; + this.name = columnName; + this.length = length; + this.sqlType = jdbcType; + this.colFlag = 0; + this.colDecimals = 0; + this.collationIndex = charsetIndex; + this.valueNeedsQuoting = determineNeedsQuoting(); + + switch (this.sqlType) { + case Types.BINARY: + case Types.VARBINARY: + this.colFlag |= 128; + this.colFlag |= 16; + break; + } + } + + private void checkForImplicitTemporaryTable() { + this.isImplicitTempTable = this.tableNameLength > 5 && this.buffer[this.tableNameStart] == (byte) '#' + && this.buffer[this.tableNameStart + 1] == (byte) 's' && this.buffer[this.tableNameStart + 2] == (byte) 'q' + && this.buffer[this.tableNameStart + 3] == (byte) 'l' && this.buffer[this.tableNameStart + 4] == (byte) '_'; + } + + /** + * Returns the Java encoding (if known) for this field. + * + * @return the Java encoding + */ + public String getEncoding() throws SQLException { + return this.encoding; + } + + public void setEncoding(String javaEncodingName, Connection conn) throws SQLException { + this.encoding = javaEncodingName; + try { + this.collationIndex = CharsetMapping.getCollationIndexForJavaEncoding(javaEncodingName, conn); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + } + + public synchronized String getCollation() throws SQLException { + if (this.collationName == null) { + if (this.connection != null) { + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + if (this.connection.getUseDynamicCharsetInfo()) { + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + + String quotedIdStr = dbmd.getIdentifierQuoteString(); + + if (" ".equals(quotedIdStr)) { + quotedIdStr = ""; + } + + String csCatalogName = getDatabaseName(); + String csTableName = getOriginalTableName(); + String csColumnName = getOriginalName(); + + if (csCatalogName != null && csCatalogName.length() != 0 && csTableName != null && csTableName.length() != 0 && csColumnName != null + && csColumnName.length() != 0) { + StringBuilder queryBuf = new StringBuilder(csCatalogName.length() + csTableName.length() + 28); + queryBuf.append("SHOW FULL COLUMNS FROM "); + queryBuf.append(quotedIdStr); + queryBuf.append(csCatalogName); + queryBuf.append(quotedIdStr); + queryBuf.append("."); + queryBuf.append(quotedIdStr); + queryBuf.append(csTableName); + queryBuf.append(quotedIdStr); + + java.sql.Statement collationStmt = null; + java.sql.ResultSet collationRs = null; + + try { + collationStmt = this.connection.createStatement(); + + collationRs = collationStmt.executeQuery(queryBuf.toString()); + + while (collationRs.next()) { + if (csColumnName.equals(collationRs.getString("Field"))) { + this.collationName = collationRs.getString("Collation"); + + break; + } + } + } finally { + if (collationRs != null) { + collationRs.close(); + collationRs = null; + } + + if (collationStmt != null) { + collationStmt.close(); + collationStmt = null; + } + } + } + } else { + try { + this.collationName = CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[this.collationIndex]; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + } + } + } + } + + return this.collationName; + } + + public String getColumnLabel() throws SQLException { + return getName(); // column name if not aliased, alias if used + } + + public String getDatabaseName() throws SQLException { + if ((this.databaseName == null) && (this.databaseNameStart != -1) && (this.databaseNameLength != -1)) { + this.databaseName = getStringFromBytes(this.databaseNameStart, this.databaseNameLength); + } + + return this.databaseName; + } + + int getDecimals() { + return this.colDecimals; + } + + public String getFullName() throws SQLException { + if (this.fullName == null) { + StringBuilder fullNameBuf = new StringBuilder(getTableName().length() + 1 + getName().length()); + fullNameBuf.append(this.tableName); + + // much faster to append a char than a String + fullNameBuf.append('.'); + fullNameBuf.append(this.name); + this.fullName = fullNameBuf.toString(); + fullNameBuf = null; + } + + return this.fullName; + } + + public String getFullOriginalName() throws SQLException { + getOriginalName(); + + if (this.originalColumnName == null) { + return null; // we don't have this information + } + + if (this.fullName == null) { + StringBuilder fullOriginalNameBuf = new StringBuilder(getOriginalTableName().length() + 1 + getOriginalName().length()); + fullOriginalNameBuf.append(this.originalTableName); + + // much faster to append a char than a String + fullOriginalNameBuf.append('.'); + fullOriginalNameBuf.append(this.originalColumnName); + this.fullOriginalName = fullOriginalNameBuf.toString(); + fullOriginalNameBuf = null; + } + + return this.fullOriginalName; + } + + public long getLength() { + return this.length; + } + + public synchronized int getMaxBytesPerCharacter() throws SQLException { + if (this.maxBytesPerChar == 0) { + this.maxBytesPerChar = this.connection.getMaxBytesPerChar(this.collationIndex, getEncoding()); + } + return this.maxBytesPerChar; + } + + public int getMysqlType() { + return this.mysqlType; + } + + public String getName() throws SQLException { + if (this.name == null) { + this.name = getStringFromBytes(this.nameStart, this.nameLength); + } + + return this.name; + } + + public String getNameNoAliases() throws SQLException { + if (this.useOldNameMetadata) { + return getName(); + } + + if (this.connection != null && this.connection.versionMeetsMinimum(4, 1, 0)) { + return getOriginalName(); + } + + return getName(); + } + + public String getOriginalName() throws SQLException { + if ((this.originalColumnName == null) && (this.originalColumnNameStart != -1) && (this.originalColumnNameLength != -1)) { + this.originalColumnName = getStringFromBytes(this.originalColumnNameStart, this.originalColumnNameLength); + } + + return this.originalColumnName; + } + + public String getOriginalTableName() throws SQLException { + if ((this.originalTableName == null) && (this.originalTableNameStart != -1) && (this.originalTableNameLength != -1)) { + this.originalTableName = getStringFromBytes(this.originalTableNameStart, this.originalTableNameLength); + } + + return this.originalTableName; + } + + /** + * Returns amount of correction that should be applied to the precision + * value. + * + * Different versions of MySQL report different precision values. + * + * @return the amount to adjust precision value by. + */ + public int getPrecisionAdjustFactor() { + return this.precisionAdjustFactor; + } + + public int getSQLType() { + return this.sqlType; + } + + /** + * Create a string with the correct charset encoding from the byte-buffer + * that contains the data for this field + */ + private String getStringFromBytes(int stringStart, int stringLength) throws SQLException { + if ((stringStart == -1) || (stringLength == -1)) { + return null; + } + + String stringVal = null; + + if (this.connection != null) { + if (this.connection.getUseUnicode()) { + String javaEncoding = this.connection.getCharacterSetMetadata(); + + if (javaEncoding == null) { + javaEncoding = this.connection.getEncoding(); + } + + if (javaEncoding != null) { + SingleByteCharsetConverter converter = null; + + if (this.connection != null) { + converter = this.connection.getCharsetConverter(javaEncoding); + } + + if (converter != null) { // we have a converter + stringVal = converter.toString(this.buffer, stringStart, stringLength); + } else { + // we have no converter, use JVM converter + try { + stringVal = StringUtils.toString(this.buffer, stringStart, stringLength, javaEncoding); + } catch (UnsupportedEncodingException ue) { + throw new RuntimeException(Messages.getString("Field.12") + javaEncoding + Messages.getString("Field.13")); + } + } + } else { + // we have no encoding, use JVM standard charset + stringVal = StringUtils.toAsciiString(this.buffer, stringStart, stringLength); + } + } else { + // we are not using unicode, so use JVM standard charset + stringVal = StringUtils.toAsciiString(this.buffer, stringStart, stringLength); + } + } else { + // we don't have a connection, so punt + stringVal = StringUtils.toAsciiString(this.buffer, stringStart, stringLength); + } + + return stringVal; + } + + public String getTable() throws SQLException { + return getTableName(); + } + + public String getTableName() throws SQLException { + if (this.tableName == null) { + this.tableName = getStringFromBytes(this.tableNameStart, this.tableNameLength); + } + + return this.tableName; + } + + public String getTableNameNoAliases() throws SQLException { + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + return getOriginalTableName(); + } + + return getTableName(); // pre-4.1, no aliases returned + } + + public boolean isAutoIncrement() { + return ((this.colFlag & AUTO_INCREMENT_FLAG) > 0); + } + + public boolean isBinary() { + return ((this.colFlag & 128) > 0); + } + + public boolean isBlob() { + return ((this.colFlag & 16) > 0); + } + + /** + * Is this field owned by a server-created temporary table? + */ + private boolean isImplicitTemporaryTable() { + return this.isImplicitTempTable; + } + + public boolean isMultipleKey() { + return ((this.colFlag & 8) > 0); + } + + boolean isNotNull() { + return ((this.colFlag & 1) > 0); + } + + boolean isOpaqueBinary() throws SQLException { + + // + // Detect CHAR(n) CHARACTER SET BINARY which is a synonym for fixed-length binary types + // + + if (this.collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary && isBinary() + && (this.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING || this.getMysqlType() == MysqlDefs.FIELD_TYPE_VAR_STRING)) { + + if (this.originalTableNameLength == 0 && (this.connection != null && !this.connection.versionMeetsMinimum(5, 0, 25))) { + return false; // Probably from function + } + + // Okay, queries resolved by temp tables also have this 'signature', check for that + + return !isImplicitTemporaryTable(); + } + + return (this.connection.versionMeetsMinimum(4, 1, 0) && "binary".equalsIgnoreCase(getEncoding())); + + } + + public boolean isPrimaryKey() { + return ((this.colFlag & 2) > 0); + } + + /** + * Is this field _definitely_ not writable? + * + * @return true if this field can not be written to in an INSERT/UPDATE + * statement. + */ + boolean isReadOnly() throws SQLException { + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + String orgColumnName = getOriginalName(); + String orgTableName = getOriginalTableName(); + + return !(orgColumnName != null && orgColumnName.length() > 0 && orgTableName != null && orgTableName.length() > 0); + } + + return false; // we can't tell definitively in this case. + } + + public boolean isUniqueKey() { + return ((this.colFlag & 4) > 0); + } + + public boolean isUnsigned() { + return ((this.colFlag & 32) > 0); + } + + public void setUnsigned() { + this.colFlag |= 32; + } + + public boolean isZeroFill() { + return ((this.colFlag & 64) > 0); + } + + // + // MySQL only has one protocol-level BLOB type that it exposes which is FIELD_TYPE_BLOB, although we can divine what the actual type is by the length + // reported ... + // + private void setBlobTypeBasedOnLength() { + if (this.length == MysqlDefs.LENGTH_TINYBLOB) { + this.mysqlType = MysqlDefs.FIELD_TYPE_TINY_BLOB; + } else if (this.length == MysqlDefs.LENGTH_BLOB) { + this.mysqlType = MysqlDefs.FIELD_TYPE_BLOB; + } else if (this.length == MysqlDefs.LENGTH_MEDIUMBLOB) { + this.mysqlType = MysqlDefs.FIELD_TYPE_MEDIUM_BLOB; + } else if (this.length == MysqlDefs.LENGTH_LONGBLOB) { + this.mysqlType = MysqlDefs.FIELD_TYPE_LONG_BLOB; + } + } + + private boolean isNativeNumericType() { + return ((this.mysqlType >= MysqlDefs.FIELD_TYPE_TINY && this.mysqlType <= MysqlDefs.FIELD_TYPE_DOUBLE) + || this.mysqlType == MysqlDefs.FIELD_TYPE_LONGLONG || this.mysqlType == MysqlDefs.FIELD_TYPE_YEAR); + } + + private boolean isNativeDateTimeType() { + return (this.mysqlType == MysqlDefs.FIELD_TYPE_DATE || this.mysqlType == MysqlDefs.FIELD_TYPE_NEWDATE || this.mysqlType == MysqlDefs.FIELD_TYPE_DATETIME + || this.mysqlType == MysqlDefs.FIELD_TYPE_TIME || this.mysqlType == MysqlDefs.FIELD_TYPE_TIMESTAMP); + } + + public void setConnection(MySQLConnection conn) { + this.connection = conn; + + if (this.encoding == null || this.collationIndex == 0) { + this.encoding = this.connection.getEncoding(); + } + } + + void setMysqlType(int type) { + this.mysqlType = type; + this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType); + } + + protected void setUseOldNameMetadata(boolean useOldNameMetadata) { + this.useOldNameMetadata = useOldNameMetadata; + } + + @Override + public String toString() { + try { + StringBuilder asString = new StringBuilder(); + asString.append(super.toString()); + asString.append("["); + asString.append("catalog="); + asString.append(this.getDatabaseName()); + asString.append(",tableName="); + asString.append(this.getTableName()); + asString.append(",originalTableName="); + asString.append(this.getOriginalTableName()); + asString.append(",columnName="); + asString.append(this.getName()); + asString.append(",originalColumnName="); + asString.append(this.getOriginalName()); + asString.append(",mysqlType="); + asString.append(getMysqlType()); + asString.append("("); + asString.append(MysqlDefs.typeToName(getMysqlType())); + asString.append(")"); + asString.append(",flags="); + + if (isAutoIncrement()) { + asString.append(" AUTO_INCREMENT"); + } + + if (isPrimaryKey()) { + asString.append(" PRIMARY_KEY"); + } + + if (isUniqueKey()) { + asString.append(" UNIQUE_KEY"); + } + + if (isBinary()) { + asString.append(" BINARY"); + } + + if (isBlob()) { + asString.append(" BLOB"); + } + + if (isMultipleKey()) { + asString.append(" MULTI_KEY"); + } + + if (isUnsigned()) { + asString.append(" UNSIGNED"); + } + + if (isZeroFill()) { + asString.append(" ZEROFILL"); + } + + asString.append(", charsetIndex="); + asString.append(this.collationIndex); + asString.append(", charsetName="); + asString.append(this.encoding); + + //if (this.buffer != null) { + // asString.append("\n\nData as received from server:\n\n"); + // asString.append(StringUtils.dumpAsHex(this.buffer, + // this.buffer.length)); + //} + + asString.append("]"); + + return asString.toString(); + } catch (Throwable t) { + return super.toString(); + } + } + + protected boolean isSingleBit() { + return this.isSingleBit; + } + + protected boolean getvalueNeedsQuoting() { + return this.valueNeedsQuoting; + } + + private boolean determineNeedsQuoting() { + boolean retVal = false; + + switch (this.sqlType) { + case Types.BIGINT: + case Types.BIT: + case Types.DECIMAL: + case Types.DOUBLE: + case Types.FLOAT: + case Types.INTEGER: + case Types.NUMERIC: + case Types.REAL: + case Types.SMALLINT: + case Types.TINYINT: + retVal = false; + break; + default: + retVal = true; + } + return retVal; + + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/IterateBlock.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/IterateBlock.java new file mode 100644 index 0000000..35ba6bd --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/IterateBlock.java @@ -0,0 +1,75 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Iterator; + +import com.mysql.jdbc.DatabaseMetaData.IteratorWithCleanup; + +public abstract class IterateBlock { + IteratorWithCleanup iteratorWithCleanup; + Iterator javaIterator; + boolean stopIterating = false; + + IterateBlock(IteratorWithCleanup i) { + this.iteratorWithCleanup = i; + this.javaIterator = null; + } + + IterateBlock(Iterator i) { + this.javaIterator = i; + this.iteratorWithCleanup = null; + } + + public void doForAll() throws SQLException { + if (this.iteratorWithCleanup != null) { + try { + while (this.iteratorWithCleanup.hasNext()) { + forEach(this.iteratorWithCleanup.next()); + + if (this.stopIterating) { + break; + } + } + } finally { + this.iteratorWithCleanup.close(); + } + } else { + while (this.javaIterator.hasNext()) { + forEach(this.javaIterator.next()); + + if (this.stopIterating) { + break; + } + } + } + } + + abstract void forEach(T each) throws SQLException; + + public final boolean fullIteration() { + return !this.stopIterating; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42CallableStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42CallableStatement.java new file mode 100644 index 0000000..6f0c5ed --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42CallableStatement.java @@ -0,0 +1,226 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.SQLType; + +public class JDBC42CallableStatement extends JDBC4CallableStatement { + public JDBC42CallableStatement(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + super(conn, paramInfo); + } + + public JDBC42CallableStatement(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + super(conn, sql, catalog, isFunctionCall); + } + + /** + * Helper methods. + */ + + private int checkSqlType(int sqlType) throws SQLException { + return JDBC42Helper.checkSqlType(sqlType, getExceptionInterceptor()); + } + + private int translateAndCheckSqlType(SQLType sqlType) throws SQLException { + return JDBC42Helper.translateAndCheckSqlType(sqlType, getExceptionInterceptor()); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + super.registerOutParameter(parameterIndex, translateAndCheckSqlType(sqlType)); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + super.registerOutParameter(parameterIndex, translateAndCheckSqlType(sqlType), scale); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + super.registerOutParameter(parameterIndex, translateAndCheckSqlType(sqlType), typeName); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + super.registerOutParameter(parameterName, translateAndCheckSqlType(sqlType)); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + super.registerOutParameter(parameterName, translateAndCheckSqlType(sqlType), scale); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + super.registerOutParameter(parameterName, translateAndCheckSqlType(sqlType), typeName); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType), scaleOrLength); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType)); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterName + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterName, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType)); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterName + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterName, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42Helper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42Helper.java new file mode 100644 index 0000000..204aa26 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42Helper.java @@ -0,0 +1,74 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Date; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.JDBCType; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.time.OffsetTime; +import java.time.OffsetDateTime; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; + +public class JDBC42Helper { + /** + * JDBC 4.2 Helper methods. + */ + static Object convertJavaTimeToJavaSql(Object x) { + if (x instanceof LocalDate) { + return Date.valueOf((LocalDate) x); + } else if (x instanceof LocalDateTime) { + return Timestamp.valueOf((LocalDateTime) x); + } else if (x instanceof LocalTime) { + return Time.valueOf((LocalTime) x); + } + return x; + } + + static boolean isSqlTypeSupported(int sqlType) { + return sqlType != Types.REF_CURSOR && sqlType != Types.TIME_WITH_TIMEZONE && sqlType != Types.TIMESTAMP_WITH_TIMEZONE; + } + + static int checkSqlType(int sqlType, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (JDBC42Helper.isSqlTypeSupported(sqlType)) { + return sqlType; + } + throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("UnsupportedSQLType.0") + JDBCType.valueOf(sqlType), + SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, exceptionInterceptor); + } + + static int translateAndCheckSqlType(SQLType sqlType, ExceptionInterceptor exceptionInterceptor) throws SQLException { + return JDBC42Helper.checkSqlType(sqlType.getVendorTypeNumber(), exceptionInterceptor); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42PreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42PreparedStatement.java new file mode 100644 index 0000000..22d6e03 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42PreparedStatement.java @@ -0,0 +1,133 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Date; +import java.sql.SQLException; +import java.sql.SQLType; + +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.PreparedStatement.ParseInfo; + +public class JDBC42PreparedStatement extends JDBC4PreparedStatement { + public JDBC42PreparedStatement(MySQLConnection conn, String catalog) throws SQLException { + super(conn, catalog); + } + + public JDBC42PreparedStatement(MySQLConnection conn, String sql, String catalog) throws SQLException { + super(conn, sql, catalog); + } + + public JDBC42PreparedStatement(MySQLConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + super(conn, sql, catalog, cachedParseInfo); + } + + /* + * Helper methods. + */ + + private int checkSqlType(int sqlType) throws SQLException { + return JDBC42Helper.checkSqlType(sqlType, getExceptionInterceptor()); + } + + private int translateAndCheckSqlType(SQLType sqlType) throws SQLException { + return JDBC42Helper.translateAndCheckSqlType(sqlType, getExceptionInterceptor()); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType), scaleOrLength); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType)); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ResultSet.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ResultSet.java new file mode 100644 index 0000000..88d6d3c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ResultSet.java @@ -0,0 +1,136 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Date; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.Struct; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.format.DateTimeParseException; + +import com.mysql.jdbc.Field; +import com.mysql.jdbc.RowData; +import com.mysql.jdbc.SQLError; + +public class JDBC42ResultSet extends JDBC4ResultSet { + + public JDBC42ResultSet(long updateCount, long updateID, MySQLConnection conn, StatementImpl creatorStmt) { + super(updateCount, updateID, conn, creatorStmt); + } + + public JDBC42ResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + super(catalog, fields, tuples, conn, creatorStmt); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param type + * @return + * @throws SQLException + */ + public T getObject(int columnIndex, Class type) throws SQLException { + if (type == null) { + throw SQLError.createSQLException("Type parameter can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (type.equals(LocalDate.class)) { + return type.cast(getDate(columnIndex).toLocalDate()); + } else if (type.equals(LocalDateTime.class)) { + return type.cast(getTimestamp(columnIndex).toLocalDateTime()); + } else if (type.equals(LocalTime.class)) { + return type.cast(getTime(columnIndex).toLocalTime()); + } else if (type.equals(OffsetDateTime.class)) { + try { + return type.cast(OffsetDateTime.parse(getString(columnIndex))); + } catch (DateTimeParseException e) { + // Let it continue and try by object deserialization. + } + } else if (type.equals(OffsetTime.class)) { + try { + return type.cast(OffsetTime.parse(getString(columnIndex))); + } catch (DateTimeParseException e) { + // Let it continue and try by object deserialization. + } + } + + return super.getObject(columnIndex, type); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. (Not updatable) + * + * @param columnIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { + throw new NotUpdatable(); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param columnIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + throw new NotUpdatable(); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param columnLabel + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { + throw new NotUpdatable(); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param columnLabel + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + throw new NotUpdatable(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ServerPreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ServerPreparedStatement.java new file mode 100644 index 0000000..fc3fea8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42ServerPreparedStatement.java @@ -0,0 +1,121 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.SQLType; + +public class JDBC42ServerPreparedStatement extends JDBC4ServerPreparedStatement { + public JDBC42ServerPreparedStatement(MySQLConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) throws SQLException { + super(conn, sql, catalog, resultSetType, resultSetConcurrency); + } + + /* + * Helper methods. + */ + + private int checkSqlType(int sqlType) throws SQLException { + return JDBC42Helper.checkSqlType(sqlType, getExceptionInterceptor()); + } + + private int translateAndCheckSqlType(SQLType sqlType) throws SQLException { + return JDBC42Helper.translateAndCheckSqlType(sqlType, getExceptionInterceptor()); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType)); + } + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), checkSqlType(targetSqlType), scaleOrLength); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType)); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.setObject(parameterIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java new file mode 100644 index 0000000..348360a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC42UpdatableResultSet.java @@ -0,0 +1,194 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Date; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.Struct; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.format.DateTimeParseException; + +import com.mysql.jdbc.Field; +import com.mysql.jdbc.RowData; +import com.mysql.jdbc.SQLError; + +public class JDBC42UpdatableResultSet extends JDBC4UpdatableResultSet { + + public JDBC42UpdatableResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + super(catalog, fields, tuples, conn, creatorStmt); + } + + /* + * Helper methods. + */ + + private int translateAndCheckSqlType(SQLType sqlType) throws SQLException { + return JDBC42Helper.translateAndCheckSqlType(sqlType, getExceptionInterceptor()); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param type + * @return + * @throws SQLException + */ + public T getObject(int columnIndex, Class type) throws SQLException { + if (type == null) { + throw SQLError.createSQLException("Type parameter can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (type.equals(LocalDate.class)) { + return type.cast(getDate(columnIndex).toLocalDate()); + } else if (type.equals(LocalDateTime.class)) { + return type.cast(getTimestamp(columnIndex).toLocalDateTime()); + } else if (type.equals(LocalTime.class)) { + return type.cast(getTime(columnIndex).toLocalTime()); + } else if (type.equals(OffsetDateTime.class)) { + try { + return type.cast(OffsetDateTime.parse(getString(columnIndex))); + } catch (DateTimeParseException e) { + // Let it continue and try by object deserialization. + } + } else if (type.equals(OffsetTime.class)) { + try { + return type.cast(OffsetTime.parse(getString(columnIndex))); + } catch (DateTimeParseException e) { + // Let it continue and try by object deserialization. + } + } + + return super.getObject(columnIndex, type); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param x + * @throws SQLException + */ + @Override + public synchronized void updateObject(int columnIndex, Object x) throws SQLException { + super.updateObject(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x)); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param x + * @param scaleOrLength + * @throws SQLException + */ + @Override + public synchronized void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + super.updateObject(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), scaleOrLength); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnLabel + * @param x + * @throws SQLException + */ + @Override + public synchronized void updateObject(String columnLabel, Object x) throws SQLException { + super.updateObject(columnLabel, JDBC42Helper.convertJavaTimeToJavaSql(x)); + } + + /** + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnLabel + * @param x + * @param scaleOrLength + * @throws SQLException + */ + @Override + public synchronized void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + super.updateObject(columnLabel, JDBC42Helper.convertJavaTimeToJavaSql(x), scaleOrLength); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public synchronized void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { + super.updateObjectInternal(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), 0); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public synchronized void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + super.updateObjectInternal(columnIndex, JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnLabel + * @param x + * @param targetSqlType + * @throws SQLException + */ + public synchronized void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { + super.updateObjectInternal(findColumn(columnLabel), JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), 0); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * Support for java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.OffsetTime and java.time.OffsetDateTime. + * + * @param columnLabel + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public synchronized void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + super.updateObjectInternal(findColumn(columnLabel), JDBC42Helper.convertJavaTimeToJavaSql(x), translateAndCheckSqlType(targetSqlType), scaleOrLength); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CallableStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CallableStatement.java new file mode 100644 index 0000000..5f37c55 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CallableStatement.java @@ -0,0 +1,240 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLException; +import java.sql.RowId; +import java.sql.SQLXML; +import java.sql.NClob; + +public class JDBC4CallableStatement extends CallableStatement { + + public JDBC4CallableStatement(MySQLConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + super(conn, paramInfo); + } + + public JDBC4CallableStatement(MySQLConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + super(conn, sql, catalog, isFunctionCall); + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x); + } + + public void setRowId(String parameterName, RowId x) throws SQLException { + JDBC4PreparedStatementHelper.setRowId(this, getNamedParamIndex(parameterName, false), x); + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject); + } + + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + JDBC4PreparedStatementHelper.setSQLXML(this, getNamedParamIndex(parameterName, false), xmlObject); + + } + + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + SQLXML retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getSQLXML(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + + } + + public SQLXML getSQLXML(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + SQLXML retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getSQLXML(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public RowId getRowId(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + RowId retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getRowId(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public RowId getRowId(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + RowId retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getRowId(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing a NCLOB + * + * @throws SQLException + * if a database error occurs + */ + public void setNClob(int parameterIndex, NClob value) throws SQLException { + JDBC4PreparedStatementHelper.setNClob(this, parameterIndex, value); + } + + public void setNClob(String parameterName, NClob value) throws SQLException { + JDBC4PreparedStatementHelper.setNClob(this, getNamedParamIndex(parameterName, false), value); + + } + + public void setNClob(String parameterName, Reader reader) throws SQLException { + setNClob(getNamedParamIndex(parameterName, false), reader); + + } + + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + setNClob(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setNString(String parameterName, String value) throws SQLException { + setNString(getNamedParamIndex(parameterName, false), value); + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(int) + */ + public Reader getCharacterStream(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Reader retValue = rs.getCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(java.lang.String) + */ + public Reader getCharacterStream(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Reader retValue = rs.getCharacterStream(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNCharacterStream(int) + */ + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Reader retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNCharacterStream(java.lang.String) + */ + public Reader getNCharacterStream(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Reader retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNCharacterStream(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNClob(int) + */ + public NClob getNClob(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + NClob retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNClob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNClob(java.lang.String) + */ + public NClob getNClob(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + NClob retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNClob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNString(int) + */ + public String getNString(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + String retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNString(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getNString(java.lang.String) + */ + public String getNString(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + String retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs).getNString(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java new file mode 100644 index 0000000..7137148 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java @@ -0,0 +1,133 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.SQLClientInfoException; +import java.util.Properties; + +/** + * Classes that implement this interface and provide a no-args constructor can be used by the driver to store and retrieve client information and/or labels. + * + * The driver will create an instance for each Connection instance, and call initialize() once and only once. When the connection is closed, destroy() will be + * called, and the provider is expected to clean up any resources at this time. + */ +public interface JDBC4ClientInfoProvider { + /** + * Called once by the driver when it needs to configure the provider. + * + * @param conn + * the connection that the provider belongs too. + * @param configurationProps + * a java.util.Properties instance that contains + * configuration information for the connection. + * @throws SQLException + * if initialization fails. + */ + public void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException; + + /** + * Called once by the driver when the connection this provider instance + * belongs to is being closed. + * + * Implementations are expected to clean up and resources at this point + * in time. + * + * @throws SQLException + * if an error occurs. + */ + public void destroy() throws SQLException; + + /** + * Returns the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * @return + * @throws SQLException + * + * @see java.sql.Connection#getClientInfo() + */ + public Properties getClientInfo(java.sql.Connection conn) throws SQLException; + + /** + * Returns the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * @return + * @throws SQLException + * + * @see java.sql.Connection#getClientInfo(java.lang.String) + */ + public String getClientInfo(java.sql.Connection conn, String name) throws SQLException; + + /** + * Sets the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * @return + * @throws SQLException + * + * @see java.sql.Connection#setClientInfo(java.util.Properties) + */ + public void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException; + + /** + * Sets the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * @return + * @throws SQLException + * + * @see java.sql.Connection#setClientInfo(java.lang.String,java.lang.String) + */ + public void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java new file mode 100644 index 0000000..d9fe950 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java @@ -0,0 +1,152 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLClientInfoException; +import java.util.Enumeration; +import java.util.Properties; + +public class JDBC4ClientInfoProviderSP implements JDBC4ClientInfoProvider { + PreparedStatement setClientInfoSp; + + PreparedStatement getClientInfoSp; + + PreparedStatement getClientInfoBulkSp; + + public synchronized void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException { + String identifierQuote = conn.getMetaData().getIdentifierQuoteString(); + String setClientInfoSpName = configurationProps.getProperty("clientInfoSetSPName", "setClientInfo"); + String getClientInfoSpName = configurationProps.getProperty("clientInfoGetSPName", "getClientInfo"); + String getClientInfoBulkSpName = configurationProps.getProperty("clientInfoGetBulkSPName", "getClientInfoBulk"); + String clientInfoCatalog = configurationProps.getProperty("clientInfoCatalog", ""); // "" means use current from + // connection + + String catalog = "".equals(clientInfoCatalog) ? conn.getCatalog() : clientInfoCatalog; + + this.setClientInfoSp = ((com.mysql.jdbc.Connection) conn).clientPrepareStatement( + "CALL " + identifierQuote + catalog + identifierQuote + "." + identifierQuote + setClientInfoSpName + identifierQuote + "(?, ?)"); + + this.getClientInfoSp = ((com.mysql.jdbc.Connection) conn).clientPrepareStatement( + "CALL" + identifierQuote + catalog + identifierQuote + "." + identifierQuote + getClientInfoSpName + identifierQuote + "(?)"); + + this.getClientInfoBulkSp = ((com.mysql.jdbc.Connection) conn).clientPrepareStatement( + "CALL " + identifierQuote + catalog + identifierQuote + "." + identifierQuote + getClientInfoBulkSpName + identifierQuote + "()"); + } + + public synchronized void destroy() throws SQLException { + if (this.setClientInfoSp != null) { + this.setClientInfoSp.close(); + this.setClientInfoSp = null; + } + + if (this.getClientInfoSp != null) { + this.getClientInfoSp.close(); + this.getClientInfoSp = null; + } + + if (this.getClientInfoBulkSp != null) { + this.getClientInfoBulkSp.close(); + this.getClientInfoBulkSp = null; + } + } + + public synchronized Properties getClientInfo(java.sql.Connection conn) throws SQLException { + ResultSet rs = null; + + Properties props = new Properties(); + + try { + this.getClientInfoBulkSp.execute(); + + rs = this.getClientInfoBulkSp.getResultSet(); + + while (rs.next()) { + props.setProperty(rs.getString(1), rs.getString(2)); + } + } finally { + if (rs != null) { + rs.close(); + } + } + + return props; + } + + public synchronized String getClientInfo(java.sql.Connection conn, String name) throws SQLException { + ResultSet rs = null; + + String clientInfo = null; + + try { + this.getClientInfoSp.setString(1, name); + this.getClientInfoSp.execute(); + + rs = this.getClientInfoSp.getResultSet(); + + if (rs.next()) { + clientInfo = rs.getString(1); + } + } finally { + if (rs != null) { + rs.close(); + } + } + + return clientInfo; + } + + public synchronized void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException { + try { + Enumeration propNames = properties.propertyNames(); + + while (propNames.hasMoreElements()) { + String name = (String) propNames.nextElement(); + String value = properties.getProperty(name); + + setClientInfo(conn, name, value); + } + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + public synchronized void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException { + try { + this.setClientInfoSp.setString(1, name); + this.setClientInfoSp.setString(2, value); + this.setClientInfoSp.execute(); + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java new file mode 100644 index 0000000..0863acc --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java @@ -0,0 +1,96 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLClientInfoException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +/** + * An implementation of JDBC4ClientInfoProvider that exposes the client info as a comment prepended to all statements issued by the driver. + * + * Client information is never read from the server with this implementation, it is always cached locally. + */ + +public class JDBC4CommentClientInfoProvider implements JDBC4ClientInfoProvider { + private Properties clientInfo; + + public synchronized void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException { + this.clientInfo = new Properties(); + } + + public synchronized void destroy() throws SQLException { + this.clientInfo = null; + } + + public synchronized Properties getClientInfo(java.sql.Connection conn) throws SQLException { + return this.clientInfo; + } + + public synchronized String getClientInfo(java.sql.Connection conn, String name) throws SQLException { + return this.clientInfo.getProperty(name); + } + + public synchronized void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException { + this.clientInfo = new Properties(); + + Enumeration propNames = properties.propertyNames(); + + while (propNames.hasMoreElements()) { + String name = (String) propNames.nextElement(); + + this.clientInfo.put(name, properties.getProperty(name)); + } + + setComment(conn); + } + + public synchronized void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException { + this.clientInfo.setProperty(name, value); + setComment(conn); + } + + private synchronized void setComment(java.sql.Connection conn) { + StringBuilder commentBuf = new StringBuilder(); + Iterator> elements = this.clientInfo.entrySet().iterator(); + + while (elements.hasNext()) { + if (commentBuf.length() > 0) { + commentBuf.append(", "); + } + + Map.Entry entry = elements.next(); + commentBuf.append("" + entry.getKey()); + commentBuf.append("="); + commentBuf.append("" + entry.getValue()); + } + + ((com.mysql.jdbc.Connection) conn).setStatementComment(commentBuf.toString()); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4Connection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4Connection.java new file mode 100644 index 0000000..ad3de92 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4Connection.java @@ -0,0 +1,239 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.NClob; +import java.sql.Struct; +import java.util.Properties; +import java.util.TimerTask; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; + +public class JDBC4Connection extends ConnectionImpl implements JDBC4MySQLConnection { + + private static final long serialVersionUID = 2877471301981509475L; + + private JDBC4ClientInfoProvider infoProvider; + + public JDBC4Connection(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException { + super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url); + } + + public SQLXML createSQLXML() throws SQLException { + return new JDBC4MysqlSQLXML(getExceptionInterceptor()); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public Properties getClientInfo() throws SQLException { + return getClientInfoProviderImpl().getClientInfo(this); + } + + public String getClientInfo(String name) throws SQLException { + return getClientInfoProviderImpl().getClientInfo(this, name); + } + + /** + * Returns true if the connection has not been closed and is still valid. + * The driver shall submit a query on the connection or use some other + * mechanism that positively verifies the connection is still valid when + * this method is called. + *

    + * The query submitted by the driver to validate the connection shall be executed in the context of the current transaction. + * + * @param timeout + * - The time in seconds to wait for the database operation + * used to validate the connection to complete. If + * the timeout period expires before the operation + * completes, this method returns false. A value of + * 0 indicates a timeout is not applied to the + * database operation. + *

    + * @return true if the connection is valid, false otherwise + * @exception SQLException + * if the value supplied for timeout is less then 0 + * @since 1.6 + */ + public boolean isValid(int timeout) throws SQLException { + synchronized (getConnectionMutex()) { + if (isClosed()) { + return false; + } + + try { + try { + pingInternal(false, timeout * 1000); + } catch (Throwable t) { + try { + abortInternal(); + } catch (Throwable ignoreThrown) { + // we're dead now anyway + } + + return false; + } + + } catch (Throwable t) { + return false; + } + + return true; + } + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + getClientInfoProviderImpl().setClientInfo(this, properties); + } catch (SQLClientInfoException ciEx) { + throw ciEx; + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + getClientInfoProviderImpl().setClientInfo(this, name, value); + } catch (SQLClientInfoException ciEx) { + throw ciEx; + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() { + return new com.mysql.jdbc.Blob(getExceptionInterceptor()); + } + + /** + * @see java.sql.Connection#createClob() + */ + public Clob createClob() { + return new com.mysql.jdbc.Clob(getExceptionInterceptor()); + } + + /** + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() { + return new com.mysql.jdbc.JDBC4NClob(getExceptionInterceptor()); + } + + public JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.infoProvider == null) { + try { + try { + this.infoProvider = (JDBC4ClientInfoProvider) Util.getInstance(getClientInfoProvider(), new Class[0], new Object[0], + getExceptionInterceptor()); + } catch (SQLException sqlEx) { + if (sqlEx.getCause() instanceof ClassCastException) { + // try with package name prepended + this.infoProvider = (JDBC4ClientInfoProvider) Util.getInstance("com.mysql.jdbc." + getClientInfoProvider(), new Class[0], + new Object[0], getExceptionInterceptor()); + } + } + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("JDBC4Connection.ClientInfoNotImplemented", new Object[] { getClientInfoProvider() }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.infoProvider.initialize(this, this.props); + } + + return this.infoProvider; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java new file mode 100644 index 0000000..84ddcff --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java @@ -0,0 +1,171 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; + +import java.util.List; + +import com.mysql.jdbc.Field; + +public class JDBC4DatabaseMetaData extends DatabaseMetaData { + public JDBC4DatabaseMetaData(MySQLConnection connToSet, String databaseToSet) { + super(connToSet, databaseToSet); + } + + public RowIdLifetime getRowIdLifetime() throws SQLException { + return RowIdLifetime.ROWID_UNSUPPORTED; + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.conn.getExceptionInterceptor()); + } + } + + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return false; + } + + /** + * Changes in behavior introduced in JDBC4 when #getFunctionColumns became available. Overrides + * DatabaseMetaData#getProcedureColumns + * + * @see DatabaseMetaData#getProcedureColumns + * @since 1.6 + */ + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) + throws SQLException { + Field[] fields = createProcedureColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, procedureNamePattern, columnNamePattern, true, + conn.getGetProceduresReturnsFunctions()); + } + + /** + * Changes in behavior introduced in JDBC4 when #getFunctions became available. Overrides + * DatabaseMetaData#getProcedures. + * + * @see DatabaseMetaData#getProcedures + * @since 1.6 + */ + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + Field[] fields = createFieldMetadataForGetProcedures(); + + return getProceduresAndOrFunctions(fields, catalog, schemaPattern, procedureNamePattern, true, conn.getGetProceduresReturnsFunctions()); + } + + /** + * Overrides DatabaseMetaData#getJDBC4FunctionNoTableConstant. + * + * @return java.sql.DatabaseMetaData#functionNoTable + */ + protected int getJDBC4FunctionNoTableConstant() { + return functionNoTable; + } + + /** + * This method overrides DatabaseMetaData#getColumnType(boolean, boolean, boolean, boolean). + * + * @see JDBC4DatabaseMetaData#getProcedureOrFunctionColumnType(boolean, boolean, boolean, boolean) + */ + protected int getColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + return JDBC4DatabaseMetaData.getProcedureOrFunctionColumnType(isOutParam, isInParam, isReturnParam, forGetFunctionColumns); + } + + /** + * Determines the COLUMN_TYPE information based on parameter type (IN, OUT or INOUT) or function return parameter. + * + * @param isOutParam + * Indicates whether it's an output parameter. + * @param isInParam + * Indicates whether it's an input parameter. + * @param isReturnParam + * Indicates whether it's a function return parameter. + * @param forGetFunctionColumns + * Indicates whether the column belong to a function. + * + * @return The corresponding COLUMN_TYPE as in java.sql.getProcedureColumns API. + */ + protected static int getProcedureOrFunctionColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + + if (isInParam && isOutParam) { + return forGetFunctionColumns ? functionColumnInOut : procedureColumnInOut; + } else if (isInParam) { + return forGetFunctionColumns ? functionColumnIn : procedureColumnIn; + } else if (isOutParam) { + return forGetFunctionColumns ? functionColumnOut : procedureColumnOut; + } else if (isReturnParam) { + return forGetFunctionColumns ? functionReturn : procedureColumnReturn; + } else { + return forGetFunctionColumns ? functionColumnUnknown : procedureColumnUnknown; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java new file mode 100644 index 0000000..c433efe --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java @@ -0,0 +1,179 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; + +import java.util.List; + +import com.mysql.jdbc.Field; + +public class JDBC4DatabaseMetaDataUsingInfoSchema extends DatabaseMetaDataUsingInfoSchema { + public JDBC4DatabaseMetaDataUsingInfoSchema(MySQLConnection connToSet, String databaseToSet) throws SQLException { + super(connToSet, databaseToSet); + } + + public RowIdLifetime getRowIdLifetime() throws SQLException { + return RowIdLifetime.ROWID_UNSUPPORTED; + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.conn.getExceptionInterceptor()); + } + } + + /** + * Redirects to another implementation of #getProcedureColumns. Overrides + * DatabaseMetaDataUsingInfoSchema#getProcedureColumnsNoISParametersView. + * + * @see DatabaseMetaDataUsingInfoSchema#getProcedureColumns + * @see DatabaseMetaDataUsingInfoSchema#getProcedureColumnsNoISParametersView + */ + protected ResultSet getProcedureColumnsNoISParametersView(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) + throws SQLException { + Field[] fields = createProcedureColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, procedureNamePattern, columnNamePattern, true, + conn.getGetProceduresReturnsFunctions()); + } + + /** + * Returns a condition to be injected in the query that returns metadata for procedures only. Overrides + * DatabaseMetaDataUsingInfoSchema#injectRoutineTypeConditionForGetProcedures. When not empty must end with "AND ". + * + * @return String with the condition to be injected. + */ + protected String getRoutineTypeConditionForGetProcedures() { + return conn.getGetProceduresReturnsFunctions() ? "" : "ROUTINE_TYPE = 'PROCEDURE' AND "; + } + + /** + * Returns a condition to be injected in the query that returns metadata for procedure columns only. Overrides + * DatabaseMetaDataUsingInfoSchema#injectRoutineTypeConditionForGetProcedureColumns. When not empty must end with + * "AND ". + * + * @return String with the condition to be injected. + */ + protected String getRoutineTypeConditionForGetProcedureColumns() { + return conn.getGetProceduresReturnsFunctions() ? "" : "ROUTINE_TYPE = 'PROCEDURE' AND "; + } + + /** + * Overrides DatabaseMetaDataUsingInfoSchema#getJDBC4FunctionConstant. + * + * @param constant + * the constant id from DatabaseMetaData fields to return. + * + * @return one of the java.sql.DatabaseMetaData#function* fields. + */ + protected int getJDBC4FunctionConstant(JDBC4FunctionConstant constant) { + switch (constant) { + case FUNCTION_COLUMN_IN: + return functionColumnIn; + case FUNCTION_COLUMN_INOUT: + return functionColumnInOut; + case FUNCTION_COLUMN_OUT: + return functionColumnOut; + case FUNCTION_COLUMN_RETURN: + return functionReturn; + case FUNCTION_COLUMN_RESULT: + return functionColumnResult; + case FUNCTION_COLUMN_UNKNOWN: + return functionColumnUnknown; + case FUNCTION_NO_NULLS: + return functionNoNulls; + case FUNCTION_NULLABLE: + return functionNullable; + case FUNCTION_NULLABLE_UNKNOWN: + return functionNullableUnknown; + default: + return -1; + } + } + + /** + * Overrides DatabaseMetaDataUsingInfoSchema#getJDBC4FunctionNoTableConstant. + * + * @return java.sql.DatabaseMetaData#functionNoTable. + */ + protected int getJDBC4FunctionNoTableConstant() { + return functionNoTable; + } + + /** + * Overrides DatabaseMetaData#getColumnType(boolean, boolean, boolean, boolean). + * + * @see JDBC4DatabaseMetaData#getProcedureOrFunctionColumnType(boolean, boolean, boolean, boolean) + */ + protected int getColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + return JDBC4DatabaseMetaData.getProcedureOrFunctionColumnType(isOutParam, isInParam, isReturnParam, forGetFunctionColumns); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4LoadBalancedMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4LoadBalancedMySQLConnection.java new file mode 100644 index 0000000..e586a65 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4LoadBalancedMySQLConnection.java @@ -0,0 +1,126 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.NClob; +import java.sql.Struct; +import java.util.Properties; +import java.util.TimerTask; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.LoadBalancedMySQLConnection; +import com.mysql.jdbc.LoadBalancedConnectionProxy; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; + +public class JDBC4LoadBalancedMySQLConnection extends LoadBalancedMySQLConnection implements JDBC4MySQLConnection { + + public JDBC4LoadBalancedMySQLConnection(LoadBalancedConnectionProxy proxy) throws SQLException { + super(proxy); + } + + private JDBC4MySQLConnection getJDBC4Connection() { + return (JDBC4MySQLConnection) getActiveMySQLConnection(); + } + + public SQLXML createSQLXML() throws SQLException { + return this.getJDBC4Connection().createSQLXML(); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return this.getJDBC4Connection().createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return this.getJDBC4Connection().createStruct(typeName, attributes); + } + + public Properties getClientInfo() throws SQLException { + return this.getJDBC4Connection().getClientInfo(); + } + + public String getClientInfo(String name) throws SQLException { + return this.getJDBC4Connection().getClientInfo(name); + } + + public boolean isValid(int timeout) throws SQLException { + return this.getJDBC4Connection().isValid(timeout); + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(properties); + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(name, value); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() { + return this.getJDBC4Connection().createBlob(); + } + + /** + * @see java.sql.Connection#createClob() + */ + public Clob createClob() { + return this.getJDBC4Connection().createClob(); + } + + /** + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() { + return this.getJDBC4Connection().createNClob(); + } + + public JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getThisAsProxy()) { + return this.getJDBC4Connection().getClientInfoProviderImpl(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MultiHostMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MultiHostMySQLConnection.java new file mode 100644 index 0000000..8a03a34 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MultiHostMySQLConnection.java @@ -0,0 +1,128 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.NClob; +import java.sql.Struct; +import java.util.Properties; +import java.util.TimerTask; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MultiHostConnectionProxy; +import com.mysql.jdbc.MultiHostMySQLConnection; +import com.mysql.jdbc.SQLError; + +public class JDBC4MultiHostMySQLConnection extends MultiHostMySQLConnection implements JDBC4MySQLConnection { + + public JDBC4MultiHostMySQLConnection(MultiHostConnectionProxy proxy) throws SQLException { + super(proxy); + } + + private JDBC4Connection getJDBC4Connection() { + return (JDBC4Connection) getThisAsProxy().currentConnection; + } + + public SQLXML createSQLXML() throws SQLException { + return this.getJDBC4Connection().createSQLXML(); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return this.getJDBC4Connection().createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return this.getJDBC4Connection().createStruct(typeName, attributes); + } + + public Properties getClientInfo() throws SQLException { + return this.getJDBC4Connection().getClientInfo(); + } + + public String getClientInfo(String name) throws SQLException { + return this.getJDBC4Connection().getClientInfo(name); + } + + public boolean isValid(int timeout) throws SQLException { + synchronized (getThisAsProxy()) { + return this.getJDBC4Connection().isValid(timeout); + } + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(properties); + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(name, value); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() { + return this.getJDBC4Connection().createBlob(); + } + + /** + * @see java.sql.Connection#createClob() + */ + public Clob createClob() { + return this.getJDBC4Connection().createClob(); + } + + /** + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() { + return this.getJDBC4Connection().createNClob(); + } + + public JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getThisAsProxy()) { + return this.getJDBC4Connection().getClientInfoProviderImpl(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MySQLConnection.java new file mode 100644 index 0000000..5c4717a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MySQLConnection.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.NClob; +import java.sql.Struct; +import java.util.Properties; +import java.util.TimerTask; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; + +public interface JDBC4MySQLConnection extends MySQLConnection { + + public SQLXML createSQLXML() throws SQLException; + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException; + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException; + + public Properties getClientInfo() throws SQLException; + + public String getClientInfo(String name) throws SQLException; + + public boolean isValid(int timeout) throws SQLException; + + public void setClientInfo(Properties properties) throws SQLClientInfoException; + + public void setClientInfo(String name, String value) throws SQLClientInfoException; + + public boolean isWrapperFor(Class iface) throws SQLException; + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException; + + public Blob createBlob(); + + public Clob createClob(); + + public NClob createNClob(); + + /* + * Non standard methods: + */ + JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java new file mode 100644 index 0000000..a74c8f7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java @@ -0,0 +1,787 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLXML; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stax.StAXResult; +import javax.xml.transform.stax.StAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.SAXException; + +public class JDBC4MysqlSQLXML implements SQLXML { + + private XMLInputFactory inputFactory; + + private XMLOutputFactory outputFactory; + + private String stringRep; + + private ResultSetInternalMethods owningResultSet; + + private int columnIndexOfXml; + + private boolean fromResultSet; + + private boolean isClosed = false; + + private boolean workingWithResult; + + private DOMResult asDOMResult; + + private SAXResult asSAXResult; + + private SimpleSaxToReader saxToReaderConverter; + + private StringWriter asStringWriter; + + private ByteArrayOutputStream asByteArrayOutputStream; + + private ExceptionInterceptor exceptionInterceptor; + + protected JDBC4MysqlSQLXML(ResultSetInternalMethods owner, int index, ExceptionInterceptor exceptionInterceptor) { + this.owningResultSet = owner; + this.columnIndexOfXml = index; + this.fromResultSet = true; + this.exceptionInterceptor = exceptionInterceptor; + } + + protected JDBC4MysqlSQLXML(ExceptionInterceptor exceptionInterceptor) { + this.fromResultSet = false; + this.exceptionInterceptor = exceptionInterceptor; + } + + public synchronized void free() throws SQLException { + this.stringRep = null; + this.asDOMResult = null; + this.asSAXResult = null; + this.inputFactory = null; + this.outputFactory = null; + this.owningResultSet = null; + this.workingWithResult = false; + this.isClosed = true; + + } + + public synchronized String getString() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + if (this.fromResultSet) { + return this.owningResultSet.getString(this.columnIndexOfXml); + } + + return this.stringRep; + } + + private synchronized void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException("SQLXMLInstance has been free()d", this.exceptionInterceptor); + } + } + + private synchronized void checkWorkingWithResult() throws SQLException { + if (this.workingWithResult) { + throw SQLError.createSQLException("Can't perform requested operation after getResult() has been called to write XML data", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + /** + * Sets the XML value designated by this SQLXML instance to the given String + * representation. The format of this String is defined by + * org.xml.sax.InputSource, where the characters in the stream represent the + * unicode code points for XML according to section 2 and appendix B of the + * XML 1.0 specification. Although an encoding declaration other than + * unicode may be present, the encoding of the String is unicode. The + * behavior of this method is the same as ResultSet.updateString() when the + * designated column of the ResultSet has a type java.sql.Types of SQLXML. + *

    + * The SQL XML object becomes not writeable when this method is called and may also become not readable depending on implementation. + * + * @param value + * the XML value + * @throws SQLException + * if there is an error processing the XML value. The getCause() + * method of the exception may provide a more detailed + * exception, for example, if the stream does not contain valid + * characters. An exception is thrown if the state is not + * writable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + + public synchronized void setString(String str) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.stringRep = str; + this.fromResultSet = false; + } + + public synchronized boolean isEmpty() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + if (!this.fromResultSet) { + return this.stringRep == null || this.stringRep.length() == 0; + } + + return false; + } + + public synchronized InputStream getBinaryStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + return this.owningResultSet.getBinaryStream(this.columnIndexOfXml); + } + + /** + * Retrieves the XML value designated by this SQLXML instance as a + * java.io.Reader object. The format of this stream is defined by + * org.xml.sax.InputSource, where the characters in the stream represent the + * unicode code points for XML according to section 2 and appendix B of the + * XML 1.0 specification. Although an encoding declaration other than + * unicode may be present, the encoding of the stream is unicode. The + * behavior of this method is the same as ResultSet.getCharacterStream() + * when the designated column of the ResultSet has a type java.sql.Types of + * SQLXML. + *

    + * The SQL XML object becomes not readable when this method is called and may also become not writable depending on implementation. + * + * @return a stream containing the XML data. + * @throws SQLException + * if there is an error processing the XML value. The getCause() + * method of the exception may provide a more detailed + * exception, for example, if the stream does not contain valid + * characters. An exception is thrown if the state is not + * readable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + public synchronized Reader getCharacterStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + return this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } + + /** + * Returns a Source for reading the XML value designated by this SQLXML + * instance. Sources are used as inputs to XML parsers and XSLT + * transformers. + *

    + * Sources for XML parsers will have namespace processing on by default. The systemID of the Source is implementation dependent. + *

    + * The SQL XML object becomes not readable when this method is called and may also become not writable depending on implementation. + *

    + * Note that SAX is a callback architecture, so a returned SAXSource should then be set with a content handler that will receive the SAX events from + * parsing. The content handler will receive callbacks based on the contents of the XML. + * + *

    +     * SAXSource saxSource = sqlxml.getSource(SAXSource.class);
    +     * XMLReader xmlReader = saxSource.getXMLReader();
    +     * xmlReader.setContentHandler(myHandler);
    +     * xmlReader.parse(saxSource.getInputSource());
    +     * 
    + * + * @param sourceClass + * The class of the source, or null. If the class is null, a + * vendor specifc Source implementation will be returned. The + * following classes are supported at a minimum: + * + * (MySQL returns a SAXSource if sourceClass == null) + * + *
    +     *    javax.xml.transform.dom.DOMSource - returns a DOMSource
    +     *    javax.xml.transform.sax.SAXSource - returns a SAXSource
    +     *    javax.xml.transform.stax.StAXSource - returns a StAXSource
    +     *    javax.xml.transform.stream.StreamSource - returns a StreamSource
    +     *            
    + * + * @return a Source for reading the XML value. + * @throws SQLException + * if there is an error processing the XML value or if this + * feature is not supported. The getCause() method of the + * exception may provide a more detailed exception, for example, + * if an XML parser exception occurs. An exception is thrown if + * the state is not readable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + @SuppressWarnings("unchecked") + public synchronized T getSource(Class clazz) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + // Note that we try and use streams here wherever possible for the day that the server actually supports streaming from server -> client + // (futureproofing) + + if (clazz == null || clazz.equals(SAXSource.class)) { + + InputSource inputSource = null; + + if (this.fromResultSet) { + inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml)); + } else { + inputSource = new InputSource(new StringReader(this.stringRep)); + } + + return (T) new SAXSource(inputSource); + } else if (clazz.equals(DOMSource.class)) { + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + builderFactory.setNamespaceAware(true); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + + InputSource inputSource = null; + + if (this.fromResultSet) { + inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml)); + } else { + inputSource = new InputSource(new StringReader(this.stringRep)); + } + + return (T) new DOMSource(builder.parse(inputSource)); + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException(t.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(t); + + throw sqlEx; + } + + } else if (clazz.equals(StreamSource.class)) { + Reader reader = null; + + if (this.fromResultSet) { + reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } else { + reader = new StringReader(this.stringRep); + } + + return (T) new StreamSource(reader); + } else if (clazz.equals(StAXSource.class)) { + try { + Reader reader = null; + + if (this.fromResultSet) { + reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } else { + reader = new StringReader(this.stringRep); + } + + return (T) new StAXSource(this.inputFactory.createXMLStreamReader(reader)); + } catch (XMLStreamException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(ex); + + throw sqlEx; + } + } else { + throw SQLError.createSQLException("XML Source of type \"" + clazz.toString() + "\" Not supported.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } + + /** + * Retrieves a stream that can be used to write the XML value that this + * SQLXML instance represents. The stream begins at position 0. The bytes of + * the stream are interpreted according to appendix F of the XML 1.0 + * specification The behavior of this method is the same as + * ResultSet.updateBinaryStream() when the designated column of the + * ResultSet has a type java.sql.Types of SQLXML. + *

    + * The SQL XML object becomes not writeable when this method is called and may also become not readable depending on implementation. + * + * @return a stream to which data can be written. + * @throws SQLException + * if there is an error processing the XML value. An exception + * is thrown if the state is not writable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + public synchronized OutputStream setBinaryStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + + return setBinaryStreamInternal(); + } + + private synchronized OutputStream setBinaryStreamInternal() throws SQLException { + this.asByteArrayOutputStream = new ByteArrayOutputStream(); + + return this.asByteArrayOutputStream; + } + + /** + * Retrieves a stream to be used to write the XML value that this SQLXML + * instance represents. The format of this stream is defined by + * org.xml.sax.InputSource, where the characters in the stream represent the + * unicode code points for XML according to section 2 and appendix B of the + * XML 1.0 specification. Although an encoding declaration other than + * unicode may be present, the encoding of the stream is unicode. The + * behavior of this method is the same as ResultSet.updateCharacterStream() + * when the designated column of the ResultSet has a type java.sql.Types of + * SQLXML. + *

    + * The SQL XML object becomes not writeable when this method is called and may also become not readable depending on implementation. + * + * @return a stream to which data can be written. + * @throws SQLException + * if there is an error processing the XML value. The getCause() + * method of the exception may provide a more detailed + * exception, for example, if the stream does not contain valid + * characters. An exception is thrown if the state is not + * writable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + public synchronized Writer setCharacterStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + + return setCharacterStreamInternal(); + } + + private synchronized Writer setCharacterStreamInternal() throws SQLException { + this.asStringWriter = new StringWriter(); + + return this.asStringWriter; + } + + /** + * Returns a Result for setting the XML value designated by this SQLXML + * instance. + *

    + * The systemID of the Result is implementation dependent. + *

    + * The SQL XML object becomes not writeable when this method is called and may also become not readable depending on implementation. + *

    + * Note that SAX is a callback architecture and the returned SAXResult has a content handler assigned that will receive the SAX events based on the contents + * of the XML. Call the content handler with the contents of the XML document to assign the values. + * + *

    +     * SAXResult saxResult = sqlxml.setResult(SAXResult.class);
    +     * ContentHandler contentHandler = saxResult.getXMLReader().getContentHandler();
    +     * contentHandler.startDocument();
    +     * // set the XML elements and attributes into the result
    +     * contentHandler.endDocument();
    +     * 
    + * + * @param resultClass + * The class of the result, or null. If resultClass is null, a + * vendor specific Result implementation will be returned. The + * following classes are supported at a minimum: + * + *
    +     *    javax.xml.transform.dom.DOMResult - returns a DOMResult
    +     *    javax.xml.transform.sax.SAXResult - returns a SAXResult
    +     *    javax.xml.transform.stax.StAXResult - returns a StAXResult
    +     *    javax.xml.transform.stream.StreamResult - returns a StreamResult
    +     *            
    + * + * @return Returns a Result for setting the XML value. + * @throws SQLException + * if there is an error processing the XML value or if this + * feature is not supported. The getCause() method of the + * exception may provide a more detailed exception, for example, + * if an XML parser exception occurs. An exception is thrown if + * the state is not writable. + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support this method + * @since 1.6 + */ + @SuppressWarnings("unchecked") + public synchronized T setResult(Class clazz) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + this.asDOMResult = null; + this.asSAXResult = null; + this.saxToReaderConverter = null; + this.stringRep = null; + this.asStringWriter = null; + this.asByteArrayOutputStream = null; + + if (clazz == null || clazz.equals(SAXResult.class)) { + this.saxToReaderConverter = new SimpleSaxToReader(); + + this.asSAXResult = new SAXResult(this.saxToReaderConverter); + + return (T) this.asSAXResult; + } else if (clazz.equals(DOMResult.class)) { + + this.asDOMResult = new DOMResult(); + return (T) this.asDOMResult; + + } else if (clazz.equals(StreamResult.class)) { + return (T) new StreamResult(setCharacterStreamInternal()); + } else if (clazz.equals(StAXResult.class)) { + try { + if (this.outputFactory == null) { + this.outputFactory = XMLOutputFactory.newInstance(); + } + + return (T) new StAXResult(this.outputFactory.createXMLEventWriter(setCharacterStreamInternal())); + } catch (XMLStreamException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(ex); + + throw sqlEx; + } + } else { + throw SQLError.createSQLException("XML Result of type \"" + clazz.toString() + "\" Not supported.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } + + private Reader binaryInputStreamStreamToReader(ByteArrayOutputStream out) { + + try { + // There's got to be an easier way to do this, but I don't feel like coding up Appendix F of the XML Spec myself, when there's a reusable way to do + // it, and we can warn folks away from BINARY xml streams that have to be parsed to determine the character encoding :P + + String encoding = "UTF-8"; + + try { + ByteArrayInputStream bIn = new ByteArrayInputStream(out.toByteArray()); + XMLStreamReader reader = this.inputFactory.createXMLStreamReader(bIn); + + int eventType = 0; + + while ((eventType = reader.next()) != XMLStreamReader.END_DOCUMENT) { + if (eventType == XMLStreamReader.START_DOCUMENT) { + String possibleEncoding = reader.getEncoding(); + + if (possibleEncoding != null) { + encoding = possibleEncoding; + } + + break; + } + } + } catch (Throwable t) { + // ignore, dealt with later when the string can't be parsed into valid XML + } + + return new StringReader(new String(out.toByteArray(), encoding)); + } catch (UnsupportedEncodingException badEnc) { + throw new RuntimeException(badEnc); + } + } + + protected String readerToString(Reader reader) throws SQLException { + StringBuilder buf = new StringBuilder(); + + int charsRead = 0; + + char[] charBuf = new char[512]; + + try { + while ((charsRead = reader.read(charBuf)) != -1) { + buf.append(charBuf, 0, charsRead); + } + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(ioEx.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(ioEx); + + throw sqlEx; + } + + return buf.toString(); + } + + protected synchronized Reader serializeAsCharacterStream() throws SQLException { + checkClosed(); + if (this.workingWithResult) { + // figure out what kind of result + if (this.stringRep != null) { + return new StringReader(this.stringRep); + } + + if (this.asDOMResult != null) { + return new StringReader(domSourceToString()); + } + + if (this.asStringWriter != null) { // stax result + return new StringReader(this.asStringWriter.toString()); + } + + if (this.asSAXResult != null) { + return this.saxToReaderConverter.toReader(); + } + + if (this.asByteArrayOutputStream != null) { + return binaryInputStreamStreamToReader(this.asByteArrayOutputStream); + } + } + + return this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } + + protected String domSourceToString() throws SQLException { + try { + DOMSource source = new DOMSource(this.asDOMResult.getNode()); + Transformer identity = TransformerFactory.newInstance().newTransformer(); + StringWriter stringOut = new StringWriter(); + Result result = new StreamResult(stringOut); + identity.transform(source, result); + + return stringOut.toString(); + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException(t.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(t); + + throw sqlEx; + } + } + + protected synchronized String serializeAsString() throws SQLException { + checkClosed(); + if (this.workingWithResult) { + // figure out what kind of result + if (this.stringRep != null) { + return this.stringRep; + } + + if (this.asDOMResult != null) { + return domSourceToString(); + } + + if (this.asStringWriter != null) { // stax result + return this.asStringWriter.toString(); + } + + if (this.asSAXResult != null) { + return readerToString(this.saxToReaderConverter.toReader()); + } + + if (this.asByteArrayOutputStream != null) { + return readerToString(binaryInputStreamStreamToReader(this.asByteArrayOutputStream)); + } + } + + return this.owningResultSet.getString(this.columnIndexOfXml); + } + + /* + * The SimpleSaxToReader class is an adaptation of the SAX "Writer" + * example from the Apache XercesJ-2 Project. The license for this + * code is as follows: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + class SimpleSaxToReader extends DefaultHandler { + StringBuilder buf = new StringBuilder(); + + public void startDocument() throws SAXException { + buf.append(""); + } + + public void endDocument() throws SAXException { + // Do we need to override this? + } + + public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException { + + this.buf.append("<"); + this.buf.append(qName); + + if (attrs != null) { + for (int i = 0; i < attrs.getLength(); i++) { + this.buf.append(" "); + this.buf.append(attrs.getQName(i)).append("=\""); + escapeCharsForXml(attrs.getValue(i), true); + this.buf.append("\""); + } + } + + this.buf.append(">"); + } + + public void characters(char buf[], int offset, int len) throws SAXException { + if (!this.inCDATA) { + escapeCharsForXml(buf, offset, len, false); + } else { + this.buf.append(buf, offset, len); + } + } + + public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + characters(ch, start, length); + } + + private boolean inCDATA = false; + + public void startCDATA() throws SAXException { + this.buf.append(""); + } + + public void comment(char ch[], int start, int length) throws SAXException { + // if (!fCanonical && fElementDepth > 0) { + this.buf.append(""); + // } + } + + Reader toReader() { + return new StringReader(this.buf.toString()); + } + + private void escapeCharsForXml(String str, boolean isAttributeData) { + if (str == null) { + return; + } + + int strLen = str.length(); + + for (int i = 0; i < strLen; i++) { + escapeCharsForXml(str.charAt(i), isAttributeData); + } + } + + private void escapeCharsForXml(char[] buf, int offset, int len, boolean isAttributeData) { + + if (buf == null) { + return; + } + + for (int i = 0; i < len; i++) { + escapeCharsForXml(buf[offset + i], isAttributeData); + } + } + + private void escapeCharsForXml(char c, boolean isAttributeData) { + switch (c) { + case '<': + this.buf.append("<"); + break; + + case '>': + this.buf.append(">"); + break; + + case '&': + this.buf.append("&"); + break; + + case '"': + + if (!isAttributeData) { + this.buf.append("\""); + } else { + this.buf.append("""); + } + + break; + + case '\r': + this.buf.append(" "); + break; + + default: + + if (((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A) || (c >= 0x7F && c <= 0x9F) || c == 0x2028) + || isAttributeData && (c == 0x09 || c == 0x0A)) { + this.buf.append("&#x"); + this.buf.append(Integer.toHexString(c).toUpperCase()); + this.buf.append(";"); + } else { + this.buf.append(c); + } + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4NClob.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4NClob.java new file mode 100644 index 0000000..2383520 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4NClob.java @@ -0,0 +1,40 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import com.mysql.jdbc.ExceptionInterceptor; + +/** + * Simplistic implementation of java.sql.NClob for MySQL Connector/J + */ +public class JDBC4NClob extends Clob implements java.sql.NClob { + + JDBC4NClob(ExceptionInterceptor exceptionInterceptor) { + super(exceptionInterceptor); + } + + JDBC4NClob(String charDataInit, ExceptionInterceptor exceptionInterceptor) { + super(charDataInit, exceptionInterceptor); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatement.java new file mode 100644 index 0000000..5fd6db7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatement.java @@ -0,0 +1,74 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.sql.NClob; +import java.sql.RowId; +import java.sql.SQLXML; +import java.sql.SQLException; +import java.sql.Types; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.PreparedStatement; +import com.mysql.jdbc.PreparedStatement.ParseInfo; + +public class JDBC4PreparedStatement extends PreparedStatement { + + public JDBC4PreparedStatement(MySQLConnection conn, String catalog) throws SQLException { + super(conn, catalog); + } + + public JDBC4PreparedStatement(MySQLConnection conn, String sql, String catalog) throws SQLException { + super(conn, sql, catalog); + } + + public JDBC4PreparedStatement(MySQLConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + super(conn, sql, catalog, cachedParseInfo); + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x); + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing a NCLOB + * + * @throws SQLException + * if a database error occurs + */ + public void setNClob(int parameterIndex, NClob value) throws SQLException { + JDBC4PreparedStatementHelper.setNClob(this, parameterIndex, value); + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java new file mode 100644 index 0000000..a1f9666 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java @@ -0,0 +1,96 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.Reader; +import java.sql.NClob; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Types; + +import com.mysql.jdbc.PreparedStatement; + +public class JDBC4PreparedStatementHelper { + private JDBC4PreparedStatementHelper() { + + } + + static void setRowId(PreparedStatement pstmt, int parameterIndex, RowId x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing a NCLOB + * + * @throws SQLException + * if a database error occurs + */ + static void setNClob(PreparedStatement pstmt, int parameterIndex, NClob value) throws SQLException { + if (value == null) { + pstmt.setNull(parameterIndex, java.sql.Types.NCLOB); + } else { + pstmt.setNCharacterStream(parameterIndex, value.getCharacterStream(), value.length()); + } + } + + static void setNClob(PreparedStatement pstmt, int parameterIndex, Reader reader) throws SQLException { + pstmt.setNCharacterStream(parameterIndex, reader); + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param reader + * the java reader which contains the UNICODE data + * @param length + * the number of characters in the stream + * + * @throws SQLException + * if a database error occurs + */ + static void setNClob(PreparedStatement pstmt, int parameterIndex, Reader reader, long length) throws SQLException { + if (reader == null) { + pstmt.setNull(parameterIndex, java.sql.Types.NCLOB); + } else { + pstmt.setNCharacterStream(parameterIndex, reader, length); + } + } + + static void setSQLXML(PreparedStatement pstmt, int parameterIndex, SQLXML xmlObject) throws SQLException { + if (xmlObject == null) { + pstmt.setNull(parameterIndex, Types.SQLXML); + } else { + // FIXME: Won't work for Non-MYSQL SQLXMLs + pstmt.setCharacterStream(parameterIndex, ((JDBC4MysqlSQLXML) xmlObject).serializeAsCharacterStream()); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ReplicationMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ReplicationMySQLConnection.java new file mode 100644 index 0000000..f1369d8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ReplicationMySQLConnection.java @@ -0,0 +1,126 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Blob; +import java.sql.Clob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.NClob; +import java.sql.Struct; +import java.util.Properties; +import java.util.TimerTask; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.ReplicationConnectionProxy; +import com.mysql.jdbc.ReplicationMySQLConnection; +import com.mysql.jdbc.SQLError; + +public class JDBC4ReplicationMySQLConnection extends ReplicationMySQLConnection implements JDBC4MySQLConnection { + + public JDBC4ReplicationMySQLConnection(ReplicationConnectionProxy proxy) throws SQLException { + super(proxy); + } + + private JDBC4MySQLConnection getJDBC4Connection() { + return (JDBC4MySQLConnection) getActiveMySQLConnection(); + } + + public SQLXML createSQLXML() throws SQLException { + return this.getJDBC4Connection().createSQLXML(); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return this.getJDBC4Connection().createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return this.getJDBC4Connection().createStruct(typeName, attributes); + } + + public Properties getClientInfo() throws SQLException { + return this.getJDBC4Connection().getClientInfo(); + } + + public String getClientInfo(String name) throws SQLException { + return this.getJDBC4Connection().getClientInfo(name); + } + + public boolean isValid(int timeout) throws SQLException { + return this.getJDBC4Connection().isValid(timeout); + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(properties); + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + this.getJDBC4Connection().setClientInfo(name, value); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() { + return this.getJDBC4Connection().createBlob(); + } + + /** + * @see java.sql.Connection#createClob() + */ + public Clob createClob() { + return this.getJDBC4Connection().createClob(); + } + + /** + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() { + return this.getJDBC4Connection().createNClob(); + } + + public JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getThisAsProxy()) { + return this.getJDBC4Connection().getClientInfoProviderImpl(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ResultSet.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ResultSet.java new file mode 100644 index 0000000..061ac3c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ResultSet.java @@ -0,0 +1,533 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.sql.NClob; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Struct; +import java.sql.SQLFeatureNotSupportedException; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.Field; +import com.mysql.jdbc.NotUpdatable; +import com.mysql.jdbc.ResultSetImpl; +import com.mysql.jdbc.RowData; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Statement; + +public class JDBC4ResultSet extends ResultSetImpl { + + public JDBC4ResultSet(long updateCount, long updateID, MySQLConnection conn, StatementImpl creatorStmt) { + super(updateCount, updateID, conn, creatorStmt); + } + + public JDBC4ResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + super(catalog, fields, tuples, conn, creatorStmt); + } + + /** + * JDBC 4.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnIndex + * the column to get the value from + * + * @return the value in the column as a java.io.Reader. + * + * @throws SQLException + * if an error occurs + */ + public Reader getNCharacterStream(int columnIndex) throws SQLException { + checkColumnBounds(columnIndex); + + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNCharacterStream() when field's charset isn't UTF-8"); + } + return getCharacterStream(columnIndex); + } + + /** + * JDBC 4.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnName + * the column name to retrieve the value from + * + * @return the value as a java.io.Reader + * + * @throws SQLException + * if an error occurs + */ + public Reader getNCharacterStream(String columnName) throws SQLException { + return getNCharacterStream(findColumn(columnName)); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + public NClob getNClob(int columnIndex) throws SQLException { + checkColumnBounds(columnIndex); + + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNClob() when field's charset isn't UTF-8"); + } + if (!this.isBinaryEncoded) { + String asString = getStringForNClob(columnIndex); + + if (asString == null) { + return null; + } + + return new com.mysql.jdbc.JDBC4NClob(asString, getExceptionInterceptor()); + } + + return getNativeNClob(columnIndex); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param colName + * the column name + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + public NClob getNClob(String columnName) throws SQLException { + return getNClob(findColumn(columnName)); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + protected java.sql.NClob getNativeNClob(int columnIndex) throws SQLException { + String stringVal = getStringForNClob(columnIndex); + + if (stringVal == null) { + return null; + } + + return getNClobFromString(stringVal, columnIndex); + } + + private String getStringForNClob(int columnIndex) throws SQLException { + String asString = null; + + String forcedEncoding = "UTF-8"; + + try { + byte[] asBytes = null; + + if (!this.isBinaryEncoded) { + asBytes = getBytes(columnIndex); + } else { + asBytes = getNativeBytes(columnIndex, true); + } + + if (asBytes != null) { + asString = new String(asBytes, forcedEncoding); + } + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + return asString; + } + + private final java.sql.NClob getNClobFromString(String stringVal, int columnIndex) throws SQLException { + return new com.mysql.jdbc.JDBC4NClob(stringVal, getExceptionInterceptor()); + } + + /** + * JDBC 4.0 + * + * Get the value of a column in the current row as a Java String + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value, null for SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public String getNString(int columnIndex) throws SQLException { + checkColumnBounds(columnIndex); + + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNString() when field's charset isn't UTF-8"); + } + return getString(columnIndex); + } + + /** + * JDBC 4.0 + * + * The following routines simply convert the columnName into a columnIndex + * and then call the appropriate routine above. + * + * @param columnName + * is the SQL name of the column + * + * @return the column value + * + * @exception SQLException + * if a database access error occurs + */ + public String getNString(String columnName) throws SQLException { + return getNString(findColumn(columnName)); + } + + /** + * JDBC 4.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateNCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 4.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param reader + * the stream to update the column with + * @param length + * of the stream + * + * @throws SQLException + * if a database-access error occurs + */ + public void updateNCharacterStream(String columnName, Reader reader, int length) throws SQLException { + updateNCharacterStream(findColumn(columnName), reader, length); + } + + /** + * @see ResultSet#updateNClob(String, NClob) + */ + public void updateNClob(String columnName, NClob nClob) throws SQLException { + updateNClob(findColumn(columnName), nClob); + } + + public void updateRowId(int columnIndex, RowId x) throws SQLException { + throw new NotUpdatable(); + } + + public void updateRowId(String columnName, RowId x) throws SQLException { + updateRowId(findColumn(columnName), x); + } + + public int getHoldability() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public RowId getRowId(int columnIndex) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public RowId getRowId(String columnLabel) throws SQLException { + return getRowId(findColumn(columnLabel)); + } + + public SQLXML getSQLXML(int columnIndex) throws SQLException { + checkColumnBounds(columnIndex); + + return new JDBC4MysqlSQLXML(this, columnIndex, getExceptionInterceptor()); + } + + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return getSQLXML(findColumn(columnLabel)); + } + + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + updateAsciiStream(findColumn(columnLabel), x); + + } + + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + updateAsciiStream(findColumn(columnLabel), x, length); + } + + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + updateBinaryStream(findColumn(columnLabel), x); + } + + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + updateBinaryStream(findColumn(columnLabel), x, length); + } + + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + throw new NotUpdatable(); + } + + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + updateBlob(findColumn(columnLabel), inputStream); + } + + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + updateBlob(findColumn(columnLabel), inputStream, length); + } + + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + updateCharacterStream(findColumn(columnLabel), reader); + } + + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + updateCharacterStream(findColumn(columnLabel), reader, length); + } + + public void updateClob(int columnIndex, Reader reader) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateClob(String columnLabel, Reader reader) throws SQLException { + updateClob(findColumn(columnLabel), reader); + } + + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + updateClob(findColumn(columnLabel), reader, length); + } + + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + updateNCharacterStream(findColumn(columnLabel), reader); + + } + + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + updateNCharacterStream(findColumn(columnLabel), reader, length); + } + + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + updateNClob(findColumn(columnLabel), reader); + + } + + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new NotUpdatable(); + } + + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + updateNClob(findColumn(columnLabel), reader, length); + } + + public void updateNString(int columnIndex, String nString) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNString(String columnLabel, String nString) throws SQLException { + updateNString(findColumn(columnLabel), nString); + } + + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + updateSQLXML(findColumn(columnLabel), xmlObject); + + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + @SuppressWarnings("unchecked") + public T getObject(int columnIndex, Class type) throws SQLException { + if (type == null) { + throw SQLError.createSQLException("Type parameter can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (type.equals(Struct.class)) { + throw new SQLFeatureNotSupportedException(); + } else if (type.equals(RowId.class)) { + return (T) getRowId(columnIndex); + } else if (type.equals(NClob.class)) { + return (T) getNClob(columnIndex); + } else if (type.equals(SQLXML.class)) { + return (T) getSQLXML(columnIndex); + } + + return super.getObject(columnIndex, type); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java new file mode 100644 index 0000000..494895c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java @@ -0,0 +1,135 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.Reader; +import java.sql.NClob; +import java.sql.RowId; +import java.sql.SQLXML; +import java.sql.SQLException; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.MysqlDefs; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.ServerPreparedStatement; +import com.mysql.jdbc.ServerPreparedStatement.BindValue; + +public class JDBC4ServerPreparedStatement extends ServerPreparedStatement { + + public JDBC4ServerPreparedStatement(MySQLConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) throws SQLException { + super(conn, sql, catalog, resultSetType, resultSetConcurrency); + } + + /** + * @see java.sql.PreparedStatement#setNCharacterStream(int, java.io.Reader, long) + */ + public void setNCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + // can't take if characterEncoding isn't utf8 + if (!this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { + throw SQLError.createSQLException("Can not call setNCharacterStream() when connection character set isn't UTF-8", getExceptionInterceptor()); + } + + checkClosed(); + + if (reader == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = reader; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = length; + } else { + binding.bindLength = -1; + } + } + } + + /** + * @see java.sql.PreparedStatement#setNClob(int, java.sql.NClob) + */ + public void setNClob(int parameterIndex, NClob x) throws SQLException { + setNClob(parameterIndex, x.getCharacterStream(), this.connection.getUseStreamLengthsInPrepStmts() ? x.length() : -1); + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param reader + * the java reader which contains the UNICODE data + * @param length + * the number of characters in the stream + * + * @throws SQLException + * if a database error occurs + */ + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + // can't take if characterEncoding isn't utf8 + if (!this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { + throw SQLError.createSQLException("Can not call setNClob() when connection character set isn't UTF-8", getExceptionInterceptor()); + } + + checkClosed(); + + if (reader == null) { + setNull(parameterIndex, java.sql.Types.NCLOB); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = reader; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = length; + } else { + binding.bindLength = -1; + } + } + } + + /** + * @see java.sql.PreparedStatement#setNString(int, java.lang.String) + */ + public void setNString(int parameterIndex, String x) throws SQLException { + if (this.charEncoding.equalsIgnoreCase("UTF-8") || this.charEncoding.equalsIgnoreCase("utf8")) { + setString(parameterIndex, x); + } else { + throw SQLError.createSQLException("Can not call setNString() when connection character set isn't UTF-8", getExceptionInterceptor()); + } + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x); + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java new file mode 100644 index 0000000..1510779 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java @@ -0,0 +1,587 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.sql.NClob; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.Field; +import com.mysql.jdbc.NotUpdatable; +import com.mysql.jdbc.RowData; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.UpdatableResultSet; + +public class JDBC4UpdatableResultSet extends UpdatableResultSet { + public JDBC4UpdatableResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + super(catalog, fields, tuples, conn, creatorStmt); + } + + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + throw new NotUpdatable(); + } + + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateClob(int columnIndex, Reader reader) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + updateNCharacterStream(columnIndex, x, (int) length); + + } + + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new NotUpdatable(); + } + + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + throw new NotUpdatable(); + + } + + public void updateRowId(int columnIndex, RowId x) throws SQLException { + throw new NotUpdatable(); + } + + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + updateAsciiStream(findColumn(columnLabel), x); + } + + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + updateAsciiStream(findColumn(columnLabel), x, length); + } + + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + updateBinaryStream(findColumn(columnLabel), x); + } + + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + updateBinaryStream(findColumn(columnLabel), x, length); + } + + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + updateBlob(findColumn(columnLabel), inputStream); + } + + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + updateBlob(findColumn(columnLabel), inputStream, length); + } + + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + updateCharacterStream(findColumn(columnLabel), reader); + } + + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + updateCharacterStream(findColumn(columnLabel), reader, length); + } + + public void updateClob(String columnLabel, Reader reader) throws SQLException { + updateClob(findColumn(columnLabel), reader); + } + + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + updateClob(findColumn(columnLabel), reader, length); + } + + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + updateNCharacterStream(findColumn(columnLabel), reader); + + } + + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + updateNCharacterStream(findColumn(columnLabel), reader, length); + } + + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + updateNClob(findColumn(columnLabel), reader); + + } + + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + updateNClob(findColumn(columnLabel), reader, length); + } + + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + updateSQLXML(findColumn(columnLabel), xmlObject); + + } + + /** + * JDBC 4.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + public synchronized void updateNCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call updateNCharacterStream() when field's character set isn't UTF-8"); + } + + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + ((com.mysql.jdbc.JDBC4PreparedStatement) this.updater).setNCharacterStream(columnIndex, x, length); + } else { + ((com.mysql.jdbc.JDBC4PreparedStatement) this.inserter).setNCharacterStream(columnIndex, x, length); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); + } + } + } + + /** + * JDBC 4.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param reader + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + public synchronized void updateNCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { + updateNCharacterStream(findColumn(columnName), reader, length); + } + + /** + * @see ResultSet#updateNClob(int, NClob) + */ + public void updateNClob(int columnIndex, java.sql.NClob nClob) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call updateNClob() when field's character set isn't UTF-8"); + } + + if (nClob == null) { + updateNull(columnIndex); + } else { + updateNCharacterStream(columnIndex, nClob.getCharacterStream(), (int) nClob.length()); + } + } + + /** + * @see ResultSet#updateClob(int, Clob) + */ + public void updateNClob(String columnName, java.sql.NClob nClob) throws SQLException { + updateNClob(findColumn(columnName), nClob); + } + + /** + * JDBC 4.0 Update a column with NATIONAL CHARACTER. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public synchronized void updateNString(int columnIndex, String x) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call updateNString() when field's character set isn't UTF-8"); + } + + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + ((com.mysql.jdbc.JDBC4PreparedStatement) this.updater).setNString(columnIndex, x); + } else { + ((com.mysql.jdbc.JDBC4PreparedStatement) this.inserter).setNString(columnIndex, x); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x, this.charConverter, fieldEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor())); + } + } + } + + /** + * JDBC 4.0 Update a column with NATIONAL CHARACTER. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public synchronized void updateNString(String columnName, String x) throws SQLException { + updateNString(findColumn(columnName), x); + } + + public int getHoldability() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + protected java.sql.NClob getNativeNClob(int columnIndex) throws SQLException { + String stringVal = getStringForNClob(columnIndex); + + if (stringVal == null) { + return null; + } + + return getNClobFromString(stringVal, columnIndex); + } + + /** + * JDBC 4.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnIndex + * the column to get the value from + * + * @return the value in the column as a java.io.Reader. + * + * @throws SQLException + * if an error occurs + */ + public Reader getNCharacterStream(int columnIndex) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNCharacterStream() when field's charset isn't UTF-8"); + } + + return getCharacterStream(columnIndex); + } + + /** + * JDBC 4.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnName + * the column name to retrieve the value from + * + * @return the value as a java.io.Reader + * + * @throws SQLException + * if an error occurs + */ + public Reader getNCharacterStream(String columnName) throws SQLException { + return getNCharacterStream(findColumn(columnName)); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + public NClob getNClob(int columnIndex) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNClob() when field's charset isn't UTF-8"); + } + + if (!this.isBinaryEncoded) { + String asString = getStringForNClob(columnIndex); + + if (asString == null) { + return null; + } + + return new com.mysql.jdbc.JDBC4NClob(asString, getExceptionInterceptor()); + } + + return getNativeNClob(columnIndex); + } + + /** + * JDBC 4.0 Get a NCLOB column. + * + * @param colName + * the column name + * + * @return an object representing a NCLOB + * + * @throws SQLException + * if an error occurs + */ + public NClob getNClob(String columnName) throws SQLException { + return getNClob(findColumn(columnName)); + } + + private final java.sql.NClob getNClobFromString(String stringVal, int columnIndex) throws SQLException { + return new com.mysql.jdbc.JDBC4NClob(stringVal, getExceptionInterceptor()); + } + + /** + * JDBC 4.0 + * + * Get the value of a column in the current row as a Java String + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value, null for SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public String getNString(int columnIndex) throws SQLException { + String fieldEncoding = this.fields[columnIndex - 1].getEncoding(); + + if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) { + throw new SQLException("Can not call getNString() when field's charset isn't UTF-8"); + } + + return getString(columnIndex); + } + + /** + * JDBC 4.0 + * + * The following routines simply convert the columnName into a columnIndex + * and then call the appropriate routine above. + * + * @param columnName + * is the SQL name of the column + * + * @return the column value + * + * @exception SQLException + * if a database access error occurs + */ + public String getNString(String columnName) throws SQLException { + return getNString(findColumn(columnName)); + } + + public RowId getRowId(int columnIndex) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public RowId getRowId(String columnLabel) throws SQLException { + return getRowId(findColumn(columnLabel)); + } + + public SQLXML getSQLXML(int columnIndex) throws SQLException { + return new JDBC4MysqlSQLXML(this, columnIndex, getExceptionInterceptor()); + } + + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return getSQLXML(findColumn(columnLabel)); + } + + private String getStringForNClob(int columnIndex) throws SQLException { + String asString = null; + + String forcedEncoding = "UTF-8"; + + try { + byte[] asBytes = null; + + if (!this.isBinaryEncoded) { + asBytes = getBytes(columnIndex); + } else { + asBytes = getNativeBytes(columnIndex, true); + } + + if (asBytes != null) { + asString = new String(asBytes, forcedEncoding); + } + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + return asString; + } + + public synchronized boolean isClosed() throws SQLException { + return this.isClosed; + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LicenseConfiguration.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LicenseConfiguration.java new file mode 100644 index 0000000..1042d43 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LicenseConfiguration.java @@ -0,0 +1,52 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Map; + +/** + * Used in commercially-licensed clients that require connections to commercially-licensed servers as part of the licensing terms. + */ +class LicenseConfiguration { + + /** + * Used in commercially-licensed clients that require connections to + * commercially-licensed servers as part of the licensing terms. + * + * @param serverVariables + * a Map of the output of 'show variables' from the server we're + * connecting to. + * + * @throws SQLException + * if commercial license is required, but not found + */ + static void checkLicenseType(Map serverVariables) throws SQLException { + // This is a GPL build, so we don't check anything... + } + + private LicenseConfiguration() { + // this is a static utility class + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalanceExceptionChecker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalanceExceptionChecker.java new file mode 100644 index 0000000..2c68978 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalanceExceptionChecker.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface LoadBalanceExceptionChecker extends Extension { + + /** + * Invoked to determine whether or a given SQLException should + * trigger a failover in a load-balanced deployment. + * + * The driver will not pass in a Connection instance when calling init(), but it + * will pass in the Properties, otherwise it acts like a normal Extension. + * + * One instance of a handler *per* JDBC connection instance will be created. If + * you need singleton-like behavior, you're on your own to provide it. + * + * @param ex + * @return true if the exception should trigger failover. + */ + public boolean shouldExceptionTriggerFailover(SQLException ex); + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedAutoCommitInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedAutoCommitInterceptor.java new file mode 100644 index 0000000..f43f37a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedAutoCommitInterceptor.java @@ -0,0 +1,122 @@ +/* + Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +public class LoadBalancedAutoCommitInterceptor implements StatementInterceptorV2 { + private int matchingAfterStatementCount = 0; + private int matchingAfterStatementThreshold = 0; + private String matchingAfterStatementRegex; + private ConnectionImpl conn; + private LoadBalancedConnectionProxy proxy = null; + + public void destroy() { + // do nothing here + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.StatementInterceptorV2#executeTopLevelOnly() + */ + public boolean executeTopLevelOnly() { + // always return false + return false; + } + + public void init(Connection connection, Properties props) throws SQLException { + this.conn = (ConnectionImpl) connection; + + String autoCommitSwapThresholdAsString = props.getProperty("loadBalanceAutoCommitStatementThreshold", "0"); + try { + this.matchingAfterStatementThreshold = Integer.parseInt(autoCommitSwapThresholdAsString); + } catch (NumberFormatException nfe) { + // nothing here, being handled in LoadBalancedConnectionProxy. + } + String autoCommitSwapRegex = props.getProperty("loadBalanceAutoCommitStatementRegex", ""); + if ("".equals(autoCommitSwapRegex)) { + return; + } + this.matchingAfterStatementRegex = autoCommitSwapRegex; + + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.StatementInterceptorV2#postProcess(java.lang.String, com.mysql.jdbc.Statement, com.mysql.jdbc.ResultSetInternalMethods, + * com.mysql.jdbc.Connection, int, boolean, boolean, java.sql.SQLException) + */ + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection, + int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException { + + // don't care if auto-commit is not enabled + if (!this.conn.getAutoCommit()) { + this.matchingAfterStatementCount = 0; + // auto-commit is enabled: + } else { + + if (this.proxy == null && this.conn.isProxySet()) { + MySQLConnection lcl_proxy = this.conn.getMultiHostSafeProxy(); + while (lcl_proxy != null && !(lcl_proxy instanceof LoadBalancedMySQLConnection)) { + lcl_proxy = lcl_proxy.getMultiHostSafeProxy(); + } + if (lcl_proxy != null) { + this.proxy = ((LoadBalancedMySQLConnection) lcl_proxy).getThisAsProxy(); + } + + } + + if (this.proxy != null) { + // increment the match count if no regex specified, or if matches: + if (this.matchingAfterStatementRegex == null || sql.matches(this.matchingAfterStatementRegex)) { + this.matchingAfterStatementCount++; + } + } + // trigger rebalance if count exceeds threshold: + if (this.matchingAfterStatementCount >= this.matchingAfterStatementThreshold) { + this.matchingAfterStatementCount = 0; + try { + if (this.proxy != null) { + this.proxy.pickNewConnection(); + } + + } catch (SQLException e) { + // eat this exception, the auto-commit statement completed, but we could not rebalance for some reason. User may get exception when using + // connection next. + } + } + } + // always return the original result set. + return originalResultSet; + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + // we do nothing before execution, it's unsafe to swap servers at this point. + return null; + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnection.java new file mode 100644 index 0000000..946d849 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnection.java @@ -0,0 +1,36 @@ +/* + Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface LoadBalancedConnection extends MySQLConnection { + public boolean addHost(String host) throws SQLException; + + public void removeHost(String host) throws SQLException; + + public void removeHostWhenNotInUse(String host) throws SQLException; + + void ping(boolean allConnections) throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnectionProxy.java new file mode 100644 index 0000000..4a88e2d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedConnectionProxy.java @@ -0,0 +1,844 @@ +/* + Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Executor; + +/** + * A proxy for a dynamic com.mysql.jdbc.Connection implementation that load balances requests across a series of MySQL JDBC connections, where the balancing + * takes place at transaction commit. + * + * Therefore, for this to work (at all), you must use transactions, even if only reading data. + * + * This implementation will invalidate connections that it detects have had communication errors when processing a request. Problematic hosts will be added to a + * global blacklist for loadBalanceBlacklistTimeout ms, after which they will be removed from the blacklist and made eligible once again to be selected for new + * connections. + * + * This implementation is thread-safe, but it's questionable whether sharing a connection instance amongst threads is a good idea, given that transactions are + * scoped to connections in JDBC. + */ +public class LoadBalancedConnectionProxy extends MultiHostConnectionProxy implements PingTarget { + private ConnectionGroup connectionGroup = null; + private long connectionGroupProxyID = 0; + + protected Map liveConnections; + private Map hostsToListIndexMap; + private Map connectionsToHostsMap; + private long totalPhysicalConnections = 0; + private long[] responseTimes; + + private int retriesAllDown; + private BalanceStrategy balancer; + private int autoCommitSwapThreshold = 0; + + public static final String BLACKLIST_TIMEOUT_PROPERTY_KEY = "loadBalanceBlacklistTimeout"; + private int globalBlacklistTimeout = 0; + private static Map globalBlacklist = new HashMap(); + public static final String HOST_REMOVAL_GRACE_PERIOD_PROPERTY_KEY = "loadBalanceHostRemovalGracePeriod"; + private int hostRemovalGracePeriod = 0; + // host:port pairs to be considered as removed (definitely blacklisted) from the original hosts list. + private Set hostsToRemove = new HashSet(); + + private boolean inTransaction = false; + private long transactionStartTime = 0; + private long transactionCount = 0; + + private LoadBalanceExceptionChecker exceptionChecker; + + private static Constructor JDBC_4_LB_CONNECTION_CTOR; + private static Class[] INTERFACES_TO_PROXY; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_LB_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4LoadBalancedMySQLConnection") + .getConstructor(new Class[] { LoadBalancedConnectionProxy.class }); + INTERFACES_TO_PROXY = new Class[] { LoadBalancedConnection.class, Class.forName("com.mysql.jdbc.JDBC4MySQLConnection") }; + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + INTERFACES_TO_PROXY = new Class[] { LoadBalancedConnection.class }; + } + } + + public static LoadBalancedConnection createProxyInstance(List hosts, Properties props) throws SQLException { + LoadBalancedConnectionProxy connProxy = new LoadBalancedConnectionProxy(hosts, props); + + return (LoadBalancedConnection) java.lang.reflect.Proxy.newProxyInstance(LoadBalancedConnection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy); + } + + /** + * Creates a proxy for java.sql.Connection that routes requests between the given list of host:port and uses the given properties when creating connections. + * + * @param hosts + * The list of the hosts to load balance. + * @param props + * Connection properties from where to get initial settings and to be used in new connections. + * @throws SQLException + */ + private LoadBalancedConnectionProxy(List hosts, Properties props) throws SQLException { + super(); + + String group = props.getProperty("loadBalanceConnectionGroup", null); + boolean enableJMX = false; + String enableJMXAsString = props.getProperty("loadBalanceEnableJMX", "false"); + try { + enableJMX = Boolean.parseBoolean(enableJMXAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceEnableJMX", new Object[] { enableJMXAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + if (group != null) { + this.connectionGroup = ConnectionGroupManager.getConnectionGroupInstance(group); + if (enableJMX) { + ConnectionGroupManager.registerJmx(); + } + this.connectionGroupProxyID = this.connectionGroup.registerConnectionProxy(this, hosts); + hosts = new ArrayList(this.connectionGroup.getInitialHosts()); + } + + // hosts specifications may have been reset with settings from a previous connection group + int numHosts = initializeHostsSpecs(hosts, props); + + this.liveConnections = new HashMap(numHosts); + this.hostsToListIndexMap = new HashMap(numHosts); + for (int i = 0; i < numHosts; i++) { + this.hostsToListIndexMap.put(this.hostList.get(i), i); + } + this.connectionsToHostsMap = new HashMap(numHosts); + this.responseTimes = new long[numHosts]; + + String retriesAllDownAsString = this.localProps.getProperty("retriesAllDown", "120"); + try { + this.retriesAllDown = Integer.parseInt(retriesAllDownAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForRetriesAllDown", new Object[] { retriesAllDownAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String blacklistTimeoutAsString = this.localProps.getProperty(BLACKLIST_TIMEOUT_PROPERTY_KEY, "0"); + try { + this.globalBlacklistTimeout = Integer.parseInt(blacklistTimeoutAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceBlacklistTimeout", new Object[] { blacklistTimeoutAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String hostRemovalGracePeriodAsString = this.localProps.getProperty(HOST_REMOVAL_GRACE_PERIOD_PROPERTY_KEY, "15000"); + try { + this.hostRemovalGracePeriod = Integer.parseInt(hostRemovalGracePeriodAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceHostRemovalGracePeriod", + new Object[] { hostRemovalGracePeriodAsString }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String strategy = this.localProps.getProperty("loadBalanceStrategy", "random"); + if ("random".equals(strategy)) { + this.balancer = (BalanceStrategy) Util.loadExtensions(null, props, "com.mysql.jdbc.RandomBalanceStrategy", "InvalidLoadBalanceStrategy", null) + .get(0); + } else if ("bestResponseTime".equals(strategy)) { + this.balancer = (BalanceStrategy) Util + .loadExtensions(null, props, "com.mysql.jdbc.BestResponseTimeBalanceStrategy", "InvalidLoadBalanceStrategy", null).get(0); + } else { + this.balancer = (BalanceStrategy) Util.loadExtensions(null, props, strategy, "InvalidLoadBalanceStrategy", null).get(0); + } + + String autoCommitSwapThresholdAsString = props.getProperty("loadBalanceAutoCommitStatementThreshold", "0"); + try { + this.autoCommitSwapThreshold = Integer.parseInt(autoCommitSwapThresholdAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementThreshold", + new Object[] { autoCommitSwapThresholdAsString }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String autoCommitSwapRegex = props.getProperty("loadBalanceAutoCommitStatementRegex", ""); + if (!("".equals(autoCommitSwapRegex))) { + try { + "".matches(autoCommitSwapRegex); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementRegex", new Object[] { autoCommitSwapRegex }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + } + + if (this.autoCommitSwapThreshold > 0) { + String statementInterceptors = this.localProps.getProperty("statementInterceptors"); + if (statementInterceptors == null) { + this.localProps.setProperty("statementInterceptors", "com.mysql.jdbc.LoadBalancedAutoCommitInterceptor"); + } else if (statementInterceptors.length() > 0) { + this.localProps.setProperty("statementInterceptors", statementInterceptors + ",com.mysql.jdbc.LoadBalancedAutoCommitInterceptor"); + } + props.setProperty("statementInterceptors", this.localProps.getProperty("statementInterceptors")); + + } + + this.balancer.init(null, props); + + String lbExceptionChecker = this.localProps.getProperty("loadBalanceExceptionChecker", "com.mysql.jdbc.StandardLoadBalanceExceptionChecker"); + this.exceptionChecker = (LoadBalanceExceptionChecker) Util.loadExtensions(null, props, lbExceptionChecker, "InvalidLoadBalanceExceptionChecker", null) + .get(0); + + pickNewConnection(); + } + + /** + * Wraps this object with a new load balanced Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + @Override + MySQLConnection getNewWrapperForThisAsConnection() throws SQLException { + if (Util.isJdbc4() || JDBC_4_LB_CONNECTION_CTOR != null) { + return (MySQLConnection) Util.handleNewInstance(JDBC_4_LB_CONNECTION_CTOR, new Object[] { this }, null); + } + return new LoadBalancedMySQLConnection(this); + } + + /** + * Propagates the connection proxy down through all live connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + @Override + protected void propagateProxyDown(MySQLConnection proxyConn) { + for (MySQLConnection c : this.liveConnections.values()) { + c.setProxy(proxyConn); + } + } + + /** + * Consults the registered LoadBalanceExceptionChecker if the given exception should trigger a connection fail-over. + * + * @param ex + * The Exception instance to check. + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + return t instanceof SQLException && this.exceptionChecker.shouldExceptionTriggerFailover((SQLException) t); + } + + /** + * Always returns 'true' as there are no "masters" and "slaves" in this type of connection. + */ + @Override + boolean isMasterConnection() { + return true; + } + + /** + * Closes specified connection and removes it from required mappings. + * + * @param conn + * @throws SQLException + */ + @Override + synchronized void invalidateConnection(MySQLConnection conn) throws SQLException { + super.invalidateConnection(conn); + + // add host to the global blacklist, if enabled + if (this.isGlobalBlacklistEnabled()) { + addToGlobalBlacklist(this.connectionsToHostsMap.get(conn)); + } + + // remove from liveConnections + this.liveConnections.remove(this.connectionsToHostsMap.get(conn)); + Object mappedHost = this.connectionsToHostsMap.remove(conn); + if (mappedHost != null && this.hostsToListIndexMap.containsKey(mappedHost)) { + int hostIndex = this.hostsToListIndexMap.get(mappedHost); + // reset the statistics for the host + synchronized (this.responseTimes) { + this.responseTimes[hostIndex] = 0; + } + } + } + + /** + * Picks the "best" connection to use for the next transaction based on the BalanceStrategy in use. + * + * @throws SQLException + */ + @Override + synchronized void pickNewConnection() throws SQLException { + if (this.isClosed && this.closedExplicitly) { + return; + } + + if (this.currentConnection == null) { // startup + this.currentConnection = this.balancer.pickConnection(this, Collections.unmodifiableList(this.hostList), + Collections.unmodifiableMap(this.liveConnections), this.responseTimes.clone(), this.retriesAllDown); + return; + } + + if (this.currentConnection.isClosed()) { + invalidateCurrentConnection(); + } + + int pingTimeout = this.currentConnection.getLoadBalancePingTimeout(); + boolean pingBeforeReturn = this.currentConnection.getLoadBalanceValidateConnectionOnSwapServer(); + + for (int hostsTried = 0, hostsToTry = this.hostList.size(); hostsTried < hostsToTry; hostsTried++) { + ConnectionImpl newConn = null; + try { + newConn = this.balancer.pickConnection(this, Collections.unmodifiableList(this.hostList), Collections.unmodifiableMap(this.liveConnections), + this.responseTimes.clone(), this.retriesAllDown); + + if (this.currentConnection != null) { + if (pingBeforeReturn) { + if (pingTimeout == 0) { + newConn.ping(); + } else { + newConn.pingInternal(true, pingTimeout); + } + } + + syncSessionState(this.currentConnection, newConn); + } + + this.currentConnection = newConn; + return; + + } catch (SQLException e) { + if (shouldExceptionTriggerConnectionSwitch(e) && newConn != null) { + // connection error, close up shop on current connection + invalidateConnection(newConn); + } + } + } + + // no hosts available to swap connection to, close up. + this.isClosed = true; + this.closedReason = "Connection closed after inability to pick valid new connection during load-balance."; + } + + /** + * Creates a new physical connection for the given host:port and updates required internal mappings and statistics for that connection. + * + * @param hostPortSpec + * The host:port specification. + * @throws SQLException + */ + @Override + public synchronized ConnectionImpl createConnectionForHost(String hostPortSpec) throws SQLException { + ConnectionImpl conn = super.createConnectionForHost(hostPortSpec); + + this.liveConnections.put(hostPortSpec, conn); + this.connectionsToHostsMap.put(conn, hostPortSpec); + + this.totalPhysicalConnections++; + + return conn; + } + + /** + * Closes all live connections. + */ + private synchronized void closeAllConnections() { + // close all underlying connections + for (MySQLConnection c : this.liveConnections.values()) { + try { + c.close(); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + this.balancer.destroy(); + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Closes all live connections. + */ + @Override + synchronized void doClose() { + closeAllConnections(); + } + + /** + * Aborts all live connections + */ + @Override + synchronized void doAbortInternal() { + // abort all underlying connections + for (MySQLConnection c : this.liveConnections.values()) { + try { + c.abortInternal(); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + this.balancer.destroy(); + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Aborts all live connections, using the provided Executor. + */ + @Override + synchronized void doAbort(Executor executor) { + // close all underlying connections + for (MySQLConnection c : this.liveConnections.values()) { + try { + c.abort(executor); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + this.balancer.destroy(); + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Proxies method invocation on the java.sql.Connection interface, trapping "close", "isClosed" and "commit/rollback" to switch connections for load + * balancing. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (this.isClosed && !allowedOnClosedConnection(method) && method.getExceptionTypes().length > 0) { + if (this.autoReconnect && !this.closedExplicitly) { + // try to reconnect first! + this.currentConnection = null; + pickNewConnection(); + this.isClosed = false; + this.closedReason = null; + } else { + String reason = "No operations allowed after connection closed."; + if (this.closedReason != null) { + reason += " " + this.closedReason; + } + throw SQLError.createSQLException(reason, SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null /* no access to a interceptor here... */); + } + } + + if (!this.inTransaction) { + this.inTransaction = true; + this.transactionStartTime = System.nanoTime(); + this.transactionCount++; + } + + Object result = null; + + try { + result = method.invoke(this.thisAsConnection, args); + + if (result != null) { + if (result instanceof com.mysql.jdbc.Statement) { + ((com.mysql.jdbc.Statement) result).setPingTarget(this); + } + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } + + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + + } finally { + if ("commit".equals(methodName) || "rollback".equals(methodName)) { + this.inTransaction = false; + + // Update stats + String host = this.connectionsToHostsMap.get(this.currentConnection); + // avoid NPE if the connection has already been removed from connectionsToHostsMap in invalidateCurrenctConnection() + if (host != null) { + synchronized (this.responseTimes) { + Integer hostIndex = (this.hostsToListIndexMap.get(host)); + + if (hostIndex != null && hostIndex < this.responseTimes.length) { + this.responseTimes[hostIndex] = System.nanoTime() - this.transactionStartTime; + } + } + } + pickNewConnection(); + } + } + + return result; + } + + /** + * Pings live connections. + */ + public synchronized void doPing() throws SQLException { + SQLException se = null; + boolean foundHost = false; + int pingTimeout = this.currentConnection.getLoadBalancePingTimeout(); + + for (Iterator i = this.hostList.iterator(); i.hasNext();) { + String host = i.next(); + ConnectionImpl conn = this.liveConnections.get(host); + if (conn == null) { + continue; + } + try { + if (pingTimeout == 0) { + conn.ping(); + } else { + conn.pingInternal(true, pingTimeout); + } + foundHost = true; + } catch (SQLException e) { + // give up if it is the current connection, otherwise NPE faking resultset later. + if (host.equals(this.connectionsToHostsMap.get(this.currentConnection))) { + // clean up underlying connections, since connection pool won't do it + closeAllConnections(); + this.isClosed = true; + this.closedReason = "Connection closed because ping of current connection failed."; + throw e; + } + + // if the Exception is caused by ping connection lifetime checks, don't add to blacklist + if (e.getMessage().equals(Messages.getString("Connection.exceededConnectionLifetime"))) { + // only set the return Exception if it's null + if (se == null) { + se = e; + } + } else { + // overwrite the return Exception no matter what + se = e; + if (isGlobalBlacklistEnabled()) { + addToGlobalBlacklist(host); + } + } + // take the connection out of the liveConnections Map + this.liveConnections.remove(this.connectionsToHostsMap.get(conn)); + } + } + + // if there were no successful pings + if (!foundHost) { + closeAllConnections(); + this.isClosed = true; + this.closedReason = "Connection closed due to inability to ping any active connections."; + // throw the stored Exception, if exists + if (se != null) { + throw se; + } + // or create a new SQLException and throw it, must be no liveConnections + ((ConnectionImpl) this.currentConnection).throwConnectionClosedException(); + } + } + + /** + * Adds a host to the blacklist with the given timeout. + * + * @param host + * The host to be blacklisted. + * @param timeout + * The blacklist timeout for this entry. + */ + public void addToGlobalBlacklist(String host, long timeout) { + if (isGlobalBlacklistEnabled()) { + synchronized (globalBlacklist) { + globalBlacklist.put(host, timeout); + } + } + } + + /** + * Adds a host to the blacklist. + * + * @param host + * The host to be blacklisted. + */ + public void addToGlobalBlacklist(String host) { + addToGlobalBlacklist(host, System.currentTimeMillis() + this.globalBlacklistTimeout); + } + + /** + * Checks if host blacklist management was enabled. + */ + public boolean isGlobalBlacklistEnabled() { + return (this.globalBlacklistTimeout > 0); + } + + /** + * Returns a local hosts blacklist, while cleaning up expired records from the global blacklist, or a blacklist with the hosts to be removed. + * + * @return + * A local hosts blacklist. + */ + public synchronized Map getGlobalBlacklist() { + if (!isGlobalBlacklistEnabled()) { + if (this.hostsToRemove.isEmpty()) { + return new HashMap(1); + } + HashMap fakedBlacklist = new HashMap(); + for (String h : this.hostsToRemove) { + fakedBlacklist.put(h, System.currentTimeMillis() + 5000); + } + return fakedBlacklist; + } + + // Make a local copy of the blacklist + Map blacklistClone = new HashMap(globalBlacklist.size()); + // Copy everything from synchronized global blacklist to local copy for manipulation + synchronized (globalBlacklist) { + blacklistClone.putAll(globalBlacklist); + } + Set keys = blacklistClone.keySet(); + + // We're only interested in blacklisted hosts that are in the hostList + keys.retainAll(this.hostList); + + // Don't need to synchronize here as we using a local copy + for (Iterator i = keys.iterator(); i.hasNext();) { + String host = i.next(); + // OK if null is returned because another thread already purged Map entry. + Long timeout = globalBlacklist.get(host); + if (timeout != null && timeout < System.currentTimeMillis()) { + // Timeout has expired, remove from blacklist + synchronized (globalBlacklist) { + globalBlacklist.remove(host); + } + i.remove(); + } + + } + if (keys.size() == this.hostList.size()) { + // return an empty blacklist, let the BalanceStrategy implementations try to connect to everything since it appears that all hosts are + // unavailable - we don't want to wait for loadBalanceBlacklistTimeout to expire. + return new HashMap(1); + } + + return blacklistClone; + } + + /** + * Removes a host from the host list, allowing it some time to be released gracefully if needed. + * + * @param hostPortPair + * The host to be removed. + * @throws SQLException + */ + public void removeHostWhenNotInUse(String hostPortPair) throws SQLException { + if (this.hostRemovalGracePeriod <= 0) { + removeHost(hostPortPair); + return; + } + + int timeBetweenChecks = this.hostRemovalGracePeriod > 1000 ? 1000 : this.hostRemovalGracePeriod; + + synchronized (this) { + addToGlobalBlacklist(hostPortPair, System.currentTimeMillis() + this.hostRemovalGracePeriod + timeBetweenChecks); + + long cur = System.currentTimeMillis(); + + while (System.currentTimeMillis() < cur + this.hostRemovalGracePeriod) { + this.hostsToRemove.add(hostPortPair); + + if (!hostPortPair.equals(this.currentConnection.getHostPortPair())) { + removeHost(hostPortPair); + return; + } + + try { + Thread.sleep(timeBetweenChecks); + } catch (InterruptedException e) { + // better to swallow this and retry. + } + } + } + + removeHost(hostPortPair); + } + + /** + * Removes a host from the host list. + * + * @param hostPortPair + * The host to be removed. + * @throws SQLException + */ + public synchronized void removeHost(String hostPortPair) throws SQLException { + if (this.connectionGroup != null) { + if (this.connectionGroup.getInitialHosts().size() == 1 && this.connectionGroup.getInitialHosts().contains(hostPortPair)) { + throw SQLError.createSQLException("Cannot remove only configured host.", null); + } + } + + this.hostsToRemove.add(hostPortPair); + + this.connectionsToHostsMap.remove(this.liveConnections.remove(hostPortPair)); + if (this.hostsToListIndexMap.remove(hostPortPair) != null) { + long[] newResponseTimes = new long[this.responseTimes.length - 1]; + int newIdx = 0; + for (String h : this.hostList) { + if (!this.hostsToRemove.contains(h)) { + Integer idx = this.hostsToListIndexMap.get(h); + if (idx != null && idx < this.responseTimes.length) { + newResponseTimes[newIdx] = this.responseTimes[idx]; + } + this.hostsToListIndexMap.put(h, newIdx++); + } + } + this.responseTimes = newResponseTimes; + } + + if (hostPortPair.equals(this.currentConnection.getHostPortPair())) { + invalidateConnection(this.currentConnection); + pickNewConnection(); + } + } + + /** + * Adds a host to the hosts list. + * + * @param hostPortPair + * The host to be added. + */ + public synchronized boolean addHost(String hostPortPair) { + if (this.hostsToListIndexMap.containsKey(hostPortPair)) { + return false; + } + + long[] newResponseTimes = new long[this.responseTimes.length + 1]; + System.arraycopy(this.responseTimes, 0, newResponseTimes, 0, this.responseTimes.length); + + this.responseTimes = newResponseTimes; + if (!this.hostList.contains(hostPortPair)) { + this.hostList.add(hostPortPair); + } + this.hostsToListIndexMap.put(hostPortPair, this.responseTimes.length - 1); + this.hostsToRemove.remove(hostPortPair); + + return true; + } + + public synchronized boolean inTransaction() { + return this.inTransaction; + } + + public synchronized long getTransactionCount() { + return this.transactionCount; + } + + public synchronized long getActivePhysicalConnectionCount() { + return this.liveConnections.size(); + } + + public synchronized long getTotalPhysicalConnectionCount() { + return this.totalPhysicalConnections; + } + + public synchronized long getConnectionGroupProxyID() { + return this.connectionGroupProxyID; + } + + public synchronized String getCurrentActiveHost() { + MySQLConnection c = this.currentConnection; + if (c != null) { + Object o = this.connectionsToHostsMap.get(c); + if (o != null) { + return o.toString(); + } + } + return null; + } + + public synchronized long getCurrentTransactionDuration() { + if (this.inTransaction && this.transactionStartTime > 0) { + return System.nanoTime() - this.transactionStartTime; + } + return 0; + } + + /** + * A LoadBalancedConnection proxy that provides null-functionality. It can be used as a replacement of the null keyword in the places where a + * LoadBalancedConnection object cannot be effectively null because that would be a potential source of NPEs. + */ + private static class NullLoadBalancedConnectionProxy implements InvocationHandler { + public NullLoadBalancedConnectionProxy() { + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + SQLException exceptionToThrow = SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.unusableConnection"), + SQLError.SQL_STATE_INVALID_TRANSACTION_STATE, MysqlErrorNumbers.ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION, true, null); + Class[] declaredException = method.getExceptionTypes(); + for (Class declEx : declaredException) { + if (declEx.isAssignableFrom(exceptionToThrow.getClass())) { + throw exceptionToThrow; + } + } + throw new IllegalStateException(exceptionToThrow.getMessage(), exceptionToThrow); + } + } + + private static LoadBalancedConnection nullLBConnectionInstance = null; + + static synchronized LoadBalancedConnection getNullLoadBalancedConnectionInstance() { + if (nullLBConnectionInstance == null) { + nullLBConnectionInstance = (LoadBalancedConnection) java.lang.reflect.Proxy.newProxyInstance(LoadBalancedConnection.class.getClassLoader(), + INTERFACES_TO_PROXY, new NullLoadBalancedConnectionProxy()); + } + return nullLBConnectionInstance; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedMySQLConnection.java new file mode 100644 index 0000000..34577c0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LoadBalancedMySQLConnection.java @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public class LoadBalancedMySQLConnection extends MultiHostMySQLConnection implements LoadBalancedConnection { + + public LoadBalancedMySQLConnection(LoadBalancedConnectionProxy proxy) { + super(proxy); + } + + @Override + protected LoadBalancedConnectionProxy getThisAsProxy() { + return (LoadBalancedConnectionProxy) super.getThisAsProxy(); + } + + @Override + public void close() throws SQLException { + getThisAsProxy().doClose(); + } + + @Override + public void ping() throws SQLException { + ping(true); + } + + public void ping(boolean allConnections) throws SQLException { + if (allConnections) { + getThisAsProxy().doPing(); + } else { + getActiveMySQLConnection().ping(); + } + } + + public boolean addHost(String host) throws SQLException { + return getThisAsProxy().addHost(host); + } + + public void removeHost(String host) throws SQLException { + getThisAsProxy().removeHost(host); + } + + public void removeHostWhenNotInUse(String host) throws SQLException { + getThisAsProxy().removeHostWhenNotInUse(host); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LocalizedErrorMessages.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LocalizedErrorMessages.properties new file mode 100644 index 0000000..020828e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/LocalizedErrorMessages.properties @@ -0,0 +1,709 @@ +# +# Fixed +# + +ResultSet.Retrieved__1=Retrieved +ResultSet.Bad_format_for_BigDecimal=Bad format for BigDecimal ''{0}'' in column {1}. +ResultSet.Bad_format_for_BigInteger=Bad format for BigInteger ''{0}'' in column {1}. +ResultSet.Column_Index_out_of_range_low=Column Index out of range, {0} < 1. +ResultSet.Column_Index_out_of_range_high=Column Index out of range, {0} > {1}. +ResultSet.Value_is_out_of_range=Value ''{0}'' is out of range [{1}, {2}]. +ResultSet.Positioned_Update_not_supported=Positioned Update not supported. +ResultSet.Bad_format_for_Date=Bad format for DATE ''{0}'' in column {1}. +ResultSet.Bad_format_for_Column=Bad format for {0} ''{1}'' in column {2} ({3}). +ResultSet.Bad_format_for_number=Bad format for number ''{0}'' in column {1}. +ResultSet.Illegal_operation_on_empty_result_set=Illegal operation on empty result set. + +Statement.0=Connection is closed. +Statement.2=Unsupported character encoding ''{0}'' +Statement.5=Illegal value for setFetchDirection(). +Statement.7=Illegal value for setFetchSize(). +Statement.11=Illegal value for setMaxFieldSize(). +Statement.13=Can not set max field size > max allowed packet of {0} bytes. +Statement.15=setMaxRows() out of range. +Statement.19=Illegal flag for getMoreResults(int). +Statement.21=Illegal value for setQueryTimeout(). +Statement.27=Connection is read-only. +Statement.28=Queries leading to data modification are not allowed. +Statement.34=Connection is read-only. +Statement.35=Queries leading to data modification are not allowed. +Statement.40=Can not issue INSERT/UPDATE/DELETE with executeQuery(). +Statement.42=Connection is read-only. +Statement.43=Queries leading to data modification are not allowed. +Statement.46=Can not issue SELECT via executeUpdate() or executeLargeUpdate(). +Statement.49=No operations allowed after statement closed. +Statement.57=Can not issue data manipulation statements with executeQuery(). +Statement.59=Can not issue NULL query. +Statement.61=Can not issue empty query. +Statement.63=Statement not closed explicitly. You should call close() on created +Statement.64=Statement instances from your code to be more efficient. +Statement.GeneratedKeysNotRequested=Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate(), Statement.executeLargeUpdate() or Connection.prepareStatement(). +Statement.ConnectionKilledDueToTimeout=Connection closed to due to statement timeout being reached and "queryTimeoutKillsConnection" being set to "true". +UpdatableResultSet.1=Can not call deleteRow() when on insert row. +UpdatableResultSet.2=Can not call deleteRow() on empty result set. +UpdatableResultSet.3=Before start of result set. Can not call deleteRow(). +UpdatableResultSet.4=After end of result set. Can not call deleteRow(). +UpdatableResultSet.7=Not on insert row. +UpdatableResultSet.8=Can not call refreshRow() when on insert row. +UpdatableResultSet.9=Can not call refreshRow() on empty result set. +UpdatableResultSet.10=Before start of result set. Can not call refreshRow(). +UpdatableResultSet.11=After end of result set. Can not call refreshRow(). +UpdatableResultSet.12=refreshRow() called on row that has been deleted or had primary key changed. +UpdatableResultSet.34=Updatable result set created, but never updated. You should only create updatable result sets when you want to update/insert/delete values using the updateRow(), deleteRow() and insertRow() methods. +UpdatableResultSet.43=Can not create updatable result sets when there is no currently selected database and MySQL server version < 4.1. +UpdatableResultSet.44=Can not call updateRow() when on insert row. + +# +# Possible re-names +# + +ResultSet.Query_generated_no_fields_for_ResultSet_57=Query generated no fields for ResultSet +ResultSet.Illegal_value_for_fetch_direction_64=Illegal value for fetch direction +ResultSet.Value_must_be_between_0_and_getMaxRows()_66=Value must be between 0 and getMaxRows() +ResultSet.Query_generated_no_fields_for_ResultSet_99=Query generated no fields for ResultSet +ResultSet.Operation_not_allowed_after_ResultSet_closed_144=Operation not allowed after ResultSet closed +ResultSet.Before_start_of_result_set_146=Before start of result set +ResultSet.After_end_of_result_set_148=After end of result set +ResultSet.Query_generated_no_fields_for_ResultSet_133=Query generated no fields for ResultSet +ResultSet.ResultSet_is_from_UPDATE._No_Data_115=ResultSet is from UPDATE. No Data. +ResultSet.N/A_159=N/A + +# +# To fix +# + +ResultSet.Invalid_value_for_getFloat()_-____68=Invalid value for getFloat() - \' +ResultSet.Invalid_value_for_getInt()_-____74=Invalid value for getInt() - \' +ResultSet.Invalid_value_for_getLong()_-____79=Invalid value for getLong() - \' +ResultSet.Invalid_value_for_getFloat()_-____200=Invalid value for getFloat() - \' +ResultSet.___in_column__201=\' in column +ResultSet.Invalid_value_for_getInt()_-____206=Invalid value for getInt() - \' +ResultSet.___in_column__207=\' in column +ResultSet.Invalid_value_for_getLong()_-____211=Invalid value for getLong() - \' +ResultSet.___in_column__212=\' in column +ResultSet.Invalid_value_for_getShort()_-____217=Invalid value for getShort() - \' +ResultSet.___in_column__218=\' in column + +ResultSet.Class_not_found___91=Class not found: +ResultSet._while_reading_serialized_object_92=\ while reading serialized object + +ResultSet.Invalid_value_for_getShort()_-____96=Invalid value for getShort() - \' +ResultSet.Unsupported_character_encoding____101=Unsupported character encoding \' + +ResultSet.Malformed_URL____104=Malformed URL \' +ResultSet.Malformed_URL____107=Malformed URL \' +ResultSet.Malformed_URL____141=Malformed URL \' + +ResultSet.Column____112=Column \' +ResultSet.___not_found._113=\' not found. + +ResultSet.Unsupported_character_encoding____135=Unsupported character encoding \' +ResultSet.Unsupported_character_encoding____138=Unsupported character encoding \' + +# +# Usage advisor messages for ResultSets +# + +ResultSet.ResultSet_implicitly_closed_by_driver=ResultSet implicitly closed by driver.\n\nYou should close ResultSets explicitly from your code to free up resources in a more efficient manner. +ResultSet.Possible_incomplete_traversal_of_result_set=Possible incomplete traversal of result set. Cursor was left on row {0} of {1} rows when it was closed.\n\nYou should consider re-formulating your query to return only the rows you are interested in using. +ResultSet.The_following_columns_were_never_referenced=The following columns were part of the SELECT statement for this result set, but were never referenced: +ResultSet.Too_Large_Result_Set=Result set size of {0} rows is larger than \"resultSetSizeThreshold\" of {1} rows. Application may be requesting more data than it is using. Consider reformulating the query. +ResultSet.CostlyConversion=ResultSet type conversion via parsing detected when calling {0} for column {1} (column named ''{2}'') in table ''{3}''{4}\n\nJava class of column type is ''{5}'', MySQL field type is ''{6}''.\n\nTypes that could be converted directly without parsing are:\n{7} +ResultSet.CostlyConversionCreatedFromQuery= created from query:\n\n + +ResultSet.Value____173=Value \' +ResultSetMetaData.46=Column index out of range. +ResultSet.___is_out_of_range_[-127,127]_174=\' is out of range [-127,127] +ResultSet.Bad_format_for_Date____180=Bad format for Date \' + +ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__223=Timestamp too small to convert to Time value in column +ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__227=Precision lost converting TIMESTAMP to Time with getTime() on column +ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__230=Precision lost converting DATETIME to Time with getTime() on column +ResultSet.Bad_format_for_Time____233=Bad format for Time \' +ResultSet.___in_column__234=\' in column +ResultSet.Bad_format_for_Timestamp____244=Bad format for Timestamp \' +ResultSet.___in_column__245=\' in column +ResultSet.Cannot_convert_value____249=Cannot convert value \' +ResultSet.___from_column__250=\' from column +ResultSet._)_to_TIMESTAMP._252=\ ) to TIMESTAMP. +ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257=Timestamp too small to convert to Time value in column +ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261=Precision lost converting TIMESTAMP to Time with getTime() on column +ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264=Precision lost converting DATETIME to Time with getTime() on column +ResultSet.Bad_format_for_Time____267=Bad format for Time \' +ResultSet.___in_column__268=\' in column +ResultSet.Bad_format_for_Timestamp____278=Bad format for Timestamp \' +ResultSet.___in_column__279=\' in column +ResultSet.Cannot_convert_value____283=Cannot convert value \' +ResultSet.___from_column__284=\' from column +ResultSet._)_to_TIMESTAMP._286=\ ) to TIMESTAMP. +CallableStatement.2=Parameter name can not be NULL or zero-length. +CallableStatement.3=No parameter named ' +CallableStatement.4=' +CallableStatement.5=Parameter named ' +CallableStatement.6=' is not an OUT parameter +CallableStatement.7=No output parameters registered. +CallableStatement.8=No output parameters returned by procedure. +CallableStatement.9=Parameter number +CallableStatement.10=\ is not an OUT parameter +CallableStatement.11=Parameter index of +CallableStatement.12=\ is out of range (1, +CallableStatement.13=) +CallableStatement.14=Can not use streaming result sets with callable statements that have output parameters +CallableStatement.1=Unable to retrieve metadata for procedure. +CallableStatement.0=Parameter name can not be +CallableStatement.15=null. +CallableStatement.16=empty. +CallableStatement.21=Parameter +CallableStatement.22=\ is not registered as an output parameter +CommunicationsException.2=\ is longer than the server configured value of +CommunicationsException.3='wait_timeout' +CommunicationsException.4='interactive_timeout' +CommunicationsException.5=may or may not be greater than the server-side timeout +CommunicationsException.6=(the driver was unable to determine the value of either the +CommunicationsException.7='wait_timeout' or 'interactive_timeout' configuration values from +CommunicationsException.8=the server. +CommunicationsException.11=. You should consider either expiring and/or testing connection validity +CommunicationsException.12=before use in your application, increasing the server configured values for client timeouts, +CommunicationsException.13=or using the Connector/J connection property 'autoReconnect=true' to avoid this problem. +CommunicationsException.TooManyClientConnections=The driver was unable to create a connection due to an inability to establish the client portion of a socket.\n\nThis is usually caused by a limit on the number of sockets imposed by the operating system. This limit is usually configurable. \n\nFor Unix-based platforms, see the manual page for the 'ulimit' command. Kernel or system reconfiguration may also be required.\n\nFor Windows-based platforms, see Microsoft Knowledge Base Article 196271 (Q196271). +CommunicationsException.LocalSocketAddressNotAvailable=The configuration parameter \"localSocketAddress\" has been set to a network interface not available for use by the JVM. +CommunicationsException.20=Communications link failure +CommunicationsException.ClientWasStreaming=Application was streaming results when the connection failed. Consider raising value of 'net_write_timeout' on the server. +CommunicationsException.ServerPacketTimingInfoNoRecv=The last packet sent successfully to the server was {0} milliseconds ago. The driver has not received any packets from the server. +CommunicationsException.ServerPacketTimingInfo=The last packet successfully received from the server was {0} milliseconds ago. The last packet sent successfully to the server was {1} milliseconds ago. +CommunicationsException.TooManyAuthenticationPluginNegotiations=Too many authentication plugin negotiations. +NonRegisteringDriver.1=The url cannot be null +NonRegisteringDriver.3=Hostname of MySQL Server +NonRegisteringDriver.7=Port number of MySQL Server +NonRegisteringDriver.13=Username to authenticate as +NonRegisteringDriver.16=Password to use for authentication +NonRegisteringDriver.17=Cannot load connection class because of underlying exception: ' +NonRegisteringDriver.18='. +NonRegisteringDriver.37=Must specify port after ':' in connection string +MysqlDataSource.BadUrl=Failed to get a connection using the URL ''{0}''. +SQLError.35=Disconnect error +SQLError.36=Data truncated +SQLError.37=Privilege not revoked +SQLError.38=Invalid connection string attribute +SQLError.39=Error in row +SQLError.40=No rows updated or deleted +SQLError.41=More than one row updated or deleted +SQLError.42=Wrong number of parameters +SQLError.43=Unable to connect to data source +SQLError.44=Connection in use +SQLError.45=Connection not open +SQLError.46=Data source rejected establishment of connection +SQLError.47=Connection failure during transaction +SQLError.48=Communication link failure +SQLError.49=Insert value list does not match column list +SQLError.50=Numeric value out of range +SQLError.51=Datetime field overflow +SQLError.52=Division by zero +SQLError.53=Deadlock found when trying to get lock; Try restarting transaction +SQLError.54=Invalid authorization specification +SQLError.55=Syntax error or access violation +SQLError.56=Base table or view not found +SQLError.57=Base table or view already exists +SQLError.58=Base table not found +SQLError.59=Index already exists +SQLError.60=Index not found +SQLError.61=Column already exists +SQLError.62=Column not found +SQLError.63=No default for column +SQLError.64=General error +SQLError.65=Memory allocation failure +SQLError.66=Invalid column number +SQLError.67=Invalid argument value +SQLError.68=Driver not capable +SQLError.69=Timeout expired +ChannelBuffer.0=Unsupported character encoding ' +ChannelBuffer.1=' +Field.12=Unsupported character encoding ' +Field.13=' + +Blob.0=indexToWriteAt must be >= 1 +Blob.1=IO Error while writing bytes to blob +Blob.2=Position 'pos' can not be < 1 +Blob.invalidStreamLength=Requested stream length of {2} is out of range, given blob length of {0} and starting position of {1}. +Blob.invalidStreamPos=Position 'pos' can not be < 1 or > blob length. + +StringUtils.0=Unsupported character encoding ' +StringUtils.1='. +StringUtils.5=Unsupported character encoding ' +StringUtils.6='. +StringUtils.10=Unsupported character encoding ' +StringUtils.11='. +StringUtils.15=Illegal argument value {0} for openingMarkers and/or {1} for closingMarkers. These cannot be null and must have the same length. +RowDataDynamic.2=WARN: Possible incomplete traversal of result set. Streaming result set had +RowDataDynamic.3=\ rows left to read when it was closed. +RowDataDynamic.4=\n\nYou should consider re-formulating your query to +RowDataDynamic.5=return only the rows you are interested in using. +RowDataDynamic.6=\n\nResultSet was created at: +RowDataDynamic.7=\n\nNested Stack Trace:\n +RowDataDynamic.8=Error retrieving record: Unexpected Exception: +RowDataDynamic.9=\ message given: +RowDataDynamic.10=Operation not supported for streaming result sets +Clob.0=indexToWriteAt must be >= 1 +Clob.1=indexToWriteAt must be >= 1 +Clob.2=Starting position can not be < 1 +Clob.3=String to set can not be NULL +Clob.4=Starting position can not be < 1 +Clob.5=String to set can not be NULL +Clob.6=CLOB start position can not be < 1 +Clob.7=CLOB start position + length can not be > length of CLOB +Clob.8=Illegal starting position for search, ' +Clob.9=' +Clob.10=Starting position for search is past end of CLOB +Clob.11=Cannot truncate CLOB of length +Clob.12=\ to length of +Clob.13=. +PacketTooBigException.0=Packet for query is too large ( +PacketTooBigException.1=\ > +PacketTooBigException.2=). +PacketTooBigException.3=You can change this value on the server by setting the +PacketTooBigException.4=max_allowed_packet' variable. +Util.1=\n\n** BEGIN NESTED EXCEPTION ** \n\n +Util.2=\nMESSAGE: +Util.3=\n\nSTACKTRACE:\n\n +Util.4=\n\n** END NESTED EXCEPTION **\n\n +MiniAdmin.0=Conection can not be null. +MiniAdmin.1=MiniAdmin can only be used with MySQL connections +NamedPipeSocketFactory.2=Can not specify NULL or empty value for property ' +NamedPipeSocketFactory.3='. +NamedPipeSocketFactory.4=Named pipe path can not be null or empty +MysqlIO.1=Unexpected end of input stream +MysqlIO.2=Reading packet of length +MysqlIO.3=\nPacket header:\n +MysqlIO.4=readPacket() payload:\n +MysqlIO.8=Slow query explain results for ' +MysqlIO.9=' :\n\n +MysqlIO.10=\ message from server: " +MysqlIO.15=SSL Connection required, but not supported by server. +MysqlIO.17=Attempt to close streaming result set +MysqlIO.18=\ when no streaming result set was registered. This is an internal error. +MysqlIO.19=Attempt to close streaming result set +MysqlIO.20=\ that was not registered. +MysqlIO.21=\ Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on +MysqlIO.22=\ any active result sets before attempting more queries. +MysqlIO.23=Can not use streaming results with multiple result statements +MysqlIO.25=\ ... (truncated) +MysqlIO.SlowQuery=Slow query (exceeded {0} {1}, duration: {2} {1}): +MysqlIO.ServerSlowQuery=The server processing the query has indicated that the query was marked "slow". +Nanoseconds=ns +Milliseconds=ms +MysqlIO.28=Not issuing EXPLAIN for query of size > +MysqlIO.29=\ bytes. +MysqlIO.33=The following query was executed with a bad index, use 'EXPLAIN' for more details: +MysqlIO.35=The following query was executed using no index, use 'EXPLAIN' for more details: +MysqlIO.36=\n\nLarge packet dump truncated at +MysqlIO.37=\ bytes. +MysqlIO.39=Streaming result set +MysqlIO.40=\ is still active. +MysqlIO.41=\ No statements may be issued when any streaming result sets are open and in use on a given connection. +MysqlIO.42=\ Ensure that you have called .close() on any active streaming result sets before attempting more queries. +MysqlIO.43=Unexpected end of input stream +MysqlIO.44=Reading reusable packet of length +MysqlIO.45=\nPacket header:\n +MysqlIO.46=reuseAndReadPacket() payload:\n +MysqlIO.47=Unexpected end of input stream +MysqlIO.48=Unexpected end of input stream +MysqlIO.49=Packets received out of order +MysqlIO.50=Short read from server, expected +MysqlIO.51=\ bytes, received only +MysqlIO.57=send() compressed packet:\n +MysqlIO.58=\n\nOriginal packet (uncompressed):\n +MysqlIO.59=send() packet payload:\n +MysqlIO.60=Unable to open file +MysqlIO.63=for 'LOAD DATA LOCAL INFILE' command. +MysqlIO.64=Due to underlying IOException: +MysqlIO.65=Unable to close local file during LOAD DATA LOCAL INFILE command +MysqlIO.68=\ message from server: " +MysqlIO.70=Unknown column +MysqlIO.72=\ message from server: " +MysqlIO.75=No name specified for socket factory +MysqlIO.76=Could not create socket factory ' +MysqlIO.77=' due to underlying exception: +MysqlIO.79=Unexpected end of input stream +MysqlIO.80=Unexpected end of input stream +MysqlIO.81=Unexpected end of input stream +MysqlIO.82=Unexpected end of input stream +MysqlIO.83=Packets received out of order +MysqlIO.84=Packets received out of order +MysqlIO.85=Unexpected end of input stream +MysqlIO.86=Unexpected end of input stream +MysqlIO.87=Unexpected end of input stream +MysqlIO.88=Packets received out of order +MysqlIO.89=Packets received out of order +MysqlIO.91=Failed to create message digest 'SHA-1' for authentication. +MysqlIO.92=\ You must use a JDK that supports JCE to be able to use secure connection authentication +MysqlIO.97=Unknown type ' +MysqlIO.98=\ in column +MysqlIO.99=\ of +MysqlIO.100=\ in binary-encoded result set. +MysqlIO.102=, underlying cause: +MysqlIO.103=Unexpected packet length +MysqlIO.EOF=Can not read response from server. Expected to read {0} bytes, read {1} bytes before connection was unexpectedly lost. +MysqlIO.NoInnoDBStatusFound=No InnoDB status output returned by server. +MysqlIO.InnoDBStatusFailed=Couldn't retrieve InnoDB status due to underlying exception: +MysqlIO.LoadDataLocalNotAllowed=Server asked for stream in response to LOAD DATA LOCAL INFILE but functionality is disabled at client by 'allowLoadLocalInfile' being set to 'false'. +MysqlIO.SSLWarning=Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. +NotImplemented.0=Feature not implemented +UnsupportedSQLType.0=Unsupported SQL type: +PreparedStatement.0=SQL String can not be NULL +PreparedStatement.1=SQL String can not be NULL +PreparedStatement.2=Parameter index out of range ( +PreparedStatement.3=\ > +PreparedStatement.4=) +PreparedStatement.16=Unknown Types value +PreparedStatement.17=Cannot convert +PreparedStatement.18=\ to SQL type requested due to +PreparedStatement.19=\ - +PreparedStatement.20=Connection is read-only. +PreparedStatement.21=Queries leading to data modification are not allowed +PreparedStatement.25=Connection is read-only. +PreparedStatement.26=Queries leading to data modification are not allowed +PreparedStatement.32=Unsupported character encoding ' +PreparedStatement.33=' +PreparedStatement.34=Connection is read-only. +PreparedStatement.35=Queries leading to data modification are not allowed +PreparedStatement.37=Can not issue executeUpdate() or executeLargeUpdate() for SELECTs +PreparedStatement.40=No value specified for parameter +PreparedStatement.43=PreparedStatement created, but used 1 or fewer times. It is more efficient to prepare statements once, and re-use them many times +PreparedStatement.48=PreparedStatement has been closed. No further operations allowed. +PreparedStatement.49=Parameter index out of range ( +PreparedStatement.50=\ < 1 ). +PreparedStatement.51=Parameter index out of range ( +PreparedStatement.52=\ > number of parameters, which is +PreparedStatement.53=). +PreparedStatement.54=Invalid argument value: +PreparedStatement.55=Error reading from InputStream +PreparedStatement.56=Error reading from InputStream +PreparedStatement.61=SQL String can not be NULL +ServerPreparedStatement.2=Connection is read-only. +ServerPreparedStatement.3=Queries leading to data modification are not allowed +ServerPreparedStatement.6=\ unable to materialize as string due to underlying SQLException: +ServerPreparedStatement.7=Not supported for server-side prepared statements. +ServerPreparedStatement.8=No parameters defined during prepareCall() +ServerPreparedStatement.9=Parameter index out of bounds. +ServerPreparedStatement.10=\ is not between valid values of 1 and +ServerPreparedStatement.11=Driver can not re-execute prepared statement when a parameter has been changed +ServerPreparedStatement.12=from a streaming type to an intrinsic data type without calling clearParameters() first. +ServerPreparedStatement.13=Statement parameter +ServerPreparedStatement.14=\ not set. +ServerPreparedStatement.15=Slow query (exceeded +ServerPreparedStatement.15a=\ ms., duration:\ +ServerPreparedStatement.16=\ ms): +ServerPreparedStatement.18=Unknown LONG DATA type ' +ServerPreparedStatement.22=Unsupported character encoding ' +ServerPreparedStatement.24=Error while reading binary stream: +ServerPreparedStatement.25=Error while reading binary stream: +ByteArrayBuffer.0=ByteArrayBuffer has no NIO buffers +ByteArrayBuffer.1=Unsupported character encoding ' +ByteArrayBuffer.2=Buffer length is less then "expectedLength" value. +AssertionFailedException.0=ASSERT FAILS: Exception +AssertionFailedException.1=\ that should not be thrown, was thrown +NotUpdatable.0=Result Set not updatable. +NotUpdatable.1=This result set must come from a statement +NotUpdatable.2=that was created with a result set type of ResultSet.CONCUR_UPDATABLE, +NotUpdatable.3=the query must select only one table, can not use functions and must +NotUpdatable.4=select all primary keys from that table. See the JDBC 2.1 API Specification, +NotUpdatable.5=section 5.6 for more details. +NotUpdatableReason.0=Result Set not updatable (references more than one table). +NotUpdatableReason.1=Result Set not updatable (references more than one database). +NotUpdatableReason.2=Result Set not updatable (references no tables). +NotUpdatableReason.3=Result Set not updatable (references computed values or doesn't reference any columns or tables). +NotUpdatableReason.4=Result Set not updatable (references no primary keys). +NotUpdatableReason.5=Result Set not updatable (referenced table has no primary keys). +NotUpdatableReason.6=Result Set not updatable (references unknown primary key {0}). +NotUpdatableReason.7=Result Set not updatable (does not reference all primary keys). + +JDBC4Connection.ClientInfoNotImplemented=Configured clientInfoProvider class ''{0}'' does not implement com.mysql.jdbc.JDBC4ClientInfoProvider. + +InvalidLoadBalanceStrategy=Invalid load balancing strategy ''{0}''. +Connection.BadValueInServerVariables=Invalid value ''{1}'' for server variable named ''{0}'', falling back to sane default of ''{2}''. + +Connection.UnableToConnect=Could not create connection to database server. +Connection.UnableToConnectWithRetries=Could not create connection to database server. \ +Attempted reconnect {0} times. Giving up. +Connection.UnexpectedException=Unexpected exception encountered during query. +Connection.UnhandledExceptionDuringShutdown=Unexpected exception during server shutdown. + +MysqlClearPasswordPlugin.1=Unsupported character encoding ''{0}'' for ''passwordCharacterEncoding'' or ''characterEncoding''. + +MysqlNativePasswordPlugin.1=Unsupported character encoding ''{0}'' for ''passwordCharacterEncoding'' or ''characterEncoding''. + +Sha256PasswordPlugin.0=Unable to read public key {0} +Sha256PasswordPlugin.1=Unable to close public key file +Sha256PasswordPlugin.2=Public Key Retrieval is not allowed +Sha256PasswordPlugin.3=Unsupported character encoding ''{0}'' for ''passwordCharacterEncoding'' or ''characterEncoding''. + +MysqlXAConnection.001=Invalid flag, must use TMNOFLAGS, or any combination of TMSTARTRSCAN and TMENDRSCAN +MysqlXAConnection.002=Error while recovering XIDs from RM. GTRID and BQUAL are wrong sizes +MysqlXAConnection.003=Undetermined error occurred in the underlying Connection - check your data for consistency + +# +# ConnectionProperty Categories +# + +ConnectionProperties.categoryConnectionAuthentication=Connection/Authentication +ConnectionProperties.categoryNetworking=Networking +ConnectionProperties.categoryDebuggingProfiling=Debugging/Profiling +ConnectionProperties.categorryHA=High Availability and Clustering +ConnectionProperties.categoryMisc=Miscellaneous +ConnectionProperties.categoryPerformance=Performance Extensions +ConnectionProperties.categorySecurity=Security + +# +# ConnectionProperty Descriptions +# + +ConnectionProperties.loadDataLocal=Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to 'true'). +ConnectionProperties.allowMasterDownConnections=By default, a replication-aware connection will fail to connect when configured master hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection, by failing over to the slave servers, in read-only state. It won't prevent subsequent failures when switching back to the master hosts i.e. by setting the replication connection to read/write state. +ConnectionProperties.allowSlaveDownConnections=By default, a replication-aware connection will fail to connect when configured slave hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection. It won't prevent failures when switching to slaves i.e. by setting the replication connection to read-only state. The property 'readFromMasterWhenNoSlaves' should be used for this purpose. +ConnectionProperties.readFromMasterWhenNoSlaves=Replication-aware connections distribute load by using the master hosts when in read/write state and by using the slave hosts when in read-only state. If, when setting the connection to read-only state, none of the slave hosts are available, an SQLExeception is thrown back. Setting this property to 'true' allows to fail over to the master hosts, while setting the connection state to read-only, when no slave hosts are available at switch instant. +ConnectionProperties.allowMultiQueries=Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false', and does not affect the addBatch() and executeBatch() methods, which instead rely on rewriteBatchStatements. +ConnectionProperties.allowNANandINF=Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()? +ConnectionProperties.allowUrlInLoadLocal=Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements? +ConnectionProperties.alwaysSendSetIsolation=Should the driver always communicate with the database when Connection.setTransactionIsolation() is called? If set to false, the driver will only communicate with the database when the requested transaction isolation is different than the whichever is newer, the last value that was set via Connection.setTransactionIsolation(), or the value that was read from the server when the connection was established. Note that useLocalSessionState=true will force the same behavior as alwaysSendSetIsolation=false, regardless of how alwaysSendSetIsolation is set. +ConnectionProperties.autoClosePstmtStreams=Should the driver automatically call .close() on streams/readers passed as arguments via set*() methods? +ConnectionProperties.autoDeserialize=Should the driver automatically detect and de-serialize objects stored in BLOB fields? +ConnectionProperties.autoGenerateTestcaseScript=Should the driver dump the SQL it is executing, including server-side prepared statements to STDERR? +ConnectionProperties.autoReconnect=Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. Alternatively, as a last option, investigate setting the MySQL server variable "wait_timeout" to a high value, rather than the default of 8 hours. +ConnectionProperties.autoReconnectForPools=Use a reconnection strategy appropriate for connection pools (defaults to 'false') +ConnectionProperties.autoSlowLog=Instead of using slowQueryThreshold* to determine if a query is slow enough to be logged, maintain statistics that allow the driver to determine queries that are outside the 99th percentile? +ConnectionProperties.blobsAreStrings=Should the driver always treat BLOBs as Strings - specifically to work around dubious metadata returned by the server for GROUP BY clauses? +ConnectionProperties.functionsNeverReturnBlobs=Should the driver always treat data from functions returning BLOBs as Strings - specifically to work around dubious metadata returned by the server for GROUP BY clauses? +ConnectionProperties.blobSendChunkSize=Chunk size to use when sending BLOB/CLOBs via ServerPreparedStatements. Note that this value cannot exceed the value of "maxAllowedPacket" and, if that is the case, then this value will be corrected automatically. +ConnectionProperties.cacheCallableStatements=Should the driver cache the parsing stage of CallableStatements +ConnectionProperties.cachePrepStmts=Should the driver cache the parsing stage of PreparedStatements of client-side prepared statements, the "check" for suitability of server-side prepared and server-side prepared statements themselves? +ConnectionProperties.cacheRSMetadata=Should the driver cache ResultSetMetaData for Statements and PreparedStatements? (Req. JDK-1.4+, true/false, default 'false') +ConnectionProperties.cacheServerConfiguration=Should the driver cache the results of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis? +ConnectionProperties.callableStmtCacheSize=If 'cacheCallableStmts' is enabled, how many callable statements should be cached? +ConnectionProperties.capitalizeTypeNames=Capitalize type names in DatabaseMetaData? (usually only useful when using WebObjects, true/false, defaults to 'false') +ConnectionProperties.characterEncoding=If 'useUnicode' is set to true, what character encoding should the driver use when dealing with strings? (defaults is to 'autodetect') +ConnectionProperties.characterSetResults=Character set to tell the server to return results as. +ConnectionProperties.clientInfoProvider=The name of a class that implements the com.mysql.jdbc.JDBC4ClientInfoProvider interface in order to support JDBC-4.0's Connection.get/setClientInfo() methods +ConnectionProperties.clobberStreamingResults=This will cause a 'streaming' ResultSet to be automatically closed, and any outstanding data still streaming from the server to be discarded if another query is executed before all the data has been read from the server. +ConnectionProperties.clobCharacterEncoding=The character encoding to use for sending and retrieving TEXT, MEDIUMTEXT and LONGTEXT values instead of the configured connection characterEncoding +ConnectionProperties.compensateOnDuplicateKeyUpdateCounts=Should the driver compensate for the update counts of "ON DUPLICATE KEY" INSERT statements (2 = 1, 0 = 1) when using prepared statements? +ConnectionProperties.connectionCollation=If set, tells the server to use this collation via 'set collation_connection' +ConnectionProperties.connectionLifecycleInterceptors=A comma-delimited list of classes that implement "com.mysql.jdbc.ConnectionLifecycleInterceptor" that should notified of connection lifecycle events (creation, destruction, commit, rollback, setCatalog and setAutoCommit) and potentially alter the execution of these commands. ConnectionLifecycleInterceptors are "stackable", more than one interceptor may be specified via the configuration property as a comma-delimited list, with the interceptors executed in order from left to right. +ConnectionProperties.connectTimeout=Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. Defaults to '0'. +ConnectionProperties.continueBatchOnError=Should the driver continue processing batch commands if one statement fails. The JDBC spec allows either way (defaults to 'true'). +ConnectionProperties.createDatabaseIfNotExist=Creates the database given in the URL if it doesn't yet exist. Assumes the configured user has permissions to create databases. +ConnectionProperties.defaultFetchSize=The driver will call setFetchSize(n) with this value on all newly-created Statements +ConnectionProperties.useServerPrepStmts=Use server-side prepared statements if the server supports them? +ConnectionProperties.dontTrackOpenResources=The JDBC specification requires the driver to automatically track and close resources, however if your application doesn't do a good job of explicitly calling close() on statements or result sets, this can cause memory leakage. Setting this property to true relaxes this constraint, and can be more memory efficient for some applications. Also the automatic closing of the Statement and current ResultSet in Statement.closeOnCompletion() and Statement.getMoreResults ([Statement.CLOSE_CURRENT_RESULT | Statement.CLOSE_ALL_RESULTS]), respectively, ceases to happen. This property automatically sets holdResultsOpenOverStatementClose=true. +ConnectionProperties.dumpQueriesOnException=Should the driver dump the contents of the query sent to the server in the message for SQLExceptions? +ConnectionProperties.dynamicCalendars=Should the driver retrieve the default calendar when required, or cache it per connection/session? +ConnectionProperties.eliseSetAutoCommit=If using MySQL-4.1 or newer, should the driver only issue 'set autocommit=n' queries when the server's state doesn't match the requested state by Connection.setAutoCommit(boolean)? +ConnectionProperties.emptyStringsConvertToZero=Should the driver allow conversions from empty string fields to numeric values of '0'? +ConnectionProperties.emulateLocators=Should the driver emulate java.sql.Blobs with locators? With this feature enabled, the driver will delay loading the actual Blob data until the one of the retrieval methods (getInputStream(), getBytes(), and so forth) on the blob data stream has been accessed. For this to work, you must use a column alias with the value of the column to the actual name of the Blob. The feature also has the following restrictions: The SELECT that created the result set must reference only one table, the table must have a primary key; the SELECT must alias the original blob column name, specified as a string, to an alternate name; the SELECT must cover all columns that make up the primary key. +ConnectionProperties.emulateUnsupportedPstmts=Should the driver detect prepared statements that are not supported by the server, and replace them with client-side emulated versions? +ConnectionProperties.enablePacketDebug=When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be kept, and dumped when exceptions are thrown in key areas in the driver's code +ConnectionProperties.enableQueryTimeouts=When enabled, query timeouts set via Statement.setQueryTimeout() use a shared java.util.Timer instance for scheduling. Even if the timeout doesn't expire before the query is processed, there will be memory used by the TimerTask for the given timeout which won't be reclaimed until the time the timeout would have expired if it hadn't been cancelled by the driver. High-load environments might want to consider disabling this functionality. +ConnectionProperties.explainSlowQueries=If 'logSlowQueries' is enabled, should the driver automatically issue an 'EXPLAIN' on the server and send the results to the configured log at a WARN level? +ConnectionProperties.failoverReadOnly=When failing over in autoReconnect mode, should the connection be set to 'read-only'? +ConnectionProperties.gatherPerfMetrics=Should the driver gather performance metrics, and report them via the configured logger every 'reportMetricsIntervalMillis' milliseconds? +ConnectionProperties.generateSimpleParameterMetadata=Should the driver generate simplified parameter metadata for PreparedStatements when no metadata is available either because the server couldn't support preparing the statement, or server-side prepared statements are disabled? +ConnectionProperties.holdRSOpenOverStmtClose=Should the driver close result sets on Statement.close() as required by the JDBC specification? +ConnectionProperties.ignoreNonTxTables=Ignore non-transactional table warning for rollback? (defaults to 'false'). +ConnectionProperties.includeInnodbStatusInDeadlockExceptions=Include the output of "SHOW ENGINE INNODB STATUS" in exception messages when deadlock exceptions are detected? +ConnectionProperties.includeThreadDumpInDeadlockExceptions=Include a current Java thread dump in exception messages when deadlock exceptions are detected? +ConnectionProperties.includeThreadNamesAsStatementComment=Include the name of the current thread as a comment visible in "SHOW PROCESSLIST", or in Innodb deadlock dumps, useful in correlation with "includeInnodbStatusInDeadlockExceptions=true" and "includeThreadDumpInDeadlockExceptions=true". +ConnectionProperties.initialTimeout=If autoReconnect is enabled, the initial time to wait between re-connect attempts (in seconds, defaults to '2'). +ConnectionProperties.interactiveClient=Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout connections based on INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT +ConnectionProperties.jdbcCompliantTruncation=Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings (MySQL 4.1.0 and newer)? This property has no effect if the server sql-mode includes STRICT_TRANS_TABLES. +ConnectionProperties.largeRowSizeThreshold=What size result set row should the JDBC driver consider "large", and thus use a more memory-efficient way of representing the row internally? +ConnectionProperties.loadBalanceStrategy=If using a load-balanced connection to connect to SQL nodes in a MySQL Cluster/NDB configuration (by using the URL prefix "jdbc:mysql:loadbalance://"), which load balancing algorithm should the driver use: (1) "random" - the driver will pick a random host for each request. This tends to work better than round-robin, as the randomness will somewhat account for spreading loads where requests vary in response time, while round-robin can sometimes lead to overloaded nodes if there are variations in response times across the workload. (2) "bestResponseTime" - the driver will route the request to the host that had the best response time for the previous transaction. +ConnectionProperties.loadBalanceBlacklistTimeout=Time in milliseconds between checks of servers which are unavailable, by controlling how long a server lives in the global blacklist. +ConnectionProperties.loadBalancePingTimeout=Time in milliseconds to wait for ping response from each of load-balanced physical connections when using load-balanced Connection. +ConnectionProperties.loadBalanceValidateConnectionOnSwapServer=Should the load-balanced Connection explicitly check whether the connection is live when swapping to a new physical connection at commit/rollback? +ConnectionProperties.loadBalanceConnectionGroup=Logical group of load-balanced connections within a classloader, used to manage different groups independently. If not specified, live management of load-balanced connections is disabled. +ConnectionProperties.loadBalanceExceptionChecker=Fully-qualified class name of custom exception checker. The class must implement com.mysql.jdbc.LoadBalanceExceptionChecker interface, and is used to inspect SQLExceptions and determine whether they should trigger fail-over to another host in a load-balanced deployment. +ConnectionProperties.loadBalanceSQLStateFailover=Comma-delimited list of SQLState codes used by default load-balanced exception checker to determine whether a given SQLException should trigger failover. The SQLState of a given SQLException is evaluated to determine whether it begins with any value in the comma-delimited list. +ConnectionProperties.loadBalanceSQLExceptionSubclassFailover=Comma-delimited list of classes/interfaces used by default load-balanced exception checker to determine whether a given SQLException should trigger failover. The comparison is done using Class.isInstance(SQLException) using the thrown SQLException. +ConnectionProperties.loadBalanceEnableJMX=Enables JMX-based management of load-balanced connection groups, including live addition/removal of hosts from load-balancing pool. +ConnectionProperties.replicationEnableJMX=Enables JMX-based management of replication connection groups, including live slave promotion, addition of new slaves and removal of master or slave hosts from load-balanced master and slave connection pools. +ConnectionProperties.loadBalanceHostRemovalGracePeriod=Sets the grace period to wait for a host being removed from a load-balanced connection, to be released when it is currently the active host. +ConnectionProperties.loadBalanceAutoCommitStatementThreshold=When auto-commit is enabled, the number of statements which should be executed before triggering load-balancing to rebalance. Default value of 0 causes load-balanced connections to only rebalance when exceptions are encountered, or auto-commit is disabled and transactions are explicitly committed or rolled back. +ConnectionProperties.loadBalanceAutoCommitStatementRegex=When load-balancing is enabled for auto-commit statements (via loadBalanceAutoCommitStatementThreshold), the statement counter will only increment when the SQL matches the regular expression. By default, every statement issued matches. +ConnectionProperties.localSocketAddress=Hostname or IP address given to explicitly configure the interface that the driver will bind the client side of the TCP/IP connection to when connecting. +ConnectionProperties.locatorFetchBufferSize=If 'emulateLocators' is configured to 'true', what size buffer should be used when fetching BLOB data for getBinaryInputStream? +ConnectionProperties.logger=The name of a class that implements \"{0}\" that will be used to log messages to. (default is \"{1}\", which logs to STDERR) +ConnectionProperties.logSlowQueries=Should queries that take longer than 'slowQueryThresholdMillis' be logged? +ConnectionProperties.logXaCommands=Should the driver log XA commands sent by MysqlXaConnection to the server, at the DEBUG level of logging? +ConnectionProperties.maintainTimeStats=Should the driver maintain various internal timers to enable idle time calculations as well as more verbose error messages when the connection to the server fails? Setting this property to false removes at least two calls to System.getCurrentTimeMillis() per query. +ConnectionProperties.maxQuerySizeToLog=Controls the maximum length/size of a query that will get logged when profiling or tracing +ConnectionProperties.maxReconnects=Maximum number of reconnects to attempt if autoReconnect is true, default is '3'. +ConnectionProperties.maxRows=The maximum number of rows to return (0, the default means return all rows). +ConnectionProperties.allVersions=all versions +ConnectionProperties.metadataCacheSize=The number of queries to cache ResultSetMetadata for if cacheResultSetMetaData is set to 'true' (default 50) +ConnectionProperties.netTimeoutForStreamingResults=What value should the driver automatically set the server setting 'net_write_timeout' to when the streaming result sets feature is in use? (value has unit of seconds, the value '0' means the driver will not try and adjust this value) +ConnectionProperties.noAccessToProcedureBodies=When determining procedure parameter types for CallableStatements, and the connected user can't access procedure bodies through "SHOW CREATE PROCEDURE" or select on mysql.proc should the driver instead create basic metadata (all parameters reported as INOUT VARCHARs) instead of throwing an exception? +ConnectionProperties.noDatetimeStringSync=Don't ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString()) +ConnectionProperties.noTzConversionForTimeType=Don't convert TIME values using the server time zone if 'useTimezone'='true' +ConnectionProperties.noTzConversionForDateType=Don't convert DATE values using the server time zone if 'useTimezone'='true' or 'useLegacyDatetimeCode'='false' +ConnectionProperties.cacheDefaultTimezone=Caches client's default time zone. This results in better performance when dealing with time zone conversions in Date and Time data types, however it won't be aware of time zone changes if they happen at runtime. +ConnectionProperties.nullCatalogMeansCurrent=When DatabaseMetadataMethods ask for a 'catalog' parameter, does the value null mean use the current catalog? (this is not JDBC-compliant, but follows legacy behavior from earlier versions of the driver) +ConnectionProperties.nullNamePatternMatchesAll=Should DatabaseMetaData methods that accept *pattern parameters treat null the same as '%' (this is not JDBC-compliant, however older versions of the driver accepted this departure from the specification) +ConnectionProperties.packetDebugBufferSize=The maximum number of packets to retain when 'enablePacketDebug' is true +ConnectionProperties.padCharsWithSpace=If a result set column has the CHAR type and the value does not fill the amount of characters specified in the DDL for the column, should the driver pad the remaining characters with space (for ANSI compliance)? +ConnectionProperties.paranoid=Take measures to prevent exposure sensitive information in error messages and clear data structures holding sensitive data when possible? (defaults to 'false') +ConnectionProperties.pedantic=Follow the JDBC spec to the letter. +ConnectionProperties.pinGlobalTxToPhysicalConnection=When using XAConnections, should the driver ensure that operations on a given XID are always routed to the same physical connection? This allows the XAConnection to support "XA START ... JOIN" after "XA END" has been called +ConnectionProperties.populateInsertRowWithDefaultValues=When using ResultSets that are CONCUR_UPDATABLE, should the driver pre-populate the "insert" row with default values from the DDL for the table used in the query so those values are immediately available for ResultSet accessors? This functionality requires a call to the database for metadata each time a result set of this type is created. If disabled (the default), the default values will be populated by the an internal call to refreshRow() which pulls back default values and/or values changed by triggers. +ConnectionProperties.prepStmtCacheSize=If prepared statement caching is enabled, how many prepared statements should be cached? +ConnectionProperties.prepStmtCacheSqlLimit=If prepared statement caching is enabled, what's the largest SQL the driver will cache the parsing for? +ConnectionProperties.processEscapeCodesForPrepStmts=Should the driver process escape codes in queries that are prepared? Default escape processing behavior in non-prepared statements must be defined with the property 'enableEscapeProcessing'. +ConnectionProperties.profilerEventHandler=Name of a class that implements the interface com.mysql.jdbc.profiler.ProfilerEventHandler that will be used to handle profiling/tracing events. +ConnectionProperties.profileSqlDeprecated=Deprecated, use 'profileSQL' instead. Trace queries and their execution/fetch times on STDERR (true/false) defaults to 'false' +ConnectionProperties.profileSQL=Trace queries and their execution/fetch times to the configured logger (true/false) defaults to 'false' +ConnectionProperties.connectionPropertiesTransform=An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that the driver will use to modify URL properties passed to the driver before attempting a connection +ConnectionProperties.queriesBeforeRetryMaster=Number of queries to issue before falling back to the primary host when failed over (when using multi-host failover). Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the primary host. Setting both properties to 0 disables the automatic fall back to the primary host at transaction boundaries. Defaults to 50. +ConnectionProperties.reconnectAtTxEnd=If autoReconnect is set to true, should the driver attempt reconnections at the end of every transaction? +ConnectionProperties.relaxAutoCommit=If the version of MySQL the driver connects to does not support transactions, still allow calls to commit(), rollback() and setAutoCommit() (true/false, defaults to 'false')? +ConnectionProperties.reportMetricsIntervalMillis=If 'gatherPerfMetrics' is enabled, how often should they be logged (in ms)? +ConnectionProperties.requireSSL=Require server support of SSL connection if useSSL=true? (defaults to 'false'). +ConnectionProperties.resourceId=A globally unique name that identifies the resource that this datasource or connection is connected to, used for XAResource.isSameRM() when the driver can't determine this value based on hostnames used in the URL +ConnectionProperties.resultSetSizeThreshold=If the usage advisor is enabled, how many rows should a result set contain before the driver warns that it is suspiciously large? +ConnectionProperties.retainStatementAfterResultSetClose=Should the driver retain the Statement reference in a ResultSet after ResultSet.close() has been called. This is not JDBC-compliant after JDBC-4.0. +ConnectionProperties.retriesAllDown=When using loadbalancing or failover, the number of times the driver should cycle through available hosts, attempting to connect. Between cycles, the driver will pause for 250ms if no servers are available. +ConnectionProperties.rewriteBatchedStatements=Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(), the driver won't be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements. Please be aware using rewriteBatchedStatements=true with INSERT .. ON DUPLICATE KEY UPDATE that for rewritten statement server returns only one value as sum of all affected (or found) rows in batch and it isn't possible to map it correctly to initial statements; in this case driver returns 0 as a result of each batch statement if total count was 0, and the Statement.SUCCESS_NO_INFO as a result of each batch statement if total count was > 0. +ConnectionProperties.rollbackOnPooledClose=Should the driver issue a rollback() when the logical connection in a pool is closed? +ConnectionProperties.roundRobinLoadBalance=When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis? +ConnectionProperties.runningCTS13=Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3 +ConnectionProperties.secondsBeforeRetryMaster=How long should the driver wait, when failed over, before attempting to reconnect to the primary host? Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. Setting both properties to 0 disables the automatic fall back to the primary host at transaction boundaries. Time in seconds, defaults to 30 +ConnectionProperties.selfDestructOnPingSecondsLifetime=If set to a non-zero value, the driver will report close the connection and report failure when Connection.ping() or Connection.isValid(int) is called if the connection's lifetime exceeds this value. +ConnectionProperties.selfDestructOnPingMaxOperations==If set to a non-zero value, the driver will report close the connection and report failure when Connection.ping() or Connection.isValid(int) is called if the connection's count of commands sent to the server exceeds this value. +ConnectionProperties.serverTimezone=Override detection/mapping of time zone. Used when time zone from server doesn't map to Java time zone +ConnectionProperties.sessionVariables=A comma-separated list of name/value pairs to be sent as SET SESSION ... to the server when the driver connects. +ConnectionProperties.slowQueryThresholdMillis=If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'? +ConnectionProperties.slowQueryThresholdNanos=If 'useNanosForElapsedTime' is set to true, and this property is set to a non-zero value, the driver will use this threshold (in nanosecond units) to determine if a query was slow. +ConnectionProperties.socketFactory=The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor. +ConnectionProperties.socketTimeout=Timeout on network socket operations (0, the default means no timeout). +ConnectionProperties.socksProxyHost=Name or IP address of SOCKS host to connect through. +ConnectionProperties.socksProxyPort=Port of SOCKS server. +ConnectionProperties.statementInterceptors=A comma-delimited list of classes that implement "com.mysql.jdbc.StatementInterceptor" that should be placed "in between" query execution to influence the results. StatementInterceptors are "chainable", the results returned by the "current" interceptor will be passed on to the next in in the chain, from left-to-right order, as specified in this property. +ConnectionProperties.strictFloatingPoint=Used only in older versions of compliance test +ConnectionProperties.strictUpdates=Should the driver do strict checking (all primary keys selected) of updatable result sets (true, false, defaults to 'true')? +ConnectionProperties.overrideSupportsIEF=Should the driver return "true" for DatabaseMetaData.supportsIntegrityEnhancementFacility() even if the database doesn't support it to workaround applications that require this method to return "true" to signal support of foreign keys, even though the SQL specification states that this facility contains much more than just foreign key support (one such application being OpenOffice)? +ConnectionProperties.tcpNoDelay=If connecting using TCP/IP, should the driver set SO_TCP_NODELAY (disabling the Nagle Algorithm)? +ConnectionProperties.tcpKeepAlive=If connecting using TCP/IP, should the driver set SO_KEEPALIVE? +ConnectionProperties.tcpSoRcvBuf=If connecting using TCP/IP, should the driver set SO_RCV_BUF to the given value? The default value of '0', means use the platform default value for this property) +ConnectionProperties.tcpSoSndBuf=If connecting using TCP/IP, should the driver set SO_SND_BUF to the given value? The default value of '0', means use the platform default value for this property) +ConnectionProperties.tcpTrafficClass=If connecting using TCP/IP, should the driver set traffic class or type-of-service fields ?See the documentation for java.net.Socket.setTrafficClass() for more information. +ConnectionProperties.tinyInt1isBit=Should the driver treat the datatype TINYINT(1) as the BIT type (because the server silently converts BIT -> TINYINT(1) when creating tables)? +ConnectionProperties.traceProtocol=Should trace-level network protocol be logged? +ConnectionProperties.treatUtilDateAsTimestamp=Should the driver treat java.util.Date as a TIMESTAMP for the purposes of PreparedStatement.setObject()? +ConnectionProperties.transformedBitIsBoolean=If the driver converts TINYINT(1) to a different type, should it use BOOLEAN instead of BIT for future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT type? +ConnectionProperties.useCompression=Use zlib compression when communicating with the server (true/false)? Defaults to 'false'. +ConnectionProperties.useConfigs=Load the comma-delimited list of configuration properties before parsing the URL or applying user-specified properties. These configurations are explained in the 'Configurations' of the documentation. +ConnectionProperties.useCursorFetch=If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a statement, should that statement use cursor-based fetching to retrieve rows? +ConnectionProperties.useDynamicCharsetInfo=Should the driver use a per-connection cache of character set information queried from the server when necessary, or use a built-in static mapping that is more efficient, but isn't aware of custom character sets or character sets implemented after the release of the JDBC driver? +ConnectionProperties.useFastIntParsing=Use internal String->Integer conversion routines to avoid excessive object creation? +ConnectionProperties.useFastDateParsing=Use internal String->Date/Time/Timestamp conversion routines to avoid excessive object creation? This is part of the legacy date-time code, thus the property has an effect only when "useLegacyDatetimeCode=true." +ConnectionProperties.useHostsInPrivileges=Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges() (true/false), defaults to 'true'. +ConnectionProperties.useInformationSchema=When connected to MySQL-5.0.7 or newer, should the driver use the INFORMATION_SCHEMA to derive information used by DatabaseMetaData? +ConnectionProperties.useJDBCCompliantTimezoneShift=Should the driver use JDBC-compliant rules when converting TIME/TIMESTAMP/DATETIME values' time zone information for those JDBC arguments which take a java.util.Calendar argument? This is part of the legacy date-time code, thus the property has an effect only when "useLegacyDatetimeCode=true." +ConnectionProperties.useLocalSessionState=Should the driver refer to the internal values of autocommit and transaction isolation that are set by Connection.setAutoCommit() and Connection.setTransactionIsolation() and transaction state as maintained by the protocol, rather than querying the database or blindly sending commands to the database for commit() or rollback() method calls? +ConnectionProperties.useLocalTransactionState=Should the driver use the in-transaction state provided by the MySQL protocol to determine if a commit() or rollback() should actually be sent to the database? +ConnectionProperties.useNanosForElapsedTime=For profiling/debugging functionality that measures elapsed time, should the driver try to use nanoseconds resolution if available (JDK >= 1.5)? +ConnectionProperties.useOldAliasMetadataBehavior=Should the driver use the legacy behavior for "AS" clauses on columns and tables, and only return aliases (if any) for ResultSetMetaData.getColumnName() or ResultSetMetaData.getTableName() rather than the original column/table name? In 5.0.x, the default value was true. +ConnectionProperties.useOldUtf8Behavior=Use the UTF-8 behavior the driver did when communicating with 4.0 and older servers +ConnectionProperties.useOnlyServerErrorMessages=Don't prepend 'standard' SQLState error messages to error messages returned by the server. +ConnectionProperties.useReadAheadInput=Use newer, optimized non-blocking, buffered input stream when reading from the server? +ConnectionProperties.useSqlStateCodes=Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true' +ConnectionProperties.useSSL=Use SSL when communicating with the server (true/false), default is 'true' when connecting to MySQL 5.5.45+, 5.6.26+ or 5.7.6+, otherwise default is 'false' +ConnectionProperties.useSSPSCompatibleTimezoneShift=If migrating from an environment that was using server-side prepared statements, and the configuration property "useJDBCCompliantTimeZoneShift" set to "true", use compatible behavior when not using server-side prepared statements when sending TIMESTAMP values to the MySQL server. +ConnectionProperties.useStreamLengthsInPrepStmts=Honor stream length parameter in PreparedStatement/ResultSet.setXXXStream() method calls (true/false, defaults to 'true')? +ConnectionProperties.useTimezone=Convert time/date types between client and server time zones (true/false, defaults to 'false')? This is part of the legacy date-time code, thus the property has an effect only when "useLegacyDatetimeCode=true." +ConnectionProperties.ultraDevHack=Create PreparedStatements for prepareCall() when required, because UltraDev is broken and issues a prepareCall() for _all_ statements? (true/false, defaults to 'false') +ConnectionProperties.useUnbufferedInput=Don't use BufferedInputStream for reading data from the server +ConnectionProperties.useUnicode=Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8), true/false, defaults to 'true' +ConnectionProperties.useUsageAdvisor=Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log (true/false, defaults to 'false')? +ConnectionProperties.verifyServerCertificate=If "useSSL" is set to "true", should the driver verify the server's certificate? When using this feature, the keystore parameters should be specified by the "clientCertificateKeyStore*" properties, rather than system properties. Default is 'false' when connecting to MySQL 5.5.45+, 5.6.26+ or 5.7.6+ and "useSSL" was not explicitly set to "true". Otherwise default is 'true' +ConnectionProperties.yearIsDateType=Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date, or as a SHORT? +ConnectionProperties.zeroDateTimeBehavior=What should happen when the driver encounters DATETIME values that are composed entirely of zeros (used by MySQL to represent invalid dates)? Valid values are \"{0}\", \"{1}\" and \"{2}\". +ConnectionProperties.useJvmCharsetConverters=Always use the character encoding routines built into the JVM, rather than using lookup tables for single-byte character sets? +ConnectionProperties.useGmtMillisForDatetimes=Convert between session time zone and GMT before creating Date and Timestamp instances (value of 'false' leads to legacy behavior, 'true' leads to more JDBC-compliant behavior)? This is part of the legacy date-time code, thus the property has an effect only when "useLegacyDatetimeCode=true." +ConnectionProperties.dumpMetadataOnColumnNotFound=Should the driver dump the field-level metadata of a result set into the exception message when ResultSet.findColumn() fails? +ConnectionProperties.clientCertificateKeyStoreUrl=URL to the client certificate KeyStore (if not specified, use defaults) +ConnectionProperties.trustCertificateKeyStoreUrl=URL to the trusted root certificate KeyStore (if not specified, use defaults) +ConnectionProperties.clientCertificateKeyStoreType=KeyStore type for client certificates (NULL or empty means use the default, which is "JKS". Standard keystore types supported by the JVM are "JKS" and "PKCS12", your environment may have more available depending on what security products are installed and available to the JVM. +ConnectionProperties.clientCertificateKeyStorePassword=Password for the client certificates KeyStore +ConnectionProperties.trustCertificateKeyStoreType=KeyStore type for trusted root certificates (NULL or empty means use the default, which is "JKS". Standard keystore types supported by the JVM are "JKS" and "PKCS12", your environment may have more available depending on what security products are installed and available to the JVM. +ConnectionProperties.trustCertificateKeyStorePassword=Password for the trusted root certificates KeyStore +ConnectionProperties.serverRSAPublicKeyFile=File path to the server RSA public key file for sha256_password authentication. If not specified, the public key will be retrieved from the server. +ConnectionProperties.allowPublicKeyRetrieval=Allows special handshake roundtrip to get server RSA public key directly from server. +ConnectionProperties.Username=The user to connect as +ConnectionProperties.Password=The password to use when connecting +ConnectionProperties.useBlobToStoreUTF8OutsideBMP=Tells the driver to treat [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR columns holding text encoded in UTF-8 that has characters outside the BMP (4-byte encodings), which MySQL server can't handle natively. +ConnectionProperties.utf8OutsideBmpExcludedColumnNamePattern=When "useBlobToStoreUTF8OutsideBMP" is set to "true", column names matching the given regex will still be treated as BLOBs unless they match the regex specified for "utf8OutsideBmpIncludedColumnNamePattern". The regex must follow the patterns used for the java.util.regex package. +ConnectionProperties.utf8OutsideBmpIncludedColumnNamePattern=Used to specify exclusion rules to "utf8OutsideBmpExcludedColumnNamePattern". The regex must follow the patterns used for the java.util.regex package. +ConnectionProperties.useLegacyDatetimeCode=Use code for DATE/TIME/DATETIME/TIMESTAMP handling in result sets and statements that consistently handles time zone conversions from client to server and back again, or use the legacy code for these datatypes that has been in the driver for backwards-compatibility? Setting this property to 'false' voids the effects of "useTimezone," "useJDBCCompliantTimezoneShift," "useGmtMillisForDatetimes," and "useFastDateParsing." +ConnectionProperties.sendFractionalSeconds=Send fractional part from TIMESTAMP seconds. If set to false, the nanoseconds value of TIMESTAMP values will be truncated before sending any data to the server. This option applies only to prepared statements, callable statements or updatable result sets. +ConnectionProperties.useColumnNamesInFindColumn=Prior to JDBC-4.0, the JDBC specification had a bug related to what could be given as a "column name" to ResultSet methods like findColumn(), or getters that took a String property. JDBC-4.0 clarified "column name" to mean the label, as given in an "AS" clause and returned by ResultSetMetaData.getColumnLabel(), and if no AS clause, the column name. Setting this property to "true" will give behavior that is congruent to JDBC-3.0 and earlier versions of the JDBC specification, but which because of the specification bug could give unexpected results. This property is preferred over "useOldAliasMetadataBehavior" unless you need the specific behavior that it provides with respect to ResultSetMetadata. +ConnectionProperties.useAffectedRows=Don't set the CLIENT_FOUND_ROWS flag when connecting to the server (not JDBC-compliant, will break most applications that rely on "found" rows vs. "affected rows" for DML statements), but does cause "correct" update counts from "INSERT ... ON DUPLICATE KEY UPDATE" statements to be returned by the server. +ConnectionProperties.passwordCharacterEncoding=What character encoding is used for passwords? Leaving this set to the default value (null), uses the value set in "characterEncoding" if there is one, otherwise uses UTF-8 as default encoding. If the password contains non-ASCII characters, the password encoding must match what server encoding was set to when the password was created. For passwords in other character encodings, the encoding will have to be specified with this property (or with "characterEncoding"), as it's not possible for the driver to auto-detect this. +ConnectionProperties.exceptionInterceptors=Comma-delimited list of classes that implement com.mysql.jdbc.ExceptionInterceptor. These classes will be instantiated one per Connection instance, and all SQLExceptions thrown by the driver will be allowed to be intercepted by these interceptors, in a chained fashion, with the first class listed as the head of the chain. +ConnectionProperties.maxAllowedPacket=Maximum allowed packet size to send to server. If not set, the value of system variable 'max_allowed_packet' will be used to initialize this upon connecting. This value will not take effect if set larger than the value of 'max_allowed_packet'. Also, due to an internal dependency with the property "blobSendChunkSize", this setting has a minimum value of "8203" if "useServerPrepStmts" is set to "true". +ConnectionProperties.queryTimeoutKillsConnection=If the timeout given in Statement.setQueryTimeout() expires, should the driver forcibly abort the Connection instead of attempting to abort the query? +ConnectionProperties.authenticationPlugins=Comma-delimited list of classes that implement com.mysql.jdbc.AuthenticationPlugin and which will be used for authentication unless disabled by "disabledAuthenticationPlugins" property. +ConnectionProperties.disabledAuthenticationPlugins=Comma-delimited list of classes implementing com.mysql.jdbc.AuthenticationPlugin or mechanisms, i.e. "mysql_native_password". The authentication plugins or mechanisms listed will not be used for authentication which will fail if it requires one of them. It is an error to disable the default authentication plugin (either the one named by "defaultAuthenticationPlugin" property or the hard-coded one if "defaultAuthenticationPlugin" property is not set). +ConnectionProperties.defaultAuthenticationPlugin=Name of a class implementing com.mysql.jdbc.AuthenticationPlugin which will be used as the default authentication plugin (see below). It is an error to use a class which is not listed in "authenticationPlugins" nor it is one of the built-in plugins. It is an error to set as default a plugin which was disabled with "disabledAuthenticationPlugins" property. It is an error to set this value to null or the empty string (i.e. there must be at least a valid default authentication plugin specified for the connection, meeting all constraints listed above). +ConnectionProperties.parseInfoCacheFactory=Name of a class implementing com.mysql.jdbc.CacheAdapterFactory, which will be used to create caches for the parsed representation of client-side prepared statements. +ConnectionProperties.serverConfigCacheFactory=Name of a class implementing com.mysql.jdbc.CacheAdapterFactory>, which will be used to create caches for MySQL server configuration values +ConnectionProperties.disconnectOnExpiredPasswords=If "disconnectOnExpiredPasswords" is set to "false" and password is expired then server enters "sandbox" mode and sends ERR(08001, ER_MUST_CHANGE_PASSWORD) for all commands that are not needed to set a new password until a new password is set. +ConnectionProperties.connectionAttributes=A comma-delimited list of user-defined key:value pairs (in addition to standard MySQL-defined key:value pairs) to be passed to MySQL Server for display as connection attributes in the PERFORMANCE_SCHEMA.SESSION_CONNECT_ATTRS table. Example usage: connectionAttributes=key1:value1,key2:value2 This functionality is available for use with MySQL Server version 5.6 or later only. Earlier versions of MySQL Server do not support connection attributes, causing this configuration option to be ignored. Setting connectionAttributes=none will cause connection attribute processing to be bypassed, for situations where Connection creation/initialization speed is critical. +ConnectionProperties.getProceduresReturnsFunctions=Pre-JDBC4 DatabaseMetaData API has only the getProcedures() and getProcedureColumns() methods, so they return metadata info for both stored procedures and functions. JDBC4 was extended with the getFunctions() and getFunctionColumns() methods and the expected behaviours of previous methods are not well defined. For JDBC4 and higher, default 'true' value of the option means that calls of DatabaseMetaData.getProcedures() and DatabaseMetaData.getProcedureColumns() return metadata for both procedures and functions as before, keeping backward compatibility. Setting this property to 'false' decouples Connector/J from its pre-JDBC4 behaviours for DatabaseMetaData.getProcedures() and DatabaseMetaData.getProcedureColumns(), forcing them to return metadata for procedures only. +ConnectionProperties.detectCustomCollations=Should the driver detect custom charsets/collations installed on server (true/false, defaults to 'false'). If this option set to 'true' driver gets actual charsets/collations from server each time connection establishes. This could slow down connection initialization significantly. +ConnectionProperties.dontCheckOnDuplicateKeyUpdateInSQL=Stops checking if every INSERT statement contains the "ON DUPLICATE KEY UPDATE" clause. As a side effect, obtaining the statement's generated keys information will return a list where normally it wouldn't. Also be aware that, in this case, the list of generated keys returned may not be accurate. The effect of this property is canceled if set simultaneously with 'rewriteBatchedStatements=true'. +ConnectionProperties.readOnlyPropagatesToServer=Should the driver issue appropriate statements to implicitly set the transaction access mode on server side when Connection.setReadOnly() is called? Setting this property to 'true' enables InnoDB read-only potential optimizations but also requires an extra roundtrip to set the right transaction state. Even if this property is set to 'false', the driver will do its best effort to prevent the execution of database-state-changing queries. Requires minimum of MySQL 5.6. +ConnectionProperties.enabledSSLCipherSuites=If "useSSL" is set to "true", overrides the cipher suites enabled for use on the underlying SSL sockets. This may be required when using external JSSE providers or to specify cipher suites compatible with both MySQL server and used JVM. +ConnectionProperties.enableEscapeProcessing=Sets the default escape processing behavior for Statement objects. The method Statement.setEscapeProcessing() can be used to specify the escape processing behavior for an individual Statement object. Default escape processing behavior in prepared statements must be defined with the property 'processEscapeCodesForPrepStmts'. + +# +# Error Messages for Connection Properties +# + +ConnectionProperties.unableToInitDriverProperties=Unable to initialize driver properties due to +ConnectionProperties.unsupportedCharacterEncoding=Unsupported character encoding ''{0}''. +ConnectionProperties.errorNotExpected=Huh? +ConnectionProperties.InternalPropertiesFailure=Internal properties failure +ConnectionProperties.dynamicChangeIsNotAllowed=Dynamic change of ''{0}'' is not allowed. + +TimeUtil.UnrecognizedTimezoneId=The server time zone value ''{0}'' is unrecognized or represents more than one time zone. You must \ +configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a \ +more specifc time zone value if you want to utilize time zone support. +TimeUtil.LoadTimeZoneMappingError=Failed to load the time zone mapping resource file 'TimeZoneMapping.properties'. + +Connection.exceededConnectionLifetime=Ping or validation failed because configured connection lifetime exceeded. +Connection.badLifecycleInterceptor=Unable to load connection lifecycle interceptor ''{0}''. +MysqlIo.BadStatementInterceptor=Unable to load statement interceptor ''{0}''. +Connection.BadExceptionInterceptor=Unable to load exception interceptor ''{0}''. +Connection.CantDetectLocalConnect=Unable to determine if hostname ''{0}'' is local to this box because of exception, assuming it's not. +Connection.NoMetadataOnSocketFactory=Configured socket factory does not implement SocketMetadata, can not determine whether server is locally-connected, assuming not" +Connection.BadAuthenticationPlugin=Unable to load authentication plugin ''{0}''. +Connection.BadDefaultAuthenticationPlugin=Bad value ''{0}'' for property "defaultAuthenticationPlugin". +Connection.DefaultAuthenticationPluginIsNotListed=defaultAuthenticationPlugin ''{0}'' is not listed in "authenticationPlugins" nor it is one of the built-in plugins. +Connection.BadDisabledAuthenticationPlugin=Can''t disable the default plugin, either remove ''{0}'' from the disabled authentication plugins list, or choose a different default authentication plugin. +Connection.AuthenticationPluginRequiresSSL=SSL connection required for plugin ''{0}''. Check if "useSSL" is set to "true". +Connection.UnexpectedAuthenticationApproval=Unexpected authentication approval: ''{0}'' plugin did not reported "done" state but server has approved connection. +Connection.CantFindCacheFactory=Can not find class ''{0}'' specified by the ''{1}'' configuration property. +Connection.CantLoadCacheFactory=Can not load the cache factory ''{0}'' specified by the ''{1}'' configuration property. +Connection.LoginTimeout=Connection attempt exceeded defined timeout. + +LoadBalancedConnectionProxy.badValueForRetriesAllDown=Bad value ''{0}'' for property "retriesAllDown". +LoadBalancedConnectionProxy.badValueForLoadBalanceBlacklistTimeout=Bad value ''{0}'' for property "loadBalanceBlacklistTimeout". +LoadBalancedConnectionProxy.badValueForLoadBalanceHostRemovalGracePeriod=Bad value ''{0}'' for property "loadBalanceHostRemovalGracePeriod". +LoadBalancedConnectionProxy.badValueForLoadBalanceEnableJMX=Bad value ''{0}'' for property "loadBalanceEnableJMX". +LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementThreshold=Invalid numeric value ''{0}'' for property "loadBalanceAutoCommitStatementThreshold". +LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementRegex=Bad value ''{0}'' for property "loadBalanceAutoCommitStatementRegex". +LoadBalancedConnectionProxy.unusableConnection=The connection is unusable at the current state. There may be no hosts to connect to or all hosts this connection knows may be down at the moment. + +ReplicationConnectionProxy.badValueForAllowMasterDownConnections=Bad value ''{0}'' for property "allowMasterDownConnections". +ReplicationConnectionProxy.badValueForReadFromMasterWhenNoSlaves=Bad value ''{0}'' for property "readFromMasterWhenNoSlaves". +ReplicationConnectionProxy.badValueForReplicationEnableJMX=Bad value ''{0}'' for property "replicationEnableJMX". +ReplicationConnectionProxy.initializationWithEmptyHostsLists=A replication connection cannot be initialized without master hosts and slave hosts, simultaneously. +ReplicationConnectionProxy.noHostsInconsistentState=The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists. diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Messages.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Messages.java new file mode 100644 index 0000000..c617419 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Messages.java @@ -0,0 +1,102 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Support for localized messages. + */ +public class Messages { + + private static final String BUNDLE_NAME = "com.mysql.jdbc.LocalizedErrorMessages"; + + private static final ResourceBundle RESOURCE_BUNDLE; + + static { + ResourceBundle temp = null; + + // + // Overly-pedantic here, some appserver and JVM combos don't deal well with the no-args version, others don't deal well with the three-arg version, so + // we need to try both :( + // + + try { + temp = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault(), Messages.class.getClassLoader()); + } catch (Throwable t) { + try { + temp = ResourceBundle.getBundle(BUNDLE_NAME); + } catch (Throwable t2) { + RuntimeException rt = new RuntimeException("Can't load resource bundle due to underlying exception " + t.toString()); + rt.initCause(t2); + + throw rt; + } + } finally { + RESOURCE_BUNDLE = temp; + } + } + + /** + * Returns the localized message for the given message key + * + * @param key + * the message key + * @return The localized message for the key + */ + public static String getString(String key) { + if (RESOURCE_BUNDLE == null) { + throw new RuntimeException("Localized messages from resource bundle '" + BUNDLE_NAME + "' not loaded during initialization of driver."); + } + + try { + if (key == null) { + throw new IllegalArgumentException("Message key can not be null"); + } + + String message = RESOURCE_BUNDLE.getString(key); + + if (message == null) { + message = "Missing error message for key '" + key + "'"; + } + + return message; + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } + + public static String getString(String key, Object[] args) { + return MessageFormat.format(getString(key), args); + } + + /** + * Dis-allow construction ... + */ + private Messages() { + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MiniAdmin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MiniAdmin.java new file mode 100644 index 0000000..cfd4ba1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MiniAdmin.java @@ -0,0 +1,96 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +/** + * Utility functions for admin functionality from Java. + */ +public class MiniAdmin { + private Connection conn; + + /** + * Create a new MiniAdmin using the given connection + * + * @param conn + * the existing connection to use. + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(java.sql.Connection conn) throws SQLException { + if (conn == null) { + throw SQLError.createSQLException(Messages.getString("MiniAdmin.0"), SQLError.SQL_STATE_GENERAL_ERROR, null); + } + + if (!(conn instanceof Connection)) { + throw SQLError.createSQLException(Messages.getString("MiniAdmin.1"), SQLError.SQL_STATE_GENERAL_ERROR, + ((com.mysql.jdbc.ConnectionImpl) conn).getExceptionInterceptor()); + } + + this.conn = (Connection) conn; + } + + /** + * Create a new MiniAdmin, connecting using the given JDBC URL. + * + * @param jdbcUrl + * the JDBC URL to use + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(String jdbcUrl) throws SQLException { + this(jdbcUrl, new Properties()); + } + + /** + * Create a new MiniAdmin, connecting using the given JDBC URL and + * properties + * + * @param jdbcUrl + * the JDBC URL to use + * @param props + * the properties to use when connecting + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(String jdbcUrl, Properties props) throws SQLException { + this.conn = (Connection) (new Driver().connect(jdbcUrl, props)); + } + + /** + * Shuts down the MySQL server at the other end of the connection that this + * MiniAdmin was created from/for. + * + * @throws SQLException + * if an error occurs + */ + public void shutdown() throws SQLException { + this.conn.shutdownServer(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostConnectionProxy.java new file mode 100644 index 0000000..d0fe140 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostConnectionProxy.java @@ -0,0 +1,481 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; + +/** + * An abstract class that processes generic multi-host configurations. This class has to be sub-classed by specific multi-host implementations, such as + * load-balancing and failover. + */ +public abstract class MultiHostConnectionProxy implements InvocationHandler { + private static final String METHOD_GET_MULTI_HOST_SAFE_PROXY = "getMultiHostSafeProxy"; + private static final String METHOD_EQUALS = "equals"; + private static final String METHOD_HASH_CODE = "hashCode"; + private static final String METHOD_CLOSE = "close"; + private static final String METHOD_ABORT_INTERNAL = "abortInternal"; + private static final String METHOD_ABORT = "abort"; + private static final String METHOD_IS_CLOSED = "isClosed"; + private static final String METHOD_GET_AUTO_COMMIT = "getAutoCommit"; + private static final String METHOD_GET_CATALOG = "getCatalog"; + private static final String METHOD_GET_TRANSACTION_ISOLATION = "getTransactionIsolation"; + private static final String METHOD_GET_SESSION_MAX_ROWS = "getSessionMaxRows"; + + List hostList; + Properties localProps; + + boolean autoReconnect = false; + + MySQLConnection thisAsConnection = null; + MySQLConnection proxyConnection = null; + + MySQLConnection currentConnection = null; + + boolean isClosed = false; + boolean closedExplicitly = false; + String closedReason = null; + + // Keep track of the last exception processed in 'dealWithInvocationException()' in order to avoid creating connections repeatedly from each time the same + // exception is caught in every proxy instance belonging to the same call stack. + protected Throwable lastExceptionDealtWith = null; + + private static Constructor JDBC_4_MS_CONNECTION_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_MS_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4MultiHostMySQLConnection") + .getConstructor(new Class[] { MultiHostConnectionProxy.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Proxy class to intercept and deal with errors that may occur in any object bound to the current connection. + */ + class JdbcInterfaceProxy implements InvocationHandler { + Object invokeOn = null; + + JdbcInterfaceProxy(Object toInvokeOn) { + this.invokeOn = toInvokeOn; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + synchronized (MultiHostConnectionProxy.this) { + Object result = null; + + try { + result = method.invoke(this.invokeOn, args); + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } + + return result; + } + } + } + + /** + * Initializes a connection wrapper for this MultiHostConnectionProxy instance. + * + * @param props + * The properties to be used in new internal connections. + */ + MultiHostConnectionProxy() throws SQLException { + this.thisAsConnection = getNewWrapperForThisAsConnection(); + } + + /** + * Constructs a MultiHostConnectionProxy instance for the given list of hosts and connection properties. + * + * @param hosts + * The lists of hosts available to switch on. + * @param props + * The properties to be used in new internal connections. + */ + MultiHostConnectionProxy(List hosts, Properties props) throws SQLException { + this(); + initializeHostsSpecs(hosts, props); + } + + /** + * Initializes the hosts lists and makes a "clean" local copy of the given connection properties so that it can be later used to create standard + * connections. + * + * @param hosts + * The list of hosts for this multi-host connection. + * @param props + * Connection properties from where to get initial settings and to be used in new connections. + * @return + * The number of hosts found in the hosts list. + */ + int initializeHostsSpecs(List hosts, Properties props) { + this.autoReconnect = "true".equalsIgnoreCase(props.getProperty("autoReconnect")) || "true".equalsIgnoreCase(props.getProperty("autoReconnectForPools")); + + this.hostList = hosts; + int numHosts = this.hostList.size(); + + this.localProps = (Properties) props.clone(); + this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + + for (int i = 0; i < numHosts; i++) { + this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + "." + (i + 1)); + this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + "." + (i + 1)); + } + + this.localProps.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + this.localProps.setProperty("useLocalSessionState", "true"); + + return numHosts; + } + + /** + * Wraps this object with a new multi-host Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + MySQLConnection getNewWrapperForThisAsConnection() throws SQLException { + if (Util.isJdbc4() || JDBC_4_MS_CONNECTION_CTOR != null) { + return (MySQLConnection) Util.handleNewInstance(JDBC_4_MS_CONNECTION_CTOR, new Object[] { this }, null); + } + + return new MultiHostMySQLConnection(this); + } + + /** + * Get this connection's proxy. + * A multi-host connection may not be at top level in the multi-host connections chain. In such case the first connection in the chain is available as a + * proxy. + * + * @return + * Returns this connection's proxy if there is one or itself if this is the first one. + */ + protected MySQLConnection getProxy() { + return this.proxyConnection != null ? this.proxyConnection : this.thisAsConnection; + } + + /** + * Sets this connection's proxy. This proxy should be the first connection in the multi-host connections chain. + * After setting the connection proxy locally, propagates it through the dependant connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + protected final void setProxy(MySQLConnection proxyConn) { + this.proxyConnection = proxyConn; + propagateProxyDown(proxyConn); + } + + /** + * Propagates the connection proxy down through the multi-host connections chain. + * This method is intended to be overridden in subclasses that manage more than one active connection at same time. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + protected void propagateProxyDown(MySQLConnection proxyConn) { + this.currentConnection.setProxy(proxyConn); + } + + /** + * If the given return type is or implements a JDBC interface, proxies the given object so that we can catch SQL errors and fire a connection switch. + * + * @param returnType + * The type the object instance to proxy is supposed to be. + * @param toProxy + * The object instance to proxy. + * @return + * The proxied object or the original one if it does not implement a JDBC interface. + */ + Object proxyIfReturnTypeIsJdbcInterface(Class returnType, Object toProxy) { + if (toProxy != null) { + if (Util.isJdbcInterface(returnType)) { + Class toProxyClass = toProxy.getClass(); + return Proxy.newProxyInstance(toProxyClass.getClassLoader(), Util.getImplementedInterfaces(toProxyClass), getNewJdbcInterfaceProxy(toProxy)); + } + } + return toProxy; + } + + /** + * Instantiates a new JdbcInterfaceProxy for the given object. Subclasses can override this to return instances of JdbcInterfaceProxy subclasses. + * + * @param toProxy + * The object instance to be proxied. + * @return + * The new InvocationHandler instance. + */ + InvocationHandler getNewJdbcInterfaceProxy(Object toProxy) { + return new JdbcInterfaceProxy(toProxy); + } + + /** + * Deals with InvocationException from proxied objects. + * + * @param e + * The Exception instance to check. + */ + void dealWithInvocationException(InvocationTargetException e) throws SQLException, Throwable, InvocationTargetException { + Throwable t = e.getTargetException(); + + if (t != null) { + if (this.lastExceptionDealtWith != t && shouldExceptionTriggerConnectionSwitch(t)) { + invalidateCurrentConnection(); + pickNewConnection(); + this.lastExceptionDealtWith = t; + } + throw t; + } + throw e; + } + + /** + * Checks if the given throwable should trigger a connection switch. + * + * @param t + * The Throwable instance to analyze. + */ + abstract boolean shouldExceptionTriggerConnectionSwitch(Throwable t); + + /** + * Checks if current connection is to a master host. + */ + abstract boolean isMasterConnection(); + + /** + * Invalidates the current connection. + */ + synchronized void invalidateCurrentConnection() throws SQLException { + invalidateConnection(this.currentConnection); + } + + /** + * Invalidates the specified connection by closing it. + * + * @param conn + * The connection instance to invalidate. + */ + synchronized void invalidateConnection(MySQLConnection conn) throws SQLException { + try { + if (conn != null && !conn.isClosed()) { + conn.realClose(true, !conn.getAutoCommit(), true, null); + } + } catch (SQLException e) { + // swallow this exception, current connection should be useless anyway. + } + } + + /** + * Picks the "best" connection to use from now on. Each subclass needs to implement its connection switch strategy on it. + */ + abstract void pickNewConnection() throws SQLException; + + /** + * Creates a new physical connection for the given host:port. + * + * @param hostPortSpec + * The host:port specification. + * @return + * The new Connection instance. + */ + synchronized ConnectionImpl createConnectionForHost(String hostPortSpec) throws SQLException { + Properties connProps = (Properties) this.localProps.clone(); + + String[] hostPortPair = NonRegisteringDriver.parseHostPortPair(hostPortSpec); + String hostName = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX]; + String portNumber = hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]; + String dbName = connProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + if (hostName == null) { + throw new SQLException("Could not find a hostname to start a connection to"); + } + if (portNumber == null) { + portNumber = "3306"; // use default + } + + connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, hostName); + connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, portNumber); + connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1", hostName); + connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1", portNumber); + connProps.setProperty(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY, "1"); + connProps.setProperty("roundRobinLoadBalance", "false"); // make sure we don't pickup the default value + + ConnectionImpl conn = (ConnectionImpl) ConnectionImpl.getInstance(hostName, Integer.parseInt(portNumber), connProps, dbName, + "jdbc:mysql://" + hostName + ":" + portNumber + "/"); + + conn.setProxy(getProxy()); + + return conn; + } + + /** + * Synchronizes session state between two connections. + * + * @param source + * The connection where to get state from. + * @param target + * The connection where to set state. + */ + static void syncSessionState(Connection source, Connection target) throws SQLException { + if (source == null || target == null) { + return; + } + syncSessionState(source, target, source.isReadOnly()); + } + + /** + * Synchronizes session state between two connections, allowing to override the read-only status. + * + * @param source + * The connection where to get state from. + * @param target + * The connection where to set state. + * @param readOnly + * The new read-only status. + */ + static void syncSessionState(Connection source, Connection target, boolean readOnly) throws SQLException { + if (target != null) { + target.setReadOnly(readOnly); + } + + if (source == null || target == null) { + return; + } + target.setAutoCommit(source.getAutoCommit()); + target.setCatalog(source.getCatalog()); + target.setTransactionIsolation(source.getTransactionIsolation()); + target.setSessionMaxRows(source.getSessionMaxRows()); + } + + /** + * Executes a close() invocation; + */ + abstract void doClose() throws SQLException; + + /** + * Executes a abortInternal() invocation; + */ + abstract void doAbortInternal() throws SQLException; + + /** + * Executes a abort() invocation; + */ + abstract void doAbort(Executor executor) throws SQLException; + + /** + * Proxies method invocation on the java.sql.Connection interface, trapping multi-host specific methods and generic methods. + * Subclasses have to override this to complete the method invocation process, deal with exceptions and decide when to switch connection. + * To avoid unnecessary additional exception handling overriders should consult #canDealWith(Method) before chaining here. + */ + public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (METHOD_GET_MULTI_HOST_SAFE_PROXY.equals(methodName)) { + return this.thisAsConnection; + } + + if (METHOD_EQUALS.equals(methodName)) { + // Let args[0] "unwrap" to its InvocationHandler if it is a proxy. + return args[0].equals(this); + } + + if (METHOD_HASH_CODE.equals(methodName)) { + return this.hashCode(); + } + + if (METHOD_CLOSE.equals(methodName)) { + doClose(); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + this.closedExplicitly = true; + return null; + } + + if (METHOD_ABORT_INTERNAL.equals(methodName)) { + doAbortInternal(); + this.currentConnection.abortInternal(); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + return null; + } + + if (METHOD_ABORT.equals(methodName) && args.length == 1) { + doAbort((Executor) args[0]); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + return null; + } + + if (METHOD_IS_CLOSED.equals(methodName)) { + return this.isClosed; + } + + try { + return invokeMore(proxy, method, args); + } catch (InvocationTargetException e) { + throw e.getCause() != null ? e.getCause() : e; + } catch (Exception e) { + // Check if the captured exception must be wrapped by an unchecked exception. + Class[] declaredException = method.getExceptionTypes(); + for (Class declEx : declaredException) { + if (declEx.isAssignableFrom(e.getClass())) { + throw e; + } + } + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * Continuation of the method invocation process, to be implemented within each subclass. + */ + abstract Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable; + + /** + * Checks if the given method is allowed on closed connections. + */ + protected boolean allowedOnClosedConnection(Method method) { + String methodName = method.getName(); + + return methodName.equals(METHOD_GET_AUTO_COMMIT) || methodName.equals(METHOD_GET_CATALOG) || methodName.equals(METHOD_GET_TRANSACTION_ISOLATION) + || methodName.equals(METHOD_GET_SESSION_MAX_ROWS); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostMySQLConnection.java new file mode 100644 index 0000000..4e20ca6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MultiHostMySQLConnection.java @@ -0,0 +1,2485 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.CallableStatement; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Savepoint; +import java.sql.Statement; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; +import java.util.Timer; +import java.util.concurrent.Executor; + +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +/** + * Each instance of MultiHostMySQLConnection is coupled with a MultiHostConnectionProxy instance. + * + * While this class implements MySQLConnection directly, MultiHostConnectionProxy does the same but via a dynamic proxy. + * + * Most of the methods in this class refer directly to the active connection from its MultiHostConnectionProxy pair, providing a non-proxied access to the + * current active connection managed by this multi-host structure. The remaining methods either implement some local behavior or refer to the proxy itself + * instead of the sub-connection. + * + * Referring to the higher level proxy connection is needed when some operation needs to be extended to all open sub-connections existing in this multi-host + * structure as opposed to just refer to the active current connection, such as with close() which is most likely required to close all sub-connections as + * well. + */ +public class MultiHostMySQLConnection implements MySQLConnection { + /** + * thisAsProxy holds the proxy (MultiHostConnectionProxy or one of its subclasses) this connection is associated with. + * It is used as a gateway to the current active sub-connection managed by this multi-host structure or as a target to where some of the methods implemented + * here in this class refer to. + */ + protected MultiHostConnectionProxy thisAsProxy; + + public MultiHostMySQLConnection(MultiHostConnectionProxy proxy) { + this.thisAsProxy = proxy; + } + + protected MultiHostConnectionProxy getThisAsProxy() { + return this.thisAsProxy; + } + + protected MySQLConnection getActiveMySQLConnection() { + synchronized (this.thisAsProxy) { + return this.thisAsProxy.currentConnection; + } + } + + public void abortInternal() throws SQLException { + getActiveMySQLConnection().abortInternal(); + } + + public void changeUser(String userName, String newPassword) throws SQLException { + getActiveMySQLConnection().changeUser(userName, newPassword); + } + + public void checkClosed() throws SQLException { + getActiveMySQLConnection().checkClosed(); + } + + @Deprecated + public void clearHasTriedMaster() { + getActiveMySQLConnection().clearHasTriedMaster(); + } + + public void clearWarnings() throws SQLException { + getActiveMySQLConnection().clearWarnings(); + } + + public PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement clientPrepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql); + } + + public void close() throws SQLException { + getActiveMySQLConnection().close(); + } + + public void commit() throws SQLException { + getActiveMySQLConnection().commit(); + } + + public void createNewIO(boolean isForReconnect) throws SQLException { + getActiveMySQLConnection().createNewIO(isForReconnect); + } + + public Statement createStatement() throws SQLException { + return getActiveMySQLConnection().createStatement(); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().createStatement(resultSetType, resultSetConcurrency); + } + + public void dumpTestcaseQuery(String query) { + getActiveMySQLConnection().dumpTestcaseQuery(query); + } + + public Connection duplicate() throws SQLException { + return getActiveMySQLConnection().duplicate(); + } + + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException { + return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata, isBatch); + } + + public ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata) throws SQLException { + return getActiveMySQLConnection().execSQL(callingStatement, sql, maxRows, packet, resultSetType, resultSetConcurrency, streamResults, catalog, + cachedMetadata); + } + + public String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket, int endOfQueryPacketPosition) throws SQLException { + return getActiveMySQLConnection().extractSqlFromPacket(possibleSqlQuery, queryPacket, endOfQueryPacketPosition); + } + + public String exposeAsXml() throws SQLException { + return getActiveMySQLConnection().exposeAsXml(); + } + + public boolean getAllowLoadLocalInfile() { + return getActiveMySQLConnection().getAllowLoadLocalInfile(); + } + + public boolean getAllowMultiQueries() { + return getActiveMySQLConnection().getAllowMultiQueries(); + } + + public boolean getAllowNanAndInf() { + return getActiveMySQLConnection().getAllowNanAndInf(); + } + + public boolean getAllowUrlInLocalInfile() { + return getActiveMySQLConnection().getAllowUrlInLocalInfile(); + } + + public boolean getAlwaysSendSetIsolation() { + return getActiveMySQLConnection().getAlwaysSendSetIsolation(); + } + + public boolean getAutoClosePStmtStreams() { + return getActiveMySQLConnection().getAutoClosePStmtStreams(); + } + + public boolean getAutoDeserialize() { + return getActiveMySQLConnection().getAutoDeserialize(); + } + + public boolean getAutoGenerateTestcaseScript() { + return getActiveMySQLConnection().getAutoGenerateTestcaseScript(); + } + + public boolean getAutoReconnectForPools() { + return getActiveMySQLConnection().getAutoReconnectForPools(); + } + + public boolean getAutoSlowLog() { + return getActiveMySQLConnection().getAutoSlowLog(); + } + + public int getBlobSendChunkSize() { + return getActiveMySQLConnection().getBlobSendChunkSize(); + } + + public boolean getBlobsAreStrings() { + return getActiveMySQLConnection().getBlobsAreStrings(); + } + + public boolean getCacheCallableStatements() { + return getActiveMySQLConnection().getCacheCallableStatements(); + } + + public boolean getCacheCallableStmts() { + return getActiveMySQLConnection().getCacheCallableStmts(); + } + + public boolean getCachePrepStmts() { + return getActiveMySQLConnection().getCachePrepStmts(); + } + + public boolean getCachePreparedStatements() { + return getActiveMySQLConnection().getCachePreparedStatements(); + } + + public boolean getCacheResultSetMetadata() { + return getActiveMySQLConnection().getCacheResultSetMetadata(); + } + + public boolean getCacheServerConfiguration() { + return getActiveMySQLConnection().getCacheServerConfiguration(); + } + + public int getCallableStatementCacheSize() { + return getActiveMySQLConnection().getCallableStatementCacheSize(); + } + + public int getCallableStmtCacheSize() { + return getActiveMySQLConnection().getCallableStmtCacheSize(); + } + + public boolean getCapitalizeTypeNames() { + return getActiveMySQLConnection().getCapitalizeTypeNames(); + } + + public String getCharacterSetResults() { + return getActiveMySQLConnection().getCharacterSetResults(); + } + + public String getClientCertificateKeyStorePassword() { + return getActiveMySQLConnection().getClientCertificateKeyStorePassword(); + } + + public String getClientCertificateKeyStoreType() { + return getActiveMySQLConnection().getClientCertificateKeyStoreType(); + } + + public String getClientCertificateKeyStoreUrl() { + return getActiveMySQLConnection().getClientCertificateKeyStoreUrl(); + } + + public String getClientInfoProvider() { + return getActiveMySQLConnection().getClientInfoProvider(); + } + + public String getClobCharacterEncoding() { + return getActiveMySQLConnection().getClobCharacterEncoding(); + } + + public boolean getClobberStreamingResults() { + return getActiveMySQLConnection().getClobberStreamingResults(); + } + + public boolean getCompensateOnDuplicateKeyUpdateCounts() { + return getActiveMySQLConnection().getCompensateOnDuplicateKeyUpdateCounts(); + } + + public int getConnectTimeout() { + return getActiveMySQLConnection().getConnectTimeout(); + } + + public String getConnectionCollation() { + return getActiveMySQLConnection().getConnectionCollation(); + } + + public String getConnectionLifecycleInterceptors() { + return getActiveMySQLConnection().getConnectionLifecycleInterceptors(); + } + + public boolean getContinueBatchOnError() { + return getActiveMySQLConnection().getContinueBatchOnError(); + } + + public boolean getCreateDatabaseIfNotExist() { + return getActiveMySQLConnection().getCreateDatabaseIfNotExist(); + } + + public int getDefaultFetchSize() { + return getActiveMySQLConnection().getDefaultFetchSize(); + } + + public boolean getDontTrackOpenResources() { + return getActiveMySQLConnection().getDontTrackOpenResources(); + } + + public boolean getDumpMetadataOnColumnNotFound() { + return getActiveMySQLConnection().getDumpMetadataOnColumnNotFound(); + } + + public boolean getDumpQueriesOnException() { + return getActiveMySQLConnection().getDumpQueriesOnException(); + } + + public boolean getDynamicCalendars() { + return getActiveMySQLConnection().getDynamicCalendars(); + } + + public boolean getElideSetAutoCommits() { + return getActiveMySQLConnection().getElideSetAutoCommits(); + } + + public boolean getEmptyStringsConvertToZero() { + return getActiveMySQLConnection().getEmptyStringsConvertToZero(); + } + + public boolean getEmulateLocators() { + return getActiveMySQLConnection().getEmulateLocators(); + } + + public boolean getEmulateUnsupportedPstmts() { + return getActiveMySQLConnection().getEmulateUnsupportedPstmts(); + } + + public boolean getEnablePacketDebug() { + return getActiveMySQLConnection().getEnablePacketDebug(); + } + + public boolean getEnableQueryTimeouts() { + return getActiveMySQLConnection().getEnableQueryTimeouts(); + } + + public String getEncoding() { + return getActiveMySQLConnection().getEncoding(); + } + + public String getExceptionInterceptors() { + return getActiveMySQLConnection().getExceptionInterceptors(); + } + + public boolean getExplainSlowQueries() { + return getActiveMySQLConnection().getExplainSlowQueries(); + } + + public boolean getFailOverReadOnly() { + return getActiveMySQLConnection().getFailOverReadOnly(); + } + + public boolean getFunctionsNeverReturnBlobs() { + return getActiveMySQLConnection().getFunctionsNeverReturnBlobs(); + } + + public boolean getGatherPerfMetrics() { + return getActiveMySQLConnection().getGatherPerfMetrics(); + } + + public boolean getGatherPerformanceMetrics() { + return getActiveMySQLConnection().getGatherPerformanceMetrics(); + } + + public boolean getGenerateSimpleParameterMetadata() { + return getActiveMySQLConnection().getGenerateSimpleParameterMetadata(); + } + + public boolean getIgnoreNonTxTables() { + return getActiveMySQLConnection().getIgnoreNonTxTables(); + } + + public boolean getIncludeInnodbStatusInDeadlockExceptions() { + return getActiveMySQLConnection().getIncludeInnodbStatusInDeadlockExceptions(); + } + + public int getInitialTimeout() { + return getActiveMySQLConnection().getInitialTimeout(); + } + + public boolean getInteractiveClient() { + return getActiveMySQLConnection().getInteractiveClient(); + } + + public boolean getIsInteractiveClient() { + return getActiveMySQLConnection().getIsInteractiveClient(); + } + + public boolean getJdbcCompliantTruncation() { + return getActiveMySQLConnection().getJdbcCompliantTruncation(); + } + + public boolean getJdbcCompliantTruncationForReads() { + return getActiveMySQLConnection().getJdbcCompliantTruncationForReads(); + } + + public String getLargeRowSizeThreshold() { + return getActiveMySQLConnection().getLargeRowSizeThreshold(); + } + + public int getLoadBalanceBlacklistTimeout() { + return getActiveMySQLConnection().getLoadBalanceBlacklistTimeout(); + } + + public int getLoadBalancePingTimeout() { + return getActiveMySQLConnection().getLoadBalancePingTimeout(); + } + + public String getLoadBalanceStrategy() { + return getActiveMySQLConnection().getLoadBalanceStrategy(); + } + + public boolean getLoadBalanceValidateConnectionOnSwapServer() { + return getActiveMySQLConnection().getLoadBalanceValidateConnectionOnSwapServer(); + } + + public String getLocalSocketAddress() { + return getActiveMySQLConnection().getLocalSocketAddress(); + } + + public int getLocatorFetchBufferSize() { + return getActiveMySQLConnection().getLocatorFetchBufferSize(); + } + + public boolean getLogSlowQueries() { + return getActiveMySQLConnection().getLogSlowQueries(); + } + + public boolean getLogXaCommands() { + return getActiveMySQLConnection().getLogXaCommands(); + } + + public String getLogger() { + return getActiveMySQLConnection().getLogger(); + } + + public String getLoggerClassName() { + return getActiveMySQLConnection().getLoggerClassName(); + } + + public boolean getMaintainTimeStats() { + return getActiveMySQLConnection().getMaintainTimeStats(); + } + + public int getMaxAllowedPacket() { + return getActiveMySQLConnection().getMaxAllowedPacket(); + } + + public int getMaxQuerySizeToLog() { + return getActiveMySQLConnection().getMaxQuerySizeToLog(); + } + + public int getMaxReconnects() { + return getActiveMySQLConnection().getMaxReconnects(); + } + + public int getMaxRows() { + return getActiveMySQLConnection().getMaxRows(); + } + + public int getMetadataCacheSize() { + return getActiveMySQLConnection().getMetadataCacheSize(); + } + + public int getNetTimeoutForStreamingResults() { + return getActiveMySQLConnection().getNetTimeoutForStreamingResults(); + } + + public boolean getNoAccessToProcedureBodies() { + return getActiveMySQLConnection().getNoAccessToProcedureBodies(); + } + + public boolean getNoDatetimeStringSync() { + return getActiveMySQLConnection().getNoDatetimeStringSync(); + } + + public boolean getNoTimezoneConversionForTimeType() { + return getActiveMySQLConnection().getNoTimezoneConversionForTimeType(); + } + + public boolean getNoTimezoneConversionForDateType() { + return getActiveMySQLConnection().getNoTimezoneConversionForDateType(); + } + + public boolean getCacheDefaultTimezone() { + return getActiveMySQLConnection().getCacheDefaultTimezone(); + } + + public boolean getNullCatalogMeansCurrent() { + return getActiveMySQLConnection().getNullCatalogMeansCurrent(); + } + + public boolean getNullNamePatternMatchesAll() { + return getActiveMySQLConnection().getNullNamePatternMatchesAll(); + } + + public boolean getOverrideSupportsIntegrityEnhancementFacility() { + return getActiveMySQLConnection().getOverrideSupportsIntegrityEnhancementFacility(); + } + + public int getPacketDebugBufferSize() { + return getActiveMySQLConnection().getPacketDebugBufferSize(); + } + + public boolean getPadCharsWithSpace() { + return getActiveMySQLConnection().getPadCharsWithSpace(); + } + + public boolean getParanoid() { + return getActiveMySQLConnection().getParanoid(); + } + + public String getPasswordCharacterEncoding() { + return getActiveMySQLConnection().getPasswordCharacterEncoding(); + } + + public boolean getPedantic() { + return getActiveMySQLConnection().getPedantic(); + } + + public boolean getPinGlobalTxToPhysicalConnection() { + return getActiveMySQLConnection().getPinGlobalTxToPhysicalConnection(); + } + + public boolean getPopulateInsertRowWithDefaultValues() { + return getActiveMySQLConnection().getPopulateInsertRowWithDefaultValues(); + } + + public int getPrepStmtCacheSize() { + return getActiveMySQLConnection().getPrepStmtCacheSize(); + } + + public int getPrepStmtCacheSqlLimit() { + return getActiveMySQLConnection().getPrepStmtCacheSqlLimit(); + } + + public int getPreparedStatementCacheSize() { + return getActiveMySQLConnection().getPreparedStatementCacheSize(); + } + + public int getPreparedStatementCacheSqlLimit() { + return getActiveMySQLConnection().getPreparedStatementCacheSqlLimit(); + } + + public boolean getProcessEscapeCodesForPrepStmts() { + return getActiveMySQLConnection().getProcessEscapeCodesForPrepStmts(); + } + + public boolean getProfileSQL() { + return getActiveMySQLConnection().getProfileSQL(); + } + + public boolean getProfileSql() { + return getActiveMySQLConnection().getProfileSql(); + } + + public String getProfilerEventHandler() { + return getActiveMySQLConnection().getProfilerEventHandler(); + } + + public String getPropertiesTransform() { + return getActiveMySQLConnection().getPropertiesTransform(); + } + + public int getQueriesBeforeRetryMaster() { + return getActiveMySQLConnection().getQueriesBeforeRetryMaster(); + } + + public boolean getQueryTimeoutKillsConnection() { + return getActiveMySQLConnection().getQueryTimeoutKillsConnection(); + } + + public boolean getReconnectAtTxEnd() { + return getActiveMySQLConnection().getReconnectAtTxEnd(); + } + + public boolean getRelaxAutoCommit() { + return getActiveMySQLConnection().getRelaxAutoCommit(); + } + + public int getReportMetricsIntervalMillis() { + return getActiveMySQLConnection().getReportMetricsIntervalMillis(); + } + + public boolean getRequireSSL() { + return getActiveMySQLConnection().getRequireSSL(); + } + + public String getResourceId() { + return getActiveMySQLConnection().getResourceId(); + } + + public int getResultSetSizeThreshold() { + return getActiveMySQLConnection().getResultSetSizeThreshold(); + } + + public boolean getRetainStatementAfterResultSetClose() { + return getActiveMySQLConnection().getRetainStatementAfterResultSetClose(); + } + + public int getRetriesAllDown() { + return getActiveMySQLConnection().getRetriesAllDown(); + } + + public boolean getRewriteBatchedStatements() { + return getActiveMySQLConnection().getRewriteBatchedStatements(); + } + + public boolean getRollbackOnPooledClose() { + return getActiveMySQLConnection().getRollbackOnPooledClose(); + } + + public boolean getRoundRobinLoadBalance() { + return getActiveMySQLConnection().getRoundRobinLoadBalance(); + } + + public boolean getRunningCTS13() { + return getActiveMySQLConnection().getRunningCTS13(); + } + + public int getSecondsBeforeRetryMaster() { + return getActiveMySQLConnection().getSecondsBeforeRetryMaster(); + } + + public int getSelfDestructOnPingMaxOperations() { + return getActiveMySQLConnection().getSelfDestructOnPingMaxOperations(); + } + + public int getSelfDestructOnPingSecondsLifetime() { + return getActiveMySQLConnection().getSelfDestructOnPingSecondsLifetime(); + } + + public String getServerTimezone() { + return getActiveMySQLConnection().getServerTimezone(); + } + + public String getSessionVariables() { + return getActiveMySQLConnection().getSessionVariables(); + } + + public int getSlowQueryThresholdMillis() { + return getActiveMySQLConnection().getSlowQueryThresholdMillis(); + } + + public long getSlowQueryThresholdNanos() { + return getActiveMySQLConnection().getSlowQueryThresholdNanos(); + } + + public String getSocketFactory() { + return getActiveMySQLConnection().getSocketFactory(); + } + + public String getSocketFactoryClassName() { + return getActiveMySQLConnection().getSocketFactoryClassName(); + } + + public int getSocketTimeout() { + return getActiveMySQLConnection().getSocketTimeout(); + } + + public String getStatementInterceptors() { + return getActiveMySQLConnection().getStatementInterceptors(); + } + + public boolean getStrictFloatingPoint() { + return getActiveMySQLConnection().getStrictFloatingPoint(); + } + + public boolean getStrictUpdates() { + return getActiveMySQLConnection().getStrictUpdates(); + } + + public boolean getTcpKeepAlive() { + return getActiveMySQLConnection().getTcpKeepAlive(); + } + + public boolean getTcpNoDelay() { + return getActiveMySQLConnection().getTcpNoDelay(); + } + + public int getTcpRcvBuf() { + return getActiveMySQLConnection().getTcpRcvBuf(); + } + + public int getTcpSndBuf() { + return getActiveMySQLConnection().getTcpSndBuf(); + } + + public int getTcpTrafficClass() { + return getActiveMySQLConnection().getTcpTrafficClass(); + } + + public boolean getTinyInt1isBit() { + return getActiveMySQLConnection().getTinyInt1isBit(); + } + + public boolean getTraceProtocol() { + return getActiveMySQLConnection().getTraceProtocol(); + } + + public boolean getTransformedBitIsBoolean() { + return getActiveMySQLConnection().getTransformedBitIsBoolean(); + } + + public boolean getTreatUtilDateAsTimestamp() { + return getActiveMySQLConnection().getTreatUtilDateAsTimestamp(); + } + + public String getTrustCertificateKeyStorePassword() { + return getActiveMySQLConnection().getTrustCertificateKeyStorePassword(); + } + + public String getTrustCertificateKeyStoreType() { + return getActiveMySQLConnection().getTrustCertificateKeyStoreType(); + } + + public String getTrustCertificateKeyStoreUrl() { + return getActiveMySQLConnection().getTrustCertificateKeyStoreUrl(); + } + + public boolean getUltraDevHack() { + return getActiveMySQLConnection().getUltraDevHack(); + } + + public boolean getUseAffectedRows() { + return getActiveMySQLConnection().getUseAffectedRows(); + } + + public boolean getUseBlobToStoreUTF8OutsideBMP() { + return getActiveMySQLConnection().getUseBlobToStoreUTF8OutsideBMP(); + } + + public boolean getUseColumnNamesInFindColumn() { + return getActiveMySQLConnection().getUseColumnNamesInFindColumn(); + } + + public boolean getUseCompression() { + return getActiveMySQLConnection().getUseCompression(); + } + + public String getUseConfigs() { + return getActiveMySQLConnection().getUseConfigs(); + } + + public boolean getUseCursorFetch() { + return getActiveMySQLConnection().getUseCursorFetch(); + } + + public boolean getUseDirectRowUnpack() { + return getActiveMySQLConnection().getUseDirectRowUnpack(); + } + + public boolean getUseDynamicCharsetInfo() { + return getActiveMySQLConnection().getUseDynamicCharsetInfo(); + } + + public boolean getUseFastDateParsing() { + return getActiveMySQLConnection().getUseFastDateParsing(); + } + + public boolean getUseFastIntParsing() { + return getActiveMySQLConnection().getUseFastIntParsing(); + } + + public boolean getUseGmtMillisForDatetimes() { + return getActiveMySQLConnection().getUseGmtMillisForDatetimes(); + } + + public boolean getUseHostsInPrivileges() { + return getActiveMySQLConnection().getUseHostsInPrivileges(); + } + + public boolean getUseInformationSchema() { + return getActiveMySQLConnection().getUseInformationSchema(); + } + + public boolean getUseJDBCCompliantTimezoneShift() { + return getActiveMySQLConnection().getUseJDBCCompliantTimezoneShift(); + } + + public boolean getUseJvmCharsetConverters() { + return getActiveMySQLConnection().getUseJvmCharsetConverters(); + } + + public boolean getUseLegacyDatetimeCode() { + return getActiveMySQLConnection().getUseLegacyDatetimeCode(); + } + + public boolean getSendFractionalSeconds() { + return getActiveMySQLConnection().getSendFractionalSeconds(); + } + + public boolean getUseLocalSessionState() { + return getActiveMySQLConnection().getUseLocalSessionState(); + } + + public boolean getUseLocalTransactionState() { + return getActiveMySQLConnection().getUseLocalTransactionState(); + } + + public boolean getUseNanosForElapsedTime() { + return getActiveMySQLConnection().getUseNanosForElapsedTime(); + } + + public boolean getUseOldAliasMetadataBehavior() { + return getActiveMySQLConnection().getUseOldAliasMetadataBehavior(); + } + + public boolean getUseOldUTF8Behavior() { + return getActiveMySQLConnection().getUseOldUTF8Behavior(); + } + + public boolean getUseOnlyServerErrorMessages() { + return getActiveMySQLConnection().getUseOnlyServerErrorMessages(); + } + + public boolean getUseReadAheadInput() { + return getActiveMySQLConnection().getUseReadAheadInput(); + } + + public boolean getUseSSL() { + return getActiveMySQLConnection().getUseSSL(); + } + + public boolean getUseSSPSCompatibleTimezoneShift() { + return getActiveMySQLConnection().getUseSSPSCompatibleTimezoneShift(); + } + + public boolean getUseServerPrepStmts() { + return getActiveMySQLConnection().getUseServerPrepStmts(); + } + + public boolean getUseServerPreparedStmts() { + return getActiveMySQLConnection().getUseServerPreparedStmts(); + } + + public boolean getUseSqlStateCodes() { + return getActiveMySQLConnection().getUseSqlStateCodes(); + } + + public boolean getUseStreamLengthsInPrepStmts() { + return getActiveMySQLConnection().getUseStreamLengthsInPrepStmts(); + } + + public boolean getUseTimezone() { + return getActiveMySQLConnection().getUseTimezone(); + } + + public boolean getUseUltraDevWorkAround() { + return getActiveMySQLConnection().getUseUltraDevWorkAround(); + } + + public boolean getUseUnbufferedInput() { + return getActiveMySQLConnection().getUseUnbufferedInput(); + } + + public boolean getUseUnicode() { + return getActiveMySQLConnection().getUseUnicode(); + } + + public boolean getUseUsageAdvisor() { + return getActiveMySQLConnection().getUseUsageAdvisor(); + } + + public String getUtf8OutsideBmpExcludedColumnNamePattern() { + return getActiveMySQLConnection().getUtf8OutsideBmpExcludedColumnNamePattern(); + } + + public String getUtf8OutsideBmpIncludedColumnNamePattern() { + return getActiveMySQLConnection().getUtf8OutsideBmpIncludedColumnNamePattern(); + } + + public boolean getVerifyServerCertificate() { + return getActiveMySQLConnection().getVerifyServerCertificate(); + } + + public boolean getYearIsDateType() { + return getActiveMySQLConnection().getYearIsDateType(); + } + + public String getZeroDateTimeBehavior() { + return getActiveMySQLConnection().getZeroDateTimeBehavior(); + } + + public void setAllowLoadLocalInfile(boolean property) { + getActiveMySQLConnection().setAllowLoadLocalInfile(property); + } + + public void setAllowMultiQueries(boolean property) { + getActiveMySQLConnection().setAllowMultiQueries(property); + } + + public void setAllowNanAndInf(boolean flag) { + getActiveMySQLConnection().setAllowNanAndInf(flag); + } + + public void setAllowUrlInLocalInfile(boolean flag) { + getActiveMySQLConnection().setAllowUrlInLocalInfile(flag); + } + + public void setAlwaysSendSetIsolation(boolean flag) { + getActiveMySQLConnection().setAlwaysSendSetIsolation(flag); + } + + public void setAutoClosePStmtStreams(boolean flag) { + getActiveMySQLConnection().setAutoClosePStmtStreams(flag); + } + + public void setAutoDeserialize(boolean flag) { + getActiveMySQLConnection().setAutoDeserialize(flag); + } + + public void setAutoGenerateTestcaseScript(boolean flag) { + getActiveMySQLConnection().setAutoGenerateTestcaseScript(flag); + } + + public void setAutoReconnect(boolean flag) { + getActiveMySQLConnection().setAutoReconnect(flag); + } + + public void setAutoReconnectForConnectionPools(boolean property) { + getActiveMySQLConnection().setAutoReconnectForConnectionPools(property); + } + + public void setAutoReconnectForPools(boolean flag) { + getActiveMySQLConnection().setAutoReconnectForPools(flag); + } + + public void setAutoSlowLog(boolean flag) { + getActiveMySQLConnection().setAutoSlowLog(flag); + } + + public void setBlobSendChunkSize(String value) throws SQLException { + getActiveMySQLConnection().setBlobSendChunkSize(value); + } + + public void setBlobsAreStrings(boolean flag) { + getActiveMySQLConnection().setBlobsAreStrings(flag); + } + + public void setCacheCallableStatements(boolean flag) { + getActiveMySQLConnection().setCacheCallableStatements(flag); + } + + public void setCacheCallableStmts(boolean flag) { + getActiveMySQLConnection().setCacheCallableStmts(flag); + } + + public void setCachePrepStmts(boolean flag) { + getActiveMySQLConnection().setCachePrepStmts(flag); + } + + public void setCachePreparedStatements(boolean flag) { + getActiveMySQLConnection().setCachePreparedStatements(flag); + } + + public void setCacheResultSetMetadata(boolean property) { + getActiveMySQLConnection().setCacheResultSetMetadata(property); + } + + public void setCacheServerConfiguration(boolean flag) { + getActiveMySQLConnection().setCacheServerConfiguration(flag); + } + + public void setCallableStatementCacheSize(int size) throws SQLException { + getActiveMySQLConnection().setCallableStatementCacheSize(size); + } + + public void setCallableStmtCacheSize(int cacheSize) throws SQLException { + getActiveMySQLConnection().setCallableStmtCacheSize(cacheSize); + } + + public void setCapitalizeDBMDTypes(boolean property) { + getActiveMySQLConnection().setCapitalizeDBMDTypes(property); + } + + public void setCapitalizeTypeNames(boolean flag) { + getActiveMySQLConnection().setCapitalizeTypeNames(flag); + } + + public void setCharacterEncoding(String encoding) { + getActiveMySQLConnection().setCharacterEncoding(encoding); + } + + public void setCharacterSetResults(String characterSet) { + getActiveMySQLConnection().setCharacterSetResults(characterSet); + } + + public void setClientCertificateKeyStorePassword(String value) { + getActiveMySQLConnection().setClientCertificateKeyStorePassword(value); + } + + public void setClientCertificateKeyStoreType(String value) { + getActiveMySQLConnection().setClientCertificateKeyStoreType(value); + } + + public void setClientCertificateKeyStoreUrl(String value) { + getActiveMySQLConnection().setClientCertificateKeyStoreUrl(value); + } + + public void setClientInfoProvider(String classname) { + getActiveMySQLConnection().setClientInfoProvider(classname); + } + + public void setClobCharacterEncoding(String encoding) { + getActiveMySQLConnection().setClobCharacterEncoding(encoding); + } + + public void setClobberStreamingResults(boolean flag) { + getActiveMySQLConnection().setClobberStreamingResults(flag); + } + + public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag) { + getActiveMySQLConnection().setCompensateOnDuplicateKeyUpdateCounts(flag); + } + + public void setConnectTimeout(int timeoutMs) throws SQLException { + getActiveMySQLConnection().setConnectTimeout(timeoutMs); + } + + public void setConnectionCollation(String collation) { + getActiveMySQLConnection().setConnectionCollation(collation); + } + + public void setConnectionLifecycleInterceptors(String interceptors) { + getActiveMySQLConnection().setConnectionLifecycleInterceptors(interceptors); + } + + public void setContinueBatchOnError(boolean property) { + getActiveMySQLConnection().setContinueBatchOnError(property); + } + + public void setCreateDatabaseIfNotExist(boolean flag) { + getActiveMySQLConnection().setCreateDatabaseIfNotExist(flag); + } + + public void setDefaultFetchSize(int n) throws SQLException { + getActiveMySQLConnection().setDefaultFetchSize(n); + } + + public void setDetectServerPreparedStmts(boolean property) { + getActiveMySQLConnection().setDetectServerPreparedStmts(property); + } + + public void setDontTrackOpenResources(boolean flag) { + getActiveMySQLConnection().setDontTrackOpenResources(flag); + } + + public void setDumpMetadataOnColumnNotFound(boolean flag) { + getActiveMySQLConnection().setDumpMetadataOnColumnNotFound(flag); + } + + public void setDumpQueriesOnException(boolean flag) { + getActiveMySQLConnection().setDumpQueriesOnException(flag); + } + + public void setDynamicCalendars(boolean flag) { + getActiveMySQLConnection().setDynamicCalendars(flag); + } + + public void setElideSetAutoCommits(boolean flag) { + getActiveMySQLConnection().setElideSetAutoCommits(flag); + } + + public void setEmptyStringsConvertToZero(boolean flag) { + getActiveMySQLConnection().setEmptyStringsConvertToZero(flag); + } + + public void setEmulateLocators(boolean property) { + getActiveMySQLConnection().setEmulateLocators(property); + } + + public void setEmulateUnsupportedPstmts(boolean flag) { + getActiveMySQLConnection().setEmulateUnsupportedPstmts(flag); + } + + public void setEnablePacketDebug(boolean flag) { + getActiveMySQLConnection().setEnablePacketDebug(flag); + } + + public void setEnableQueryTimeouts(boolean flag) { + getActiveMySQLConnection().setEnableQueryTimeouts(flag); + } + + public void setEncoding(String property) { + getActiveMySQLConnection().setEncoding(property); + } + + public void setExceptionInterceptors(String exceptionInterceptors) { + getActiveMySQLConnection().setExceptionInterceptors(exceptionInterceptors); + } + + public void setExplainSlowQueries(boolean flag) { + getActiveMySQLConnection().setExplainSlowQueries(flag); + } + + public void setFailOverReadOnly(boolean flag) { + getActiveMySQLConnection().setFailOverReadOnly(flag); + } + + public void setFunctionsNeverReturnBlobs(boolean flag) { + getActiveMySQLConnection().setFunctionsNeverReturnBlobs(flag); + } + + public void setGatherPerfMetrics(boolean flag) { + getActiveMySQLConnection().setGatherPerfMetrics(flag); + } + + public void setGatherPerformanceMetrics(boolean flag) { + getActiveMySQLConnection().setGatherPerformanceMetrics(flag); + } + + public void setGenerateSimpleParameterMetadata(boolean flag) { + getActiveMySQLConnection().setGenerateSimpleParameterMetadata(flag); + } + + public void setHoldResultsOpenOverStatementClose(boolean flag) { + getActiveMySQLConnection().setHoldResultsOpenOverStatementClose(flag); + } + + public void setIgnoreNonTxTables(boolean property) { + getActiveMySQLConnection().setIgnoreNonTxTables(property); + } + + public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) { + getActiveMySQLConnection().setIncludeInnodbStatusInDeadlockExceptions(flag); + } + + public void setInitialTimeout(int property) throws SQLException { + getActiveMySQLConnection().setInitialTimeout(property); + } + + public void setInteractiveClient(boolean property) { + getActiveMySQLConnection().setInteractiveClient(property); + } + + public void setIsInteractiveClient(boolean property) { + getActiveMySQLConnection().setIsInteractiveClient(property); + } + + public void setJdbcCompliantTruncation(boolean flag) { + getActiveMySQLConnection().setJdbcCompliantTruncation(flag); + } + + public void setJdbcCompliantTruncationForReads(boolean jdbcCompliantTruncationForReads) { + getActiveMySQLConnection().setJdbcCompliantTruncationForReads(jdbcCompliantTruncationForReads); + } + + public void setLargeRowSizeThreshold(String value) throws SQLException { + getActiveMySQLConnection().setLargeRowSizeThreshold(value); + } + + public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) throws SQLException { + getActiveMySQLConnection().setLoadBalanceBlacklistTimeout(loadBalanceBlacklistTimeout); + } + + public void setLoadBalancePingTimeout(int loadBalancePingTimeout) throws SQLException { + getActiveMySQLConnection().setLoadBalancePingTimeout(loadBalancePingTimeout); + } + + public void setLoadBalanceStrategy(String strategy) { + getActiveMySQLConnection().setLoadBalanceStrategy(strategy); + } + + public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer) { + getActiveMySQLConnection().setLoadBalanceValidateConnectionOnSwapServer(loadBalanceValidateConnectionOnSwapServer); + } + + public void setLocalSocketAddress(String address) { + getActiveMySQLConnection().setLocalSocketAddress(address); + } + + public void setLocatorFetchBufferSize(String value) throws SQLException { + getActiveMySQLConnection().setLocatorFetchBufferSize(value); + } + + public void setLogSlowQueries(boolean flag) { + getActiveMySQLConnection().setLogSlowQueries(flag); + } + + public void setLogXaCommands(boolean flag) { + getActiveMySQLConnection().setLogXaCommands(flag); + } + + public void setLogger(String property) { + getActiveMySQLConnection().setLogger(property); + } + + public void setLoggerClassName(String className) { + getActiveMySQLConnection().setLoggerClassName(className); + } + + public void setMaintainTimeStats(boolean flag) { + getActiveMySQLConnection().setMaintainTimeStats(flag); + } + + public void setMaxQuerySizeToLog(int sizeInBytes) throws SQLException { + getActiveMySQLConnection().setMaxQuerySizeToLog(sizeInBytes); + } + + public void setMaxReconnects(int property) throws SQLException { + getActiveMySQLConnection().setMaxReconnects(property); + } + + public void setMaxRows(int property) throws SQLException { + getActiveMySQLConnection().setMaxRows(property); + } + + public void setMetadataCacheSize(int value) throws SQLException { + getActiveMySQLConnection().setMetadataCacheSize(value); + } + + public void setNetTimeoutForStreamingResults(int value) throws SQLException { + getActiveMySQLConnection().setNetTimeoutForStreamingResults(value); + } + + public void setNoAccessToProcedureBodies(boolean flag) { + getActiveMySQLConnection().setNoAccessToProcedureBodies(flag); + } + + public void setNoDatetimeStringSync(boolean flag) { + getActiveMySQLConnection().setNoDatetimeStringSync(flag); + } + + public void setNoTimezoneConversionForTimeType(boolean flag) { + getActiveMySQLConnection().setNoTimezoneConversionForTimeType(flag); + } + + public void setNoTimezoneConversionForDateType(boolean flag) { + getActiveMySQLConnection().setNoTimezoneConversionForDateType(flag); + } + + public void setCacheDefaultTimezone(boolean flag) { + getActiveMySQLConnection().setCacheDefaultTimezone(flag); + } + + public void setNullCatalogMeansCurrent(boolean value) { + getActiveMySQLConnection().setNullCatalogMeansCurrent(value); + } + + public void setNullNamePatternMatchesAll(boolean value) { + getActiveMySQLConnection().setNullNamePatternMatchesAll(value); + } + + public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) { + getActiveMySQLConnection().setOverrideSupportsIntegrityEnhancementFacility(flag); + } + + public void setPacketDebugBufferSize(int size) throws SQLException { + getActiveMySQLConnection().setPacketDebugBufferSize(size); + } + + public void setPadCharsWithSpace(boolean flag) { + getActiveMySQLConnection().setPadCharsWithSpace(flag); + } + + public void setParanoid(boolean property) { + getActiveMySQLConnection().setParanoid(property); + } + + public void setPasswordCharacterEncoding(String characterSet) { + getActiveMySQLConnection().setPasswordCharacterEncoding(characterSet); + } + + public void setPedantic(boolean property) { + getActiveMySQLConnection().setPedantic(property); + } + + public void setPinGlobalTxToPhysicalConnection(boolean flag) { + getActiveMySQLConnection().setPinGlobalTxToPhysicalConnection(flag); + } + + public void setPopulateInsertRowWithDefaultValues(boolean flag) { + getActiveMySQLConnection().setPopulateInsertRowWithDefaultValues(flag); + } + + public void setPrepStmtCacheSize(int cacheSize) throws SQLException { + getActiveMySQLConnection().setPrepStmtCacheSize(cacheSize); + } + + public void setPrepStmtCacheSqlLimit(int sqlLimit) throws SQLException { + getActiveMySQLConnection().setPrepStmtCacheSqlLimit(sqlLimit); + } + + public void setPreparedStatementCacheSize(int cacheSize) throws SQLException { + getActiveMySQLConnection().setPreparedStatementCacheSize(cacheSize); + } + + public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) throws SQLException { + getActiveMySQLConnection().setPreparedStatementCacheSqlLimit(cacheSqlLimit); + } + + public void setProcessEscapeCodesForPrepStmts(boolean flag) { + getActiveMySQLConnection().setProcessEscapeCodesForPrepStmts(flag); + } + + public void setProfileSQL(boolean flag) { + getActiveMySQLConnection().setProfileSQL(flag); + } + + public void setProfileSql(boolean property) { + getActiveMySQLConnection().setProfileSql(property); + } + + public void setProfilerEventHandler(String handler) { + getActiveMySQLConnection().setProfilerEventHandler(handler); + } + + public void setPropertiesTransform(String value) { + getActiveMySQLConnection().setPropertiesTransform(value); + } + + public void setQueriesBeforeRetryMaster(int property) throws SQLException { + getActiveMySQLConnection().setQueriesBeforeRetryMaster(property); + } + + public void setQueryTimeoutKillsConnection(boolean queryTimeoutKillsConnection) { + getActiveMySQLConnection().setQueryTimeoutKillsConnection(queryTimeoutKillsConnection); + } + + public void setReconnectAtTxEnd(boolean property) { + getActiveMySQLConnection().setReconnectAtTxEnd(property); + } + + public void setRelaxAutoCommit(boolean property) { + getActiveMySQLConnection().setRelaxAutoCommit(property); + } + + public void setReportMetricsIntervalMillis(int millis) throws SQLException { + getActiveMySQLConnection().setReportMetricsIntervalMillis(millis); + } + + public void setRequireSSL(boolean property) { + getActiveMySQLConnection().setRequireSSL(property); + } + + public void setResourceId(String resourceId) { + getActiveMySQLConnection().setResourceId(resourceId); + } + + public void setResultSetSizeThreshold(int threshold) throws SQLException { + getActiveMySQLConnection().setResultSetSizeThreshold(threshold); + } + + public void setRetainStatementAfterResultSetClose(boolean flag) { + getActiveMySQLConnection().setRetainStatementAfterResultSetClose(flag); + } + + public void setRetriesAllDown(int retriesAllDown) throws SQLException { + getActiveMySQLConnection().setRetriesAllDown(retriesAllDown); + } + + public void setRewriteBatchedStatements(boolean flag) { + getActiveMySQLConnection().setRewriteBatchedStatements(flag); + } + + public void setRollbackOnPooledClose(boolean flag) { + getActiveMySQLConnection().setRollbackOnPooledClose(flag); + } + + public void setRoundRobinLoadBalance(boolean flag) { + getActiveMySQLConnection().setRoundRobinLoadBalance(flag); + } + + public void setRunningCTS13(boolean flag) { + getActiveMySQLConnection().setRunningCTS13(flag); + } + + public void setSecondsBeforeRetryMaster(int property) throws SQLException { + getActiveMySQLConnection().setSecondsBeforeRetryMaster(property); + } + + public void setSelfDestructOnPingMaxOperations(int maxOperations) throws SQLException { + getActiveMySQLConnection().setSelfDestructOnPingMaxOperations(maxOperations); + } + + public void setSelfDestructOnPingSecondsLifetime(int seconds) throws SQLException { + getActiveMySQLConnection().setSelfDestructOnPingSecondsLifetime(seconds); + } + + public void setServerTimezone(String property) { + getActiveMySQLConnection().setServerTimezone(property); + } + + public void setSessionVariables(String variables) { + getActiveMySQLConnection().setSessionVariables(variables); + } + + public void setSlowQueryThresholdMillis(int millis) throws SQLException { + getActiveMySQLConnection().setSlowQueryThresholdMillis(millis); + } + + public void setSlowQueryThresholdNanos(long nanos) throws SQLException { + getActiveMySQLConnection().setSlowQueryThresholdNanos(nanos); + } + + public void setSocketFactory(String name) { + getActiveMySQLConnection().setSocketFactory(name); + } + + public void setSocketFactoryClassName(String property) { + getActiveMySQLConnection().setSocketFactoryClassName(property); + } + + public void setSocketTimeout(int property) throws SQLException { + getActiveMySQLConnection().setSocketTimeout(property); + } + + public void setStatementInterceptors(String value) { + getActiveMySQLConnection().setStatementInterceptors(value); + } + + public void setStrictFloatingPoint(boolean property) { + getActiveMySQLConnection().setStrictFloatingPoint(property); + } + + public void setStrictUpdates(boolean property) { + getActiveMySQLConnection().setStrictUpdates(property); + } + + public void setTcpKeepAlive(boolean flag) { + getActiveMySQLConnection().setTcpKeepAlive(flag); + } + + public void setTcpNoDelay(boolean flag) { + getActiveMySQLConnection().setTcpNoDelay(flag); + } + + public void setTcpRcvBuf(int bufSize) throws SQLException { + getActiveMySQLConnection().setTcpRcvBuf(bufSize); + } + + public void setTcpSndBuf(int bufSize) throws SQLException { + getActiveMySQLConnection().setTcpSndBuf(bufSize); + } + + public void setTcpTrafficClass(int classFlags) throws SQLException { + getActiveMySQLConnection().setTcpTrafficClass(classFlags); + } + + public void setTinyInt1isBit(boolean flag) { + getActiveMySQLConnection().setTinyInt1isBit(flag); + } + + public void setTraceProtocol(boolean flag) { + getActiveMySQLConnection().setTraceProtocol(flag); + } + + public void setTransformedBitIsBoolean(boolean flag) { + getActiveMySQLConnection().setTransformedBitIsBoolean(flag); + } + + public void setTreatUtilDateAsTimestamp(boolean flag) { + getActiveMySQLConnection().setTreatUtilDateAsTimestamp(flag); + } + + public void setTrustCertificateKeyStorePassword(String value) { + getActiveMySQLConnection().setTrustCertificateKeyStorePassword(value); + } + + public void setTrustCertificateKeyStoreType(String value) { + getActiveMySQLConnection().setTrustCertificateKeyStoreType(value); + } + + public void setTrustCertificateKeyStoreUrl(String value) { + getActiveMySQLConnection().setTrustCertificateKeyStoreUrl(value); + } + + public void setUltraDevHack(boolean flag) { + getActiveMySQLConnection().setUltraDevHack(flag); + } + + public void setUseAffectedRows(boolean flag) { + getActiveMySQLConnection().setUseAffectedRows(flag); + } + + public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) { + getActiveMySQLConnection().setUseBlobToStoreUTF8OutsideBMP(flag); + } + + public void setUseColumnNamesInFindColumn(boolean flag) { + getActiveMySQLConnection().setUseColumnNamesInFindColumn(flag); + } + + public void setUseCompression(boolean property) { + getActiveMySQLConnection().setUseCompression(property); + } + + public void setUseConfigs(String configs) { + getActiveMySQLConnection().setUseConfigs(configs); + } + + public void setUseCursorFetch(boolean flag) { + getActiveMySQLConnection().setUseCursorFetch(flag); + } + + public void setUseDirectRowUnpack(boolean flag) { + getActiveMySQLConnection().setUseDirectRowUnpack(flag); + } + + public void setUseDynamicCharsetInfo(boolean flag) { + getActiveMySQLConnection().setUseDynamicCharsetInfo(flag); + } + + public void setUseFastDateParsing(boolean flag) { + getActiveMySQLConnection().setUseFastDateParsing(flag); + } + + public void setUseFastIntParsing(boolean flag) { + getActiveMySQLConnection().setUseFastIntParsing(flag); + } + + public void setUseGmtMillisForDatetimes(boolean flag) { + getActiveMySQLConnection().setUseGmtMillisForDatetimes(flag); + } + + public void setUseHostsInPrivileges(boolean property) { + getActiveMySQLConnection().setUseHostsInPrivileges(property); + } + + public void setUseInformationSchema(boolean flag) { + getActiveMySQLConnection().setUseInformationSchema(flag); + } + + public void setUseJDBCCompliantTimezoneShift(boolean flag) { + getActiveMySQLConnection().setUseJDBCCompliantTimezoneShift(flag); + } + + public void setUseJvmCharsetConverters(boolean flag) { + getActiveMySQLConnection().setUseJvmCharsetConverters(flag); + } + + public void setUseLegacyDatetimeCode(boolean flag) { + getActiveMySQLConnection().setUseLegacyDatetimeCode(flag); + } + + public void setSendFractionalSeconds(boolean flag) { + getActiveMySQLConnection().setSendFractionalSeconds(flag); + } + + public void setUseLocalSessionState(boolean flag) { + getActiveMySQLConnection().setUseLocalSessionState(flag); + } + + public void setUseLocalTransactionState(boolean flag) { + getActiveMySQLConnection().setUseLocalTransactionState(flag); + } + + public void setUseNanosForElapsedTime(boolean flag) { + getActiveMySQLConnection().setUseNanosForElapsedTime(flag); + } + + public void setUseOldAliasMetadataBehavior(boolean flag) { + getActiveMySQLConnection().setUseOldAliasMetadataBehavior(flag); + } + + public void setUseOldUTF8Behavior(boolean flag) { + getActiveMySQLConnection().setUseOldUTF8Behavior(flag); + } + + public void setUseOnlyServerErrorMessages(boolean flag) { + getActiveMySQLConnection().setUseOnlyServerErrorMessages(flag); + } + + public void setUseReadAheadInput(boolean flag) { + getActiveMySQLConnection().setUseReadAheadInput(flag); + } + + public void setUseSSL(boolean property) { + getActiveMySQLConnection().setUseSSL(property); + } + + public void setUseSSPSCompatibleTimezoneShift(boolean flag) { + getActiveMySQLConnection().setUseSSPSCompatibleTimezoneShift(flag); + } + + public void setUseServerPrepStmts(boolean flag) { + getActiveMySQLConnection().setUseServerPrepStmts(flag); + } + + public void setUseServerPreparedStmts(boolean flag) { + getActiveMySQLConnection().setUseServerPreparedStmts(flag); + } + + public void setUseSqlStateCodes(boolean flag) { + getActiveMySQLConnection().setUseSqlStateCodes(flag); + } + + public void setUseStreamLengthsInPrepStmts(boolean property) { + getActiveMySQLConnection().setUseStreamLengthsInPrepStmts(property); + } + + public void setUseTimezone(boolean property) { + getActiveMySQLConnection().setUseTimezone(property); + } + + public void setUseUltraDevWorkAround(boolean property) { + getActiveMySQLConnection().setUseUltraDevWorkAround(property); + } + + public void setUseUnbufferedInput(boolean flag) { + getActiveMySQLConnection().setUseUnbufferedInput(flag); + } + + public void setUseUnicode(boolean flag) { + getActiveMySQLConnection().setUseUnicode(flag); + } + + public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) { + getActiveMySQLConnection().setUseUsageAdvisor(useUsageAdvisorFlag); + } + + public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) { + getActiveMySQLConnection().setUtf8OutsideBmpExcludedColumnNamePattern(regexPattern); + } + + public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) { + getActiveMySQLConnection().setUtf8OutsideBmpIncludedColumnNamePattern(regexPattern); + } + + public void setVerifyServerCertificate(boolean flag) { + getActiveMySQLConnection().setVerifyServerCertificate(flag); + } + + public void setYearIsDateType(boolean flag) { + getActiveMySQLConnection().setYearIsDateType(flag); + } + + public void setZeroDateTimeBehavior(String behavior) { + getActiveMySQLConnection().setZeroDateTimeBehavior(behavior); + } + + public boolean useUnbufferedInput() { + return getActiveMySQLConnection().useUnbufferedInput(); + } + + public StringBuilder generateConnectionCommentBlock(StringBuilder buf) { + return getActiveMySQLConnection().generateConnectionCommentBlock(buf); + } + + public int getActiveStatementCount() { + return getActiveMySQLConnection().getActiveStatementCount(); + } + + public boolean getAutoCommit() throws SQLException { + return getActiveMySQLConnection().getAutoCommit(); + } + + public int getAutoIncrementIncrement() { + return getActiveMySQLConnection().getAutoIncrementIncrement(); + } + + public CachedResultSetMetaData getCachedMetaData(String sql) { + return getActiveMySQLConnection().getCachedMetaData(sql); + } + + public Calendar getCalendarInstanceForSessionOrNew() { + return getActiveMySQLConnection().getCalendarInstanceForSessionOrNew(); + } + + public Timer getCancelTimer() { + return getActiveMySQLConnection().getCancelTimer(); + } + + public String getCatalog() throws SQLException { + return getActiveMySQLConnection().getCatalog(); + } + + public String getCharacterSetMetadata() { + return getActiveMySQLConnection().getCharacterSetMetadata(); + } + + public SingleByteCharsetConverter getCharsetConverter(String javaEncodingName) throws SQLException { + return getActiveMySQLConnection().getCharsetConverter(javaEncodingName); + } + + /** + * @deprecated replaced by getEncodingForIndex(int charsetIndex) + */ + @Deprecated + public String getCharsetNameForIndex(int charsetIndex) throws SQLException { + return getEncodingForIndex(charsetIndex); + } + + public String getEncodingForIndex(int collationIndex) throws SQLException { + return getActiveMySQLConnection().getEncodingForIndex(collationIndex); + } + + public TimeZone getDefaultTimeZone() { + return getActiveMySQLConnection().getDefaultTimeZone(); + } + + public String getErrorMessageEncoding() { + return getActiveMySQLConnection().getErrorMessageEncoding(); + } + + public ExceptionInterceptor getExceptionInterceptor() { + return getActiveMySQLConnection().getExceptionInterceptor(); + } + + public int getHoldability() throws SQLException { + return getActiveMySQLConnection().getHoldability(); + } + + public String getHost() { + return getActiveMySQLConnection().getHost(); + } + + public String getHostPortPair() { + return getActiveMySQLConnection().getHostPortPair(); + } + + public long getId() { + return getActiveMySQLConnection().getId(); + } + + public long getIdleFor() { + return getActiveMySQLConnection().getIdleFor(); + } + + public MysqlIO getIO() throws SQLException { + return getActiveMySQLConnection().getIO(); + } + + /** + * @deprecated replaced by getMultiHostSafeProxy() + */ + @Deprecated + public MySQLConnection getLoadBalanceSafeProxy() { + return getMultiHostSafeProxy(); + } + + public MySQLConnection getMultiHostSafeProxy() { + return getThisAsProxy().getProxy(); + } + + public Log getLog() throws SQLException { + return getActiveMySQLConnection().getLog(); + } + + public int getMaxBytesPerChar(String javaCharsetName) throws SQLException { + return getActiveMySQLConnection().getMaxBytesPerChar(javaCharsetName); + } + + public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) throws SQLException { + return getActiveMySQLConnection().getMaxBytesPerChar(charsetIndex, javaCharsetName); + } + + public DatabaseMetaData getMetaData() throws SQLException { + return getActiveMySQLConnection().getMetaData(); + } + + public Statement getMetadataSafeStatement() throws SQLException { + return getActiveMySQLConnection().getMetadataSafeStatement(); + } + + public int getNetBufferLength() { + return getActiveMySQLConnection().getNetBufferLength(); + } + + public Properties getProperties() { + return getActiveMySQLConnection().getProperties(); + } + + public boolean getRequiresEscapingEncoder() { + return getActiveMySQLConnection().getRequiresEscapingEncoder(); + } + + /** + * @deprecated replaced by getServerCharset() + */ + @Deprecated + public String getServerCharacterEncoding() { + return getServerCharset(); + } + + public String getServerCharset() { + return getActiveMySQLConnection().getServerCharset(); + } + + public int getServerMajorVersion() { + return getActiveMySQLConnection().getServerMajorVersion(); + } + + public int getServerMinorVersion() { + return getActiveMySQLConnection().getServerMinorVersion(); + } + + public int getServerSubMinorVersion() { + return getActiveMySQLConnection().getServerSubMinorVersion(); + } + + public TimeZone getServerTimezoneTZ() { + return getActiveMySQLConnection().getServerTimezoneTZ(); + } + + public String getServerVariable(String variableName) { + return getActiveMySQLConnection().getServerVariable(variableName); + } + + public String getServerVersion() { + return getActiveMySQLConnection().getServerVersion(); + } + + public Calendar getSessionLockedCalendar() { + return getActiveMySQLConnection().getSessionLockedCalendar(); + } + + public String getStatementComment() { + return getActiveMySQLConnection().getStatementComment(); + } + + public List getStatementInterceptorsInstances() { + return getActiveMySQLConnection().getStatementInterceptorsInstances(); + } + + public int getTransactionIsolation() throws SQLException { + return getActiveMySQLConnection().getTransactionIsolation(); + } + + public Map> getTypeMap() throws SQLException { + return getActiveMySQLConnection().getTypeMap(); + } + + public String getURL() { + return getActiveMySQLConnection().getURL(); + } + + public String getUser() { + return getActiveMySQLConnection().getUser(); + } + + public Calendar getUtcCalendar() { + return getActiveMySQLConnection().getUtcCalendar(); + } + + public SQLWarning getWarnings() throws SQLException { + return getActiveMySQLConnection().getWarnings(); + } + + public boolean hasSameProperties(Connection c) { + return getActiveMySQLConnection().hasSameProperties(c); + } + + @Deprecated + public boolean hasTriedMaster() { + return getActiveMySQLConnection().hasTriedMaster(); + } + + public void incrementNumberOfPreparedExecutes() { + getActiveMySQLConnection().incrementNumberOfPreparedExecutes(); + } + + public void incrementNumberOfPrepares() { + getActiveMySQLConnection().incrementNumberOfPrepares(); + } + + public void incrementNumberOfResultSetsCreated() { + getActiveMySQLConnection().incrementNumberOfResultSetsCreated(); + } + + public void initializeExtension(Extension ex) throws SQLException { + getActiveMySQLConnection().initializeExtension(ex); + } + + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + getActiveMySQLConnection().initializeResultsMetadataFromCache(sql, cachedMetaData, resultSet); + } + + public void initializeSafeStatementInterceptors() throws SQLException { + getActiveMySQLConnection().initializeSafeStatementInterceptors(); + } + + public boolean isAbonormallyLongQuery(long millisOrNanos) { + return getActiveMySQLConnection().isAbonormallyLongQuery(millisOrNanos); + } + + public boolean isClientTzUTC() { + return getActiveMySQLConnection().isClientTzUTC(); + } + + public boolean isCursorFetchEnabled() throws SQLException { + return getActiveMySQLConnection().isCursorFetchEnabled(); + } + + public boolean isInGlobalTx() { + return getActiveMySQLConnection().isInGlobalTx(); + } + + public boolean isMasterConnection() { + return getThisAsProxy().isMasterConnection(); + } + + public boolean isNoBackslashEscapesSet() { + return getActiveMySQLConnection().isNoBackslashEscapesSet(); + } + + public boolean isReadInfoMsgEnabled() { + return getActiveMySQLConnection().isReadInfoMsgEnabled(); + } + + public boolean isReadOnly() throws SQLException { + return getActiveMySQLConnection().isReadOnly(); + } + + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + return getActiveMySQLConnection().isReadOnly(useSessionStatus); + } + + public boolean isRunningOnJDK13() { + return getActiveMySQLConnection().isRunningOnJDK13(); + } + + public boolean isSameResource(Connection otherConnection) { + return getActiveMySQLConnection().isSameResource(otherConnection); + } + + public boolean isServerTzUTC() { + return getActiveMySQLConnection().isServerTzUTC(); + } + + public boolean lowerCaseTableNames() { + return getActiveMySQLConnection().lowerCaseTableNames(); + } + + public String nativeSQL(String sql) throws SQLException { + return getActiveMySQLConnection().nativeSQL(sql); + } + + public boolean parserKnowsUnicode() { + return getActiveMySQLConnection().parserKnowsUnicode(); + } + + public void ping() throws SQLException { + getActiveMySQLConnection().ping(); + } + + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + getActiveMySQLConnection().pingInternal(checkForClosedConnection, timeoutMillis); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql, resultSetType, resultSetConcurrency); + } + + public CallableStatement prepareCall(String sql) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement prepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement prepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement prepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql); + } + + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + getActiveMySQLConnection().realClose(calledExplicitly, issueRollback, skipLocalTeardown, reason); + } + + public void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + getActiveMySQLConnection().recachePreparedStatement(pstmt); + } + + public void decachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException { + getActiveMySQLConnection().decachePreparedStatement(pstmt); + } + + public void registerQueryExecutionTime(long queryTimeMs) { + getActiveMySQLConnection().registerQueryExecutionTime(queryTimeMs); + } + + public void registerStatement(com.mysql.jdbc.Statement stmt) { + getActiveMySQLConnection().registerStatement(stmt); + } + + public void releaseSavepoint(Savepoint arg0) throws SQLException { + getActiveMySQLConnection().releaseSavepoint(arg0); + } + + public void reportNumberOfTablesAccessed(int numTablesAccessed) { + getActiveMySQLConnection().reportNumberOfTablesAccessed(numTablesAccessed); + } + + public void reportQueryTime(long millisOrNanos) { + getActiveMySQLConnection().reportQueryTime(millisOrNanos); + } + + public void resetServerState() throws SQLException { + getActiveMySQLConnection().resetServerState(); + } + + public void rollback() throws SQLException { + getActiveMySQLConnection().rollback(); + } + + public void rollback(Savepoint savepoint) throws SQLException { + getActiveMySQLConnection().rollback(savepoint); + } + + public PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement serverPrepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql); + } + + public boolean serverSupportsConvertFn() throws SQLException { + return getActiveMySQLConnection().serverSupportsConvertFn(); + } + + public void setAutoCommit(boolean autoCommitFlag) throws SQLException { + getActiveMySQLConnection().setAutoCommit(autoCommitFlag); + } + + public void setCatalog(String catalog) throws SQLException { + getActiveMySQLConnection().setCatalog(catalog); + } + + public void setFailedOver(boolean flag) { + getActiveMySQLConnection().setFailedOver(flag); + } + + public void setHoldability(int arg0) throws SQLException { + getActiveMySQLConnection().setHoldability(arg0); + } + + public void setInGlobalTx(boolean flag) { + getActiveMySQLConnection().setInGlobalTx(flag); + } + + @Deprecated + public void setPreferSlaveDuringFailover(boolean flag) { + getActiveMySQLConnection().setPreferSlaveDuringFailover(flag); + } + + public void setProxy(MySQLConnection proxy) { + getThisAsProxy().setProxy(proxy); + } + + public void setReadInfoMsgEnabled(boolean flag) { + getActiveMySQLConnection().setReadInfoMsgEnabled(flag); + } + + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + getActiveMySQLConnection().setReadOnly(readOnlyFlag); + } + + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + getActiveMySQLConnection().setReadOnlyInternal(readOnlyFlag); + } + + public Savepoint setSavepoint() throws SQLException { + return getActiveMySQLConnection().setSavepoint(); + } + + public Savepoint setSavepoint(String name) throws SQLException { + return getActiveMySQLConnection().setSavepoint(name); + } + + public void setStatementComment(String comment) { + getActiveMySQLConnection().setStatementComment(comment); + } + + public void setTransactionIsolation(int level) throws SQLException { + getActiveMySQLConnection().setTransactionIsolation(level); + } + + public void shutdownServer() throws SQLException { + getActiveMySQLConnection().shutdownServer(); + } + + public boolean storesLowerCaseTableName() { + return getActiveMySQLConnection().storesLowerCaseTableName(); + } + + public boolean supportsIsolationLevel() { + return getActiveMySQLConnection().supportsIsolationLevel(); + } + + public boolean supportsQuotedIdentifiers() { + return getActiveMySQLConnection().supportsQuotedIdentifiers(); + } + + public boolean supportsTransactions() { + return getActiveMySQLConnection().supportsTransactions(); + } + + public void throwConnectionClosedException() throws SQLException { + getActiveMySQLConnection().throwConnectionClosedException(); + } + + public void transactionBegun() throws SQLException { + getActiveMySQLConnection().transactionBegun(); + } + + public void transactionCompleted() throws SQLException { + getActiveMySQLConnection().transactionCompleted(); + } + + public void unregisterStatement(com.mysql.jdbc.Statement stmt) { + getActiveMySQLConnection().unregisterStatement(stmt); + } + + public void unSafeStatementInterceptors() throws SQLException { + getActiveMySQLConnection().unSafeStatementInterceptors(); + } + + public boolean useAnsiQuotedIdentifiers() { + return getActiveMySQLConnection().useAnsiQuotedIdentifiers(); + } + + public boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException { + return getActiveMySQLConnection().versionMeetsMinimum(major, minor, subminor); + } + + public boolean isClosed() throws SQLException { + return getThisAsProxy().isClosed; + } + + public boolean getHoldResultsOpenOverStatementClose() { + return getActiveMySQLConnection().getHoldResultsOpenOverStatementClose(); + } + + public String getLoadBalanceConnectionGroup() { + return getActiveMySQLConnection().getLoadBalanceConnectionGroup(); + } + + public boolean getLoadBalanceEnableJMX() { + return getActiveMySQLConnection().getLoadBalanceEnableJMX(); + } + + public String getLoadBalanceExceptionChecker() { + return getActiveMySQLConnection().getLoadBalanceExceptionChecker(); + } + + public String getLoadBalanceSQLExceptionSubclassFailover() { + return getActiveMySQLConnection().getLoadBalanceSQLExceptionSubclassFailover(); + } + + public String getLoadBalanceSQLStateFailover() { + return getActiveMySQLConnection().getLoadBalanceSQLStateFailover(); + } + + public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup) { + getActiveMySQLConnection().setLoadBalanceConnectionGroup(loadBalanceConnectionGroup); + + } + + public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX) { + getActiveMySQLConnection().setLoadBalanceEnableJMX(loadBalanceEnableJMX); + + } + + public void setLoadBalanceExceptionChecker(String loadBalanceExceptionChecker) { + getActiveMySQLConnection().setLoadBalanceExceptionChecker(loadBalanceExceptionChecker); + + } + + public void setLoadBalanceSQLExceptionSubclassFailover(String loadBalanceSQLExceptionSubclassFailover) { + getActiveMySQLConnection().setLoadBalanceSQLExceptionSubclassFailover(loadBalanceSQLExceptionSubclassFailover); + + } + + public void setLoadBalanceSQLStateFailover(String loadBalanceSQLStateFailover) { + getActiveMySQLConnection().setLoadBalanceSQLStateFailover(loadBalanceSQLStateFailover); + } + + public void setLoadBalanceHostRemovalGracePeriod(int loadBalanceHostRemovalGracePeriod) throws SQLException { + getActiveMySQLConnection().setLoadBalanceHostRemovalGracePeriod(loadBalanceHostRemovalGracePeriod); + } + + public int getLoadBalanceHostRemovalGracePeriod() { + return getActiveMySQLConnection().getLoadBalanceHostRemovalGracePeriod(); + } + + public boolean isProxySet() { + return this.getActiveMySQLConnection().isProxySet(); + } + + public String getLoadBalanceAutoCommitStatementRegex() { + return getActiveMySQLConnection().getLoadBalanceAutoCommitStatementRegex(); + } + + public int getLoadBalanceAutoCommitStatementThreshold() { + return getActiveMySQLConnection().getLoadBalanceAutoCommitStatementThreshold(); + } + + public void setLoadBalanceAutoCommitStatementRegex(String loadBalanceAutoCommitStatementRegex) { + getActiveMySQLConnection().setLoadBalanceAutoCommitStatementRegex(loadBalanceAutoCommitStatementRegex); + } + + public void setLoadBalanceAutoCommitStatementThreshold(int loadBalanceAutoCommitStatementThreshold) throws SQLException { + getActiveMySQLConnection().setLoadBalanceAutoCommitStatementThreshold(loadBalanceAutoCommitStatementThreshold); + } + + public boolean getIncludeThreadDumpInDeadlockExceptions() { + return getActiveMySQLConnection().getIncludeThreadDumpInDeadlockExceptions(); + } + + public void setIncludeThreadDumpInDeadlockExceptions(boolean flag) { + getActiveMySQLConnection().setIncludeThreadDumpInDeadlockExceptions(flag); + } + + public void setTypeMap(Map> map) throws SQLException { + getActiveMySQLConnection().setTypeMap(map); + } + + public boolean getIncludeThreadNamesAsStatementComment() { + return getActiveMySQLConnection().getIncludeThreadNamesAsStatementComment(); + } + + public void setIncludeThreadNamesAsStatementComment(boolean flag) { + getActiveMySQLConnection().setIncludeThreadNamesAsStatementComment(flag); + } + + public boolean isServerLocal() throws SQLException { + return getActiveMySQLConnection().isServerLocal(); + } + + public void setAuthenticationPlugins(String authenticationPlugins) { + getActiveMySQLConnection().setAuthenticationPlugins(authenticationPlugins); + } + + public String getAuthenticationPlugins() { + return getActiveMySQLConnection().getAuthenticationPlugins(); + } + + public void setDisabledAuthenticationPlugins(String disabledAuthenticationPlugins) { + getActiveMySQLConnection().setDisabledAuthenticationPlugins(disabledAuthenticationPlugins); + } + + public String getDisabledAuthenticationPlugins() { + return getActiveMySQLConnection().getDisabledAuthenticationPlugins(); + } + + public void setDefaultAuthenticationPlugin(String defaultAuthenticationPlugin) { + getActiveMySQLConnection().setDefaultAuthenticationPlugin(defaultAuthenticationPlugin); + } + + public String getDefaultAuthenticationPlugin() { + return getActiveMySQLConnection().getDefaultAuthenticationPlugin(); + } + + public void setParseInfoCacheFactory(String factoryClassname) { + getActiveMySQLConnection().setParseInfoCacheFactory(factoryClassname); + } + + public String getParseInfoCacheFactory() { + return getActiveMySQLConnection().getParseInfoCacheFactory(); + } + + public void setSchema(String schema) throws SQLException { + getActiveMySQLConnection().setSchema(schema); + } + + public String getSchema() throws SQLException { + return getActiveMySQLConnection().getSchema(); + } + + public void abort(Executor executor) throws SQLException { + getActiveMySQLConnection().abort(executor); + } + + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + getActiveMySQLConnection().setNetworkTimeout(executor, milliseconds); + } + + public int getNetworkTimeout() throws SQLException { + return getActiveMySQLConnection().getNetworkTimeout(); + } + + public void setServerConfigCacheFactory(String factoryClassname) { + getActiveMySQLConnection().setServerConfigCacheFactory(factoryClassname); + } + + public String getServerConfigCacheFactory() { + return getActiveMySQLConnection().getServerConfigCacheFactory(); + } + + public void setDisconnectOnExpiredPasswords(boolean disconnectOnExpiredPasswords) { + getActiveMySQLConnection().setDisconnectOnExpiredPasswords(disconnectOnExpiredPasswords); + } + + public boolean getDisconnectOnExpiredPasswords() { + return getActiveMySQLConnection().getDisconnectOnExpiredPasswords(); + } + + public void setGetProceduresReturnsFunctions(boolean getProcedureReturnsFunctions) { + getActiveMySQLConnection().setGetProceduresReturnsFunctions(getProcedureReturnsFunctions); + } + + public boolean getGetProceduresReturnsFunctions() { + return getActiveMySQLConnection().getGetProceduresReturnsFunctions(); + } + + public Object getConnectionMutex() { + return getActiveMySQLConnection().getConnectionMutex(); + } + + public String getConnectionAttributes() throws SQLException { + return getActiveMySQLConnection().getConnectionAttributes(); + } + + public boolean getAllowMasterDownConnections() { + return getActiveMySQLConnection().getAllowMasterDownConnections(); + } + + public void setAllowMasterDownConnections(boolean connectIfMasterDown) { + getActiveMySQLConnection().setAllowMasterDownConnections(connectIfMasterDown); + } + + public boolean getAllowSlaveDownConnections() { + return getActiveMySQLConnection().getAllowSlaveDownConnections(); + } + + public void setAllowSlaveDownConnections(boolean connectIfSlaveDown) { + getActiveMySQLConnection().setAllowSlaveDownConnections(connectIfSlaveDown); + } + + public boolean getReadFromMasterWhenNoSlaves() { + return getActiveMySQLConnection().getReadFromMasterWhenNoSlaves(); + } + + public void setReadFromMasterWhenNoSlaves(boolean useMasterIfSlavesDown) { + getActiveMySQLConnection().setReadFromMasterWhenNoSlaves(useMasterIfSlavesDown); + } + + public boolean getReplicationEnableJMX() { + return getActiveMySQLConnection().getReplicationEnableJMX(); + } + + public void setReplicationEnableJMX(boolean replicationEnableJMX) { + getActiveMySQLConnection().setReplicationEnableJMX(replicationEnableJMX); + } + + public void setDetectCustomCollations(boolean detectCustomCollations) { + getActiveMySQLConnection().setDetectCustomCollations(detectCustomCollations); + } + + public boolean getDetectCustomCollations() { + return getActiveMySQLConnection().getDetectCustomCollations(); + } + + public int getSessionMaxRows() { + return getActiveMySQLConnection().getSessionMaxRows(); + } + + public void setSessionMaxRows(int max) throws SQLException { + getActiveMySQLConnection().setSessionMaxRows(max); + } + + public ProfilerEventHandler getProfilerEventHandlerInstance() { + return getActiveMySQLConnection().getProfilerEventHandlerInstance(); + } + + public void setProfilerEventHandlerInstance(ProfilerEventHandler h) { + getActiveMySQLConnection().setProfilerEventHandlerInstance(h); + } + + public String getServerRSAPublicKeyFile() { + return getActiveMySQLConnection().getServerRSAPublicKeyFile(); + } + + public void setServerRSAPublicKeyFile(String serverRSAPublicKeyFile) throws SQLException { + getActiveMySQLConnection().setServerRSAPublicKeyFile(serverRSAPublicKeyFile); + } + + public boolean getAllowPublicKeyRetrieval() { + return getActiveMySQLConnection().getAllowPublicKeyRetrieval(); + } + + public void setAllowPublicKeyRetrieval(boolean allowPublicKeyRetrieval) throws SQLException { + getActiveMySQLConnection().setAllowPublicKeyRetrieval(allowPublicKeyRetrieval); + } + + public void setDontCheckOnDuplicateKeyUpdateInSQL(boolean dontCheckOnDuplicateKeyUpdateInSQL) { + getActiveMySQLConnection().setDontCheckOnDuplicateKeyUpdateInSQL(dontCheckOnDuplicateKeyUpdateInSQL); + } + + public boolean getDontCheckOnDuplicateKeyUpdateInSQL() { + return getActiveMySQLConnection().getDontCheckOnDuplicateKeyUpdateInSQL(); + } + + public void setSocksProxyHost(String socksProxyHost) { + getActiveMySQLConnection().setSocksProxyHost(socksProxyHost); + } + + public String getSocksProxyHost() { + return getActiveMySQLConnection().getSocksProxyHost(); + } + + public void setSocksProxyPort(int socksProxyPort) throws SQLException { + getActiveMySQLConnection().setSocksProxyPort(socksProxyPort); + } + + public int getSocksProxyPort() { + return getActiveMySQLConnection().getSocksProxyPort(); + } + + public boolean getReadOnlyPropagatesToServer() { + return getActiveMySQLConnection().getReadOnlyPropagatesToServer(); + } + + public void setReadOnlyPropagatesToServer(boolean flag) { + getActiveMySQLConnection().setReadOnlyPropagatesToServer(flag); + } + + public String getEnabledSSLCipherSuites() { + return getActiveMySQLConnection().getEnabledSSLCipherSuites(); + } + + public void setEnabledSSLCipherSuites(String cipherSuites) { + getActiveMySQLConnection().setEnabledSSLCipherSuites(cipherSuites); + } + + public boolean getEnableEscapeProcessing() { + return getActiveMySQLConnection().getEnableEscapeProcessing(); + } + + public void setEnableEscapeProcessing(boolean flag) { + getActiveMySQLConnection().setEnableEscapeProcessing(flag); + } + + public boolean isUseSSLExplicit() { + return getActiveMySQLConnection().isUseSSLExplicit(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MySQLConnection.java new file mode 100644 index 0000000..ae5dbcd --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MySQLConnection.java @@ -0,0 +1,213 @@ +/* + Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Calendar; +import java.util.List; +import java.util.Properties; +import java.util.TimeZone; +import java.util.Timer; + +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +public interface MySQLConnection extends Connection, ConnectionProperties { + + public boolean isProxySet(); + + void createNewIO(boolean isForReconnect) throws SQLException; + + void dumpTestcaseQuery(String query); + + Connection duplicate() throws SQLException; + + ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata) throws SQLException; + + ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows, Buffer packet, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Field[] cachedMetadata, boolean isBatch) throws SQLException; + + String extractSqlFromPacket(String possibleSqlQuery, Buffer queryPacket, int endOfQueryPacketPosition) throws SQLException; + + StringBuilder generateConnectionCommentBlock(StringBuilder buf); + + int getActiveStatementCount(); + + int getAutoIncrementIncrement(); + + CachedResultSetMetaData getCachedMetaData(String sql); + + Calendar getCalendarInstanceForSessionOrNew(); + + Timer getCancelTimer(); + + String getCharacterSetMetadata(); + + SingleByteCharsetConverter getCharsetConverter(String javaEncodingName) throws SQLException; + + /** + * @deprecated replaced by getEncodingForIndex(int collationIndex) + */ + @Deprecated + String getCharsetNameForIndex(int charsetIndex) throws SQLException; + + String getEncodingForIndex(int collationIndex) throws SQLException; + + TimeZone getDefaultTimeZone(); + + String getErrorMessageEncoding(); + + ExceptionInterceptor getExceptionInterceptor(); + + String getHost(); + + String getHostPortPair(); + + long getId(); + + long getIdleFor(); + + MysqlIO getIO() throws SQLException; + + Log getLog() throws SQLException; + + int getMaxBytesPerChar(String javaCharsetName) throws SQLException; + + int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) throws SQLException; + + java.sql.Statement getMetadataSafeStatement() throws SQLException; + + int getNetBufferLength(); + + Properties getProperties(); + + boolean getRequiresEscapingEncoder(); + + String getServerCharset(); + + int getServerMajorVersion(); + + int getServerMinorVersion(); + + int getServerSubMinorVersion(); + + TimeZone getServerTimezoneTZ(); + + String getServerVariable(String variableName); + + String getServerVersion(); + + Calendar getSessionLockedCalendar(); + + String getStatementComment(); + + List getStatementInterceptorsInstances(); + + String getURL(); + + String getUser(); + + Calendar getUtcCalendar(); + + void incrementNumberOfPreparedExecutes(); + + void incrementNumberOfPrepares(); + + void incrementNumberOfResultSetsCreated(); + + void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException; + + void initializeSafeStatementInterceptors() throws SQLException; + + boolean isAbonormallyLongQuery(long millisOrNanos); + + boolean isClientTzUTC(); + + boolean isCursorFetchEnabled() throws SQLException; + + boolean isReadInfoMsgEnabled(); + + public boolean isReadOnly() throws SQLException; + + public boolean isReadOnly(boolean useSessionStatus) throws SQLException; + + boolean isRunningOnJDK13(); + + boolean isServerTzUTC(); + + boolean lowerCaseTableNames(); + + void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException; + + void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException; + + void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException; + + void decachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException; + + void registerQueryExecutionTime(long queryTimeMs); + + void registerStatement(Statement stmt); + + void reportNumberOfTablesAccessed(int numTablesAccessed); + + boolean serverSupportsConvertFn() throws SQLException; + + void setProxy(MySQLConnection proxy); + + void setReadInfoMsgEnabled(boolean flag); + + void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException; + + void shutdownServer() throws SQLException; + + boolean storesLowerCaseTableName(); + + void throwConnectionClosedException() throws SQLException; + + void transactionBegun() throws SQLException; + + void transactionCompleted() throws SQLException; + + void unregisterStatement(Statement stmt); + + void unSafeStatementInterceptors() throws SQLException; + + boolean useAnsiQuotedIdentifiers(); + + String getConnectionAttributes() throws SQLException; + + /** + * @deprecated replaced by getMultiHostSafeProxy() + */ + @Deprecated + MySQLConnection getLoadBalanceSafeProxy(); + + MySQLConnection getMultiHostSafeProxy(); + + ProfilerEventHandler getProfilerEventHandlerInstance(); + + void setProfilerEventHandlerInstance(ProfilerEventHandler h); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDataTruncation.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDataTruncation.java new file mode 100644 index 0000000..fe8772a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDataTruncation.java @@ -0,0 +1,76 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.DataTruncation; + +/** + * MySQL wrapper for DataTruncation until the server can support sending all needed information. + */ +public class MysqlDataTruncation extends DataTruncation { + + static final long serialVersionUID = 3263928195256986226L; + + private String message; + + private int vendorErrorCode; + + /** + * Creates a new MysqlDataTruncation exception/warning. + * + * @param message + * the message from the server + * @param index + * of column or parameter + * @param parameter + * was a parameter? + * @param read + * was truncated on read? + * @param dataSize + * size requested + * @param transferSize + * size actually used + */ + public MysqlDataTruncation(String message, int index, boolean parameter, boolean read, int dataSize, int transferSize, int vendorErrorCode) { + super(index, parameter, read, dataSize, transferSize); + + this.message = message; + this.vendorErrorCode = vendorErrorCode; + } + + @Override + public int getErrorCode() { + return this.vendorErrorCode; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Throwable#getMessage() + */ + @Override + public String getMessage() { + return super.getMessage() + ": " + this.message; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDefs.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDefs.java new file mode 100644 index 0000000..9ae7897 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlDefs.java @@ -0,0 +1,556 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Types; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * MysqlDefs contains many values that are needed for communication with the MySQL server. + */ +public final class MysqlDefs { + static final int COM_BINLOG_DUMP = 18; + + static final int COM_CHANGE_USER = 17; + + static final int COM_CLOSE_STATEMENT = 25; + + static final int COM_CONNECT_OUT = 20; + + static final int COM_END = 29; + + static final int COM_EXECUTE = 23; + + static final int COM_FETCH = 28; + + static final int COM_LONG_DATA = 24; + + static final int COM_PREPARE = 22; + + static final int COM_REGISTER_SLAVE = 21; + + static final int COM_RESET_STMT = 26; + + static final int COM_SET_OPTION = 27; + + static final int COM_TABLE_DUMP = 19; + + static final int CONNECT = 11; + + static final int CREATE_DB = 5; + + static final int DEBUG = 13; + + static final int DELAYED_INSERT = 16; + + static final int DROP_DB = 6; + + static final int FIELD_LIST = 4; + + static final int FIELD_TYPE_BIT = 16; + + public static final int FIELD_TYPE_BLOB = 252; + + static final int FIELD_TYPE_DATE = 10; + + static final int FIELD_TYPE_DATETIME = 12; + + // Data Types + static final int FIELD_TYPE_DECIMAL = 0; + + static final int FIELD_TYPE_DOUBLE = 5; + + static final int FIELD_TYPE_ENUM = 247; + + static final int FIELD_TYPE_FLOAT = 4; + + static final int FIELD_TYPE_GEOMETRY = 255; + + static final int FIELD_TYPE_INT24 = 9; + + static final int FIELD_TYPE_LONG = 3; + + static final int FIELD_TYPE_LONG_BLOB = 251; + + static final int FIELD_TYPE_LONGLONG = 8; + + static final int FIELD_TYPE_MEDIUM_BLOB = 250; + + static final int FIELD_TYPE_NEW_DECIMAL = 246; + + static final int FIELD_TYPE_NEWDATE = 14; + + static final int FIELD_TYPE_NULL = 6; + + static final int FIELD_TYPE_SET = 248; + + static final int FIELD_TYPE_SHORT = 2; + + static final int FIELD_TYPE_STRING = 254; + + static final int FIELD_TYPE_TIME = 11; + + static final int FIELD_TYPE_TIMESTAMP = 7; + + static final int FIELD_TYPE_TINY = 1; + + // Older data types + static final int FIELD_TYPE_TINY_BLOB = 249; + + static final int FIELD_TYPE_VAR_STRING = 253; + + static final int FIELD_TYPE_VARCHAR = 15; + + // Newer data types + static final int FIELD_TYPE_YEAR = 13; + + static final int FIELD_TYPE_JSON = 245; + + static final int INIT_DB = 2; + + static final long LENGTH_BLOB = 65535; + + static final long LENGTH_LONGBLOB = 4294967295L; + + static final long LENGTH_MEDIUMBLOB = 16777215; + + static final long LENGTH_TINYBLOB = 255; + + // Limitations + static final int MAX_ROWS = 50000000; // From the MySQL FAQ + + /** + * Used to indicate that the server sent no field-level character set information, so the driver should use the connection-level character encoding instead. + */ + public static final int NO_CHARSET_INFO = -1; + + static final byte OPEN_CURSOR_FLAG = 1; + + static final int PING = 14; + + static final int PROCESS_INFO = 10; + + static final int PROCESS_KILL = 12; + + static final int QUERY = 3; + + static final int QUIT = 1; + + static final int RELOAD = 7; + + static final int SHUTDOWN = 8; + + // + // Constants defined from mysql + // + // DB Operations + static final int SLEEP = 0; + + static final int STATISTICS = 9; + + static final int TIME = 15; + + /** + * Maps the given MySQL type to the correct JDBC type. + */ + static int mysqlToJavaType(int mysqlType) { + int jdbcType; + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + case MysqlDefs.FIELD_TYPE_DECIMAL: + jdbcType = Types.DECIMAL; + + break; + + case MysqlDefs.FIELD_TYPE_TINY: + jdbcType = Types.TINYINT; + + break; + + case MysqlDefs.FIELD_TYPE_SHORT: + jdbcType = Types.SMALLINT; + + break; + + case MysqlDefs.FIELD_TYPE_LONG: + jdbcType = Types.INTEGER; + + break; + + case MysqlDefs.FIELD_TYPE_FLOAT: + jdbcType = Types.REAL; + + break; + + case MysqlDefs.FIELD_TYPE_DOUBLE: + jdbcType = Types.DOUBLE; + + break; + + case MysqlDefs.FIELD_TYPE_NULL: + jdbcType = Types.NULL; + + break; + + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + jdbcType = Types.TIMESTAMP; + + break; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + jdbcType = Types.BIGINT; + + break; + + case MysqlDefs.FIELD_TYPE_INT24: + jdbcType = Types.INTEGER; + + break; + + case MysqlDefs.FIELD_TYPE_DATE: + jdbcType = Types.DATE; + + break; + + case MysqlDefs.FIELD_TYPE_TIME: + jdbcType = Types.TIME; + + break; + + case MysqlDefs.FIELD_TYPE_DATETIME: + jdbcType = Types.TIMESTAMP; + + break; + + case MysqlDefs.FIELD_TYPE_YEAR: + jdbcType = Types.DATE; + + break; + + case MysqlDefs.FIELD_TYPE_NEWDATE: + jdbcType = Types.DATE; + + break; + + case MysqlDefs.FIELD_TYPE_ENUM: + jdbcType = Types.CHAR; + + break; + + case MysqlDefs.FIELD_TYPE_SET: + jdbcType = Types.CHAR; + + break; + + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + jdbcType = Types.VARBINARY; + + break; + + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + jdbcType = Types.LONGVARBINARY; + + break; + + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + jdbcType = Types.LONGVARBINARY; + + break; + + case MysqlDefs.FIELD_TYPE_BLOB: + jdbcType = Types.LONGVARBINARY; + + break; + + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + jdbcType = Types.VARCHAR; + + break; + + case MysqlDefs.FIELD_TYPE_JSON: + case MysqlDefs.FIELD_TYPE_STRING: + jdbcType = Types.CHAR; + + break; + case MysqlDefs.FIELD_TYPE_GEOMETRY: + jdbcType = Types.BINARY; + + break; + case MysqlDefs.FIELD_TYPE_BIT: + jdbcType = Types.BIT; + + break; + default: + jdbcType = Types.VARCHAR; + } + + return jdbcType; + } + + /** + * Maps the given MySQL type to the correct JDBC type. + */ + static int mysqlToJavaType(String mysqlType) { + if (mysqlType.equalsIgnoreCase("BIT")) { + return mysqlToJavaType(FIELD_TYPE_BIT); + } else if (mysqlType.equalsIgnoreCase("TINYINT")) { + return mysqlToJavaType(FIELD_TYPE_TINY); + } else if (mysqlType.equalsIgnoreCase("SMALLINT")) { + return mysqlToJavaType(FIELD_TYPE_SHORT); + } else if (mysqlType.equalsIgnoreCase("MEDIUMINT")) { + return mysqlToJavaType(FIELD_TYPE_INT24); + } else if (mysqlType.equalsIgnoreCase("INT") || mysqlType.equalsIgnoreCase("INTEGER")) { + return mysqlToJavaType(FIELD_TYPE_LONG); + } else if (mysqlType.equalsIgnoreCase("BIGINT")) { + return mysqlToJavaType(FIELD_TYPE_LONGLONG); + } else if (mysqlType.equalsIgnoreCase("INT24")) { + return mysqlToJavaType(FIELD_TYPE_INT24); + } else if (mysqlType.equalsIgnoreCase("REAL")) { + return mysqlToJavaType(FIELD_TYPE_DOUBLE); + } else if (mysqlType.equalsIgnoreCase("FLOAT")) { + return mysqlToJavaType(FIELD_TYPE_FLOAT); + } else if (mysqlType.equalsIgnoreCase("DECIMAL")) { + return mysqlToJavaType(FIELD_TYPE_DECIMAL); + } else if (mysqlType.equalsIgnoreCase("NUMERIC")) { + return mysqlToJavaType(FIELD_TYPE_DECIMAL); + } else if (mysqlType.equalsIgnoreCase("DOUBLE")) { + return mysqlToJavaType(FIELD_TYPE_DOUBLE); + } else if (mysqlType.equalsIgnoreCase("CHAR")) { + return mysqlToJavaType(FIELD_TYPE_STRING); + } else if (mysqlType.equalsIgnoreCase("VARCHAR")) { + return mysqlToJavaType(FIELD_TYPE_VAR_STRING); + } else if (mysqlType.equalsIgnoreCase("DATE")) { + return mysqlToJavaType(FIELD_TYPE_DATE); + } else if (mysqlType.equalsIgnoreCase("TIME")) { + return mysqlToJavaType(FIELD_TYPE_TIME); + } else if (mysqlType.equalsIgnoreCase("YEAR")) { + return mysqlToJavaType(FIELD_TYPE_YEAR); + } else if (mysqlType.equalsIgnoreCase("TIMESTAMP")) { + return mysqlToJavaType(FIELD_TYPE_TIMESTAMP); + } else if (mysqlType.equalsIgnoreCase("DATETIME")) { + return mysqlToJavaType(FIELD_TYPE_DATETIME); + } else if (mysqlType.equalsIgnoreCase("TINYBLOB")) { + return java.sql.Types.BINARY; + } else if (mysqlType.equalsIgnoreCase("BLOB")) { + return java.sql.Types.LONGVARBINARY; + } else if (mysqlType.equalsIgnoreCase("MEDIUMBLOB")) { + return java.sql.Types.LONGVARBINARY; + } else if (mysqlType.equalsIgnoreCase("LONGBLOB")) { + return java.sql.Types.LONGVARBINARY; + } else if (mysqlType.equalsIgnoreCase("TINYTEXT")) { + return java.sql.Types.VARCHAR; + } else if (mysqlType.equalsIgnoreCase("TEXT")) { + return java.sql.Types.LONGVARCHAR; + } else if (mysqlType.equalsIgnoreCase("MEDIUMTEXT")) { + return java.sql.Types.LONGVARCHAR; + } else if (mysqlType.equalsIgnoreCase("LONGTEXT")) { + return java.sql.Types.LONGVARCHAR; + } else if (mysqlType.equalsIgnoreCase("ENUM")) { + return mysqlToJavaType(FIELD_TYPE_ENUM); + } else if (mysqlType.equalsIgnoreCase("SET")) { + return mysqlToJavaType(FIELD_TYPE_SET); + } else if (mysqlType.equalsIgnoreCase("GEOMETRY")) { + return mysqlToJavaType(FIELD_TYPE_GEOMETRY); + } else if (mysqlType.equalsIgnoreCase("BINARY")) { + return Types.BINARY; // no concrete type on the wire + } else if (mysqlType.equalsIgnoreCase("VARBINARY")) { + return Types.VARBINARY; // no concrete type on the wire + } else if (mysqlType.equalsIgnoreCase("BIT")) { + return mysqlToJavaType(FIELD_TYPE_BIT); + } else if (mysqlType.equalsIgnoreCase("JSON")) { + return mysqlToJavaType(FIELD_TYPE_JSON); + } + + // Punt + return java.sql.Types.OTHER; + } + + /** + * @param mysqlType + */ + public static String typeToName(int mysqlType) { + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_DECIMAL: + return "FIELD_TYPE_DECIMAL"; + + case MysqlDefs.FIELD_TYPE_TINY: + return "FIELD_TYPE_TINY"; + + case MysqlDefs.FIELD_TYPE_SHORT: + return "FIELD_TYPE_SHORT"; + + case MysqlDefs.FIELD_TYPE_LONG: + return "FIELD_TYPE_LONG"; + + case MysqlDefs.FIELD_TYPE_FLOAT: + return "FIELD_TYPE_FLOAT"; + + case MysqlDefs.FIELD_TYPE_DOUBLE: + return "FIELD_TYPE_DOUBLE"; + + case MysqlDefs.FIELD_TYPE_NULL: + return "FIELD_TYPE_NULL"; + + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + return "FIELD_TYPE_TIMESTAMP"; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + return "FIELD_TYPE_LONGLONG"; + + case MysqlDefs.FIELD_TYPE_INT24: + return "FIELD_TYPE_INT24"; + + case MysqlDefs.FIELD_TYPE_BIT: + return "FIELD_TYPE_BIT"; + + case MysqlDefs.FIELD_TYPE_DATE: + return "FIELD_TYPE_DATE"; + + case MysqlDefs.FIELD_TYPE_TIME: + return "FIELD_TYPE_TIME"; + + case MysqlDefs.FIELD_TYPE_DATETIME: + return "FIELD_TYPE_DATETIME"; + + case MysqlDefs.FIELD_TYPE_YEAR: + return "FIELD_TYPE_YEAR"; + + case MysqlDefs.FIELD_TYPE_NEWDATE: + return "FIELD_TYPE_NEWDATE"; + + case MysqlDefs.FIELD_TYPE_ENUM: + return "FIELD_TYPE_ENUM"; + + case MysqlDefs.FIELD_TYPE_SET: + return "FIELD_TYPE_SET"; + + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + return "FIELD_TYPE_TINY_BLOB"; + + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + return "FIELD_TYPE_MEDIUM_BLOB"; + + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + return "FIELD_TYPE_LONG_BLOB"; + + case MysqlDefs.FIELD_TYPE_BLOB: + return "FIELD_TYPE_BLOB"; + + case MysqlDefs.FIELD_TYPE_VAR_STRING: + return "FIELD_TYPE_VAR_STRING"; + + case MysqlDefs.FIELD_TYPE_STRING: + return "FIELD_TYPE_STRING"; + + case MysqlDefs.FIELD_TYPE_VARCHAR: + return "FIELD_TYPE_VARCHAR"; + + case MysqlDefs.FIELD_TYPE_GEOMETRY: + return "FIELD_TYPE_GEOMETRY"; + + case MysqlDefs.FIELD_TYPE_JSON: + return "FIELD_TYPE_JSON"; + + default: + return " Unknown MySQL Type # " + mysqlType; + } + } + + private static Map mysqlToJdbcTypesMap = new HashMap(); + + static { + mysqlToJdbcTypesMap.put("BIT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_BIT))); + + mysqlToJdbcTypesMap.put("TINYINT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_TINY))); + mysqlToJdbcTypesMap.put("SMALLINT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_SHORT))); + mysqlToJdbcTypesMap.put("MEDIUMINT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_INT24))); + mysqlToJdbcTypesMap.put("INT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_LONG))); + mysqlToJdbcTypesMap.put("INTEGER", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_LONG))); + mysqlToJdbcTypesMap.put("BIGINT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_LONGLONG))); + mysqlToJdbcTypesMap.put("INT24", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_INT24))); + mysqlToJdbcTypesMap.put("REAL", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DOUBLE))); + mysqlToJdbcTypesMap.put("FLOAT", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_FLOAT))); + mysqlToJdbcTypesMap.put("DECIMAL", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DECIMAL))); + mysqlToJdbcTypesMap.put("NUMERIC", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DECIMAL))); + mysqlToJdbcTypesMap.put("DOUBLE", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DOUBLE))); + mysqlToJdbcTypesMap.put("CHAR", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_STRING))); + mysqlToJdbcTypesMap.put("VARCHAR", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_VAR_STRING))); + mysqlToJdbcTypesMap.put("DATE", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DATE))); + mysqlToJdbcTypesMap.put("TIME", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_TIME))); + mysqlToJdbcTypesMap.put("YEAR", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_YEAR))); + mysqlToJdbcTypesMap.put("TIMESTAMP", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_TIMESTAMP))); + mysqlToJdbcTypesMap.put("DATETIME", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_DATETIME))); + mysqlToJdbcTypesMap.put("TINYBLOB", Integer.valueOf(java.sql.Types.BINARY)); + mysqlToJdbcTypesMap.put("BLOB", Integer.valueOf(java.sql.Types.LONGVARBINARY)); + mysqlToJdbcTypesMap.put("MEDIUMBLOB", Integer.valueOf(java.sql.Types.LONGVARBINARY)); + mysqlToJdbcTypesMap.put("LONGBLOB", Integer.valueOf(java.sql.Types.LONGVARBINARY)); + mysqlToJdbcTypesMap.put("TINYTEXT", Integer.valueOf(java.sql.Types.VARCHAR)); + mysqlToJdbcTypesMap.put("TEXT", Integer.valueOf(java.sql.Types.LONGVARCHAR)); + mysqlToJdbcTypesMap.put("MEDIUMTEXT", Integer.valueOf(java.sql.Types.LONGVARCHAR)); + mysqlToJdbcTypesMap.put("LONGTEXT", Integer.valueOf(java.sql.Types.LONGVARCHAR)); + mysqlToJdbcTypesMap.put("ENUM", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_ENUM))); + mysqlToJdbcTypesMap.put("SET", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_SET))); + mysqlToJdbcTypesMap.put("GEOMETRY", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_GEOMETRY))); + mysqlToJdbcTypesMap.put("JSON", Integer.valueOf(mysqlToJavaType(FIELD_TYPE_JSON))); + } + + static final void appendJdbcTypeMappingQuery(StringBuilder buf, String mysqlTypeColumnName) { + + buf.append("CASE "); + Map typesMap = new HashMap(); + typesMap.putAll(mysqlToJdbcTypesMap); + typesMap.put("BINARY", Integer.valueOf(Types.BINARY)); + typesMap.put("VARBINARY", Integer.valueOf(Types.VARBINARY)); + + Iterator mysqlTypes = typesMap.keySet().iterator(); + + while (mysqlTypes.hasNext()) { + String mysqlTypeName = mysqlTypes.next(); + buf.append(" WHEN UPPER("); + buf.append(mysqlTypeColumnName); + buf.append(")='"); + buf.append(mysqlTypeName); + buf.append("' THEN "); + buf.append(typesMap.get(mysqlTypeName)); + + if (mysqlTypeName.equalsIgnoreCase("DOUBLE") || mysqlTypeName.equalsIgnoreCase("FLOAT") || mysqlTypeName.equalsIgnoreCase("DECIMAL") + || mysqlTypeName.equalsIgnoreCase("NUMERIC")) { + buf.append(" WHEN "); + buf.append(mysqlTypeColumnName); + buf.append("='"); + buf.append(mysqlTypeName); + buf.append(" UNSIGNED' THEN "); + buf.append(typesMap.get(mysqlTypeName)); + } + } + + buf.append(" ELSE "); + buf.append(Types.OTHER); + buf.append(" END "); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlErrorNumbers.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlErrorNumbers.java new file mode 100644 index 0000000..eefcef7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlErrorNumbers.java @@ -0,0 +1,953 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Constants representing MySQL error numbers returned by the server in error messages. + */ +public final class MysqlErrorNumbers { + + public final static int ER_ERROR_MESSAGES = 298; + public final static int ER_HASHCHK = 1000; //SQLSTATE: HY000 Message: hashchk + public final static int ER_NISAMCHK = 1001; //SQLSTATE: HY000 Message: isamchk + public final static int ER_NO = 1002; //SQLSTATE: HY000 Message: NO; Used in the construction of other messages. + public final static int ER_YES = 1003; //SQLSTATE: HY000 Message: YES + public final static int ER_CANT_CREATE_FILE = 1004; //SQLSTATE: HY000 Message: Can't create file '%s' (errno: %d - %s) + public final static int ER_CANT_CREATE_TABLE = 1005; //SQLSTATE: HY000 Message: Can't create table '%s' (errno: %d) + public final static int ER_CANT_CREATE_DB = 1006; //SQLSTATE: HY000 Message: Can't create database '%s' (errno: %d) + public final static int ER_DB_CREATE_EXISTS = 1007; //SQLSTATE: HY000 Message: Can't create database '%s'; database exists... + public final static int ER_DB_DROP_EXISTS = 1008; //SQLSTATE: HY000 Message: Can't drop database '%s'; database doesn't exist + public final static int ER_DB_DROP_DELETE = 1009; //SQLSTATE: HY000 Message: Error dropping database (can't delete '%s', errno: %d) + public final static int ER_DB_DROP_RMDIR = 1010; //SQLSTATE: HY000 Message: Error dropping database (can't rmdir '%s', errno: %d) + public final static int ER_CANT_DELETE_FILE = 1011; //SQLSTATE: HY000 Message: Error on delete of '%s' (errno: %d - %s) + public final static int ER_CANT_FIND_SYSTEM_REC = 1012; //SQLSTATE: HY000 Message: Can't read record in system table + public final static int ER_CANT_GET_STAT = 1013; //SQLSTATE: HY000 Message: Can't get status of '%s' (errno: %d - %s) + public final static int ER_CANT_GET_WD = 1014; //SQLSTATE: HY000 Message: Can't get working directory (errno: %d - %s) + public final static int ER_CANT_LOCK = 1015; //SQLSTATE: HY000 Message: Can't lock file (errno: %d - %s) + public final static int ER_CANT_OPEN_FILE = 1016; //SQLSTATE: HY000 Message: Can't open file: '%s' (errno: %d - %s) + public final static int ER_FILE_NOT_FOUND = 1017; //SQLSTATE: HY000 Message: Can't find file: '%s' (errno: %d - %s) + public final static int ER_CANT_READ_DIR = 1018; //SQLSTATE: HY000 Message: Can't read dir of '%s' (errno: %d - %s) + public final static int ER_CANT_SET_WD = 1019; //SQLSTATE: HY000 Message: Can't change dir to '%s' (errno: %d - %s) + public final static int ER_CHECKREAD = 1020; //SQLSTATE: HY000 Message: Record has changed since last read in table '%s' + public final static int ER_DISK_FULL = 1021; //SQLSTATE: HY000 Message: Disk full (%s); waiting for someone to free some space... (errno: %d - %s) + public final static int ER_DUP_KEY = 1022; //SQLSTATE: 23000 Message: Can't write; duplicate key in table '%s' + public final static int ER_ERROR_ON_CLOSE = 1023; //SQLSTATE: HY000 Message: Error on close of '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_READ = 1024; //SQLSTATE: HY000 Message: Error reading file '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_RENAME = 1025; //SQLSTATE: HY000 Message: Error on rename of '%s' to '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_WRITE = 1026; //SQLSTATE: HY000 Message: Error writing file '%s' (errno: %d - %s) + public final static int ER_FILE_USED = 1027; //SQLSTATE: HY000 Message: '%s' is locked against change + public final static int ER_FILSORT_ABORT = 1028; //SQLSTATE: HY000 Message: Sort aborted + public final static int ER_FORM_NOT_FOUND = 1029; //SQLSTATE: HY000 Message: View '%s' doesn't exist for '%s' + public final static int ER_GET_ERRNO = 1030; //SQLSTATE: HY000 Message: Got error %d from storage engine... + public final static int ER_ILLEGAL_HA = 1031; //SQLSTATE: HY000 Message: Table storage engine for '%s' doesn't have this option + public final static int ER_KEY_NOT_FOUND = 1032; //SQLSTATE: HY000 Message: Can't find record in '%s' + public final static int ER_NOT_FORM_FILE = 1033; //SQLSTATE: HY000 Message: Incorrect information in file: '%s' + public final static int ER_NOT_KEYFILE = 1034; //SQLSTATE: HY000 Message: Incorrect key file for table '%s'; try to repair it + public final static int ER_OLD_KEYFILE = 1035; //SQLSTATE: HY000 Message: Old key file for table '%s'; repair it! + public final static int ER_OPEN_AS_READONLY = 1036; //SQLSTATE: HY000 Message: Table '%s' is read only + public final static int ER_OUTOFMEMORY = 1037; //SQLSTATE: HY001 Message: Out of memory; restart server and try again (needed %d bytes) + public final static int ER_OUT_OF_SORTMEMORY = 1038; //SQLSTATE: HY001 Message: Out of sort memory, consider increasing server sort buffer size + public final static int ER_UNEXPECTED_EOF = 1039; //SQLSTATE: HY000 Message: Unexpected EOF found when reading file '%s' (errno: %d - %s) + public final static int ER_CON_COUNT_ERROR = 1040; //SQLSTATE: 08004 Message: Too many connections + public final static int ER_OUT_OF_RESOURCES = 1041; //SQLSTATE: HY000 Message: Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space + public final static int ER_BAD_HOST_ERROR = 1042; //SQLSTATE: 08S01 Message: Can't get hostname for your address + public final static int ER_HANDSHAKE_ERROR = 1043; //SQLSTATE: 08S01 Message: Bad handshake + public final static int ER_DBACCESS_DENIED_ERROR = 1044; //SQLSTATE: 42000 Message: Access denied for user '%s'@'%s' to database '%s' + public final static int ER_ACCESS_DENIED_ERROR = 1045; //SQLSTATE: 28000 Message: Access denied for user '%s'@'%s' (using password: %s) + public final static int ER_NO_DB_ERROR = 1046; //SQLSTATE: 3D000 Message: No database selected + public final static int ER_UNKNOWN_COM_ERROR = 1047; //SQLSTATE: 08S01 Message: Unknown command + public final static int ER_BAD_NULL_ERROR = 1048; //SQLSTATE: 23000 Message: Column '%s' cannot be null + public final static int ER_BAD_DB_ERROR = 1049; //SQLSTATE: 42000 Message: Unknown database '%s' + public final static int ER_TABLE_EXISTS_ERROR = 1050; //SQLSTATE: 42S01 Message: Table '%s' already exists + public final static int ER_BAD_TABLE_ERROR = 1051; //SQLSTATE: 42S02 Message: Unknown table '%s' + public final static int ER_NON_UNIQ_ERROR = 1052; //SQLSTATE: 23000 Message: Column '%s' in %s is ambiguous + public final static int ER_SERVER_SHUTDOWN = 1053; //SQLSTATE: 08S01 Message: Server shutdown in progress + public final static int ER_BAD_FIELD_ERROR = 1054; //SQLSTATE: 42S22 Message: Unknown column '%s' in '%s' + public final static int ER_WRONG_FIELD_WITH_GROUP = 1055; //SQLSTATE: 42000 Message: '%s' isn't in GROUP BY + public final static int ER_WRONG_GROUP_FIELD = 1056; //SQLSTATE: 42000 Message: Can't group on '%s' + public final static int ER_WRONG_SUM_SELECT = 1057; //SQLSTATE: 42000 Message: Statement has sum functions and columns in same statement + public final static int ER_WRONG_VALUE_COUNT = 1058; //SQLSTATE: 21S01 Message: Column count doesn't match value count + public final static int ER_TOO_LONG_IDENT = 1059; //SQLSTATE: 42000 Message: Identifier name '%s' is too long + public final static int ER_DUP_FIELDNAME = 1060; //SQLSTATE: 42S21 Message: Duplicate column name '%s' + public final static int ER_DUP_KEYNAME = 1061; //SQLSTATE: 42000 Message: Duplicate key name '%s' + public final static int ER_DUP_ENTRY = 1062; //SQLSTATE: 23000 Message: Duplicate entry '%s' for key %d + public final static int ER_WRONG_FIELD_SPEC = 1063; //SQLSTATE: 42000 Message: Incorrect column specifier for column '%s' + public final static int ER_PARSE_ERROR = 1064; //SQLSTATE: 42000 Message: %s near '%s' at line %d + public final static int ER_EMPTY_QUERY = 1065; //SQLSTATE: 42000 Message: Query was empty + public final static int ER_NONUNIQ_TABLE = 1066; //SQLSTATE: 42000 Message: Not unique table/alias: '%s' + public final static int ER_INVALID_DEFAULT = 1067; //SQLSTATE: 42000 Message: Invalid default value for '%s' + public final static int ER_MULTIPLE_PRI_KEY = 1068; //SQLSTATE: 42000 Message: Multiple primary key defined + public final static int ER_TOO_MANY_KEYS = 1069; //SQLSTATE: 42000 Message: Too many keys specified; max %d keys allowed + public final static int ER_TOO_MANY_KEY_PARTS = 1070; //SQLSTATE: 42000 Message: Too many key parts specified; max %d parts allowed + public final static int ER_TOO_LONG_KEY = 1071; //SQLSTATE: 42000 Message: Specified key was too long; max key length is %d bytes + public final static int ER_KEY_COLUMN_DOES_NOT_EXITS = 1072; //SQLSTATE: 42000 Message: Key column '%s' doesn't exist in table + public final static int ER_BLOB_USED_AS_KEY = 1073; //SQLSTATE: 42000 Message: BLOB column '%s' can't be used in key specification with the used table type + public final static int ER_TOO_BIG_FIELDLENGTH = 1074; //SQLSTATE: 42000 Message: Column length too big for column '%s' (max = %lu); use BLOB or TEXT instead + public final static int ER_WRONG_AUTO_KEY = 1075; //SQLSTATE: 42000 Message: Incorrect table definition; there can be only one auto column and it must be defined as a key + public final static int ER_READY = 1076; //SQLSTATE: HY000 Message: %s: ready for connections. Version: '%s' socket: '%s' port: %d + public final static int ER_NORMAL_SHUTDOWN = 1077; //SQLSTATE: HY000 Message: %s: Normal shutdown + public final static int ER_GOT_SIGNAL = 1078; //SQLSTATE: HY000 Message: %s: Got signal %d. Aborting! + public final static int ER_SHUTDOWN_COMPLETE = 1079; //SQLSTATE: HY000 Message: %s: Shutdown complete + public final static int ER_FORCING_CLOSE = 1080; //SQLSTATE: 08S01 Message: %s: Forcing close of thread %ld user: '%s' + public final static int ER_IPSOCK_ERROR = 1081; //SQLSTATE: 08S01 Message: Can't create IP socket + public final static int ER_NO_SUCH_INDEX = 1082; //SQLSTATE: 42S12 Message: Table '%s' has no index like the one used in CREATE INDEX; recreate the table + public final static int ER_WRONG_FIELD_TERMINATORS = 1083; //SQLSTATE: 42000 Message: Field separator argument is not what is expected; check the manual + public final static int ER_BLOBS_AND_NO_TERMINATED = 1084; //SQLSTATE: 42000 Message: You can't use fixed rowlength with BLOBs; please use 'fields terminated by' + public final static int ER_TEXTFILE_NOT_READABLE = 1085; //SQLSTATE: HY000 Message: The file '%s' must be in the database directory or be readable by all + public final static int ER_FILE_EXISTS_ERROR = 1086; //SQLSTATE: HY000 Message: File '%s' already exists + public final static int ER_LOAD_INFO = 1087; //SQLSTATE: HY000 Message: Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld + public final static int ER_ALTER_INFO = 1088; //SQLSTATE: HY000 Message: Records: %ld Duplicates: %ld + public final static int ER_WRONG_SUB_KEY = 1089; //SQLSTATE: HY000 Message: Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys + public final static int ER_CANT_REMOVE_ALL_FIELDS = 1090; //SQLSTATE: 42000 Message: You can't delete all columns with ALTER TABLE; use DROP TABLE instead + public final static int ER_CANT_DROP_FIELD_OR_KEY = 1091; //SQLSTATE: 42000 Message: Can't DROP '%s'; check that column/key exists + public final static int ER_INSERT_INFO = 1092; //SQLSTATE: HY000 Message: Records: %ld Duplicates: %ld Warnings: %ld + public final static int ER_UPDATE_TABLE_USED = 1093; //SQLSTATE: HY000 Message: You can't specify target table '%s' for update in FROM clause + public final static int ER_NO_SUCH_THREAD = 1094; //SQLSTATE: HY000 Message: Unknown thread id: %lu + public final static int ER_KILL_DENIED_ERROR = 1095; //SQLSTATE: HY000 Message: You are not owner of thread %lu + public final static int ER_NO_TABLES_USED = 1096; //SQLSTATE: HY000 Message: No tables used + public final static int ER_TOO_BIG_SET = 1097; //SQLSTATE: HY000 Message: Too many strings for column %s and SET + public final static int ER_NO_UNIQUE_LOGFILE = 1098; //SQLSTATE: HY000 Message: Can't generate a unique log-filename %s.(1-999) + public final static int ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099; //SQLSTATE: HY000 Message: Table '%s' was locked with a READ lock and can't be updated + public final static int ER_TABLE_NOT_LOCKED = 1100; //SQLSTATE: HY000 Message: Table '%s' was not locked with LOCK TABLES + public final static int ER_BLOB_CANT_HAVE_DEFAULT = 1101; //SQLSTATE: 42000 Message: BLOB/TEXT column '%s' can't have a default value + public final static int ER_WRONG_DB_NAME = 1102; //SQLSTATE: 42000 Message: Incorrect database name '%s' + public final static int ER_WRONG_TABLE_NAME = 1103; //SQLSTATE: 42000 Message: Incorrect table name '%s' + public final static int ER_TOO_BIG_SELECT = 1104; //SQLSTATE: 42000 Message: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay + public final static int ER_UNKNOWN_ERROR = 1105; //SQLSTATE: HY000 Message: Unknown error + public final static int ER_UNKNOWN_PROCEDURE = 1106; //SQLSTATE: 42000 Message: Unknown procedure '%s' + public final static int ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107; //SQLSTATE: 42000 Message: Incorrect parameter count to procedure '%s' + public final static int ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108; //SQLSTATE: HY000 Message: Incorrect parameters to procedure '%s' + public final static int ER_UNKNOWN_TABLE = 1109; //SQLSTATE: 42S02 Message: Unknown table '%s' in %s + public final static int ER_FIELD_SPECIFIED_TWICE = 1110; //SQLSTATE: 42000 Message: Column '%s' specified twice + public final static int ER_INVALID_GROUP_FUNC_USE = 1111; //SQLSTATE: HY000 Message: Invalid use of group function + public final static int ER_UNSUPPORTED_EXTENSION = 1112; //SQLSTATE: 42000 Message: Table '%s' uses an extension that doesn't exist in this MySQL version + public final static int ER_TABLE_MUST_HAVE_COLUMNS = 1113; //SQLSTATE: 42000 Message: A table must have at least 1 column + public final static int ER_RECORD_FILE_FULL = 1114; //SQLSTATE: HY000 Message: The table '%s' is full + public final static int ER_UNKNOWN_CHARACTER_SET = 1115; //SQLSTATE: 42000 Message: Unknown character set: '%s' + public final static int ER_TOO_MANY_TABLES = 1116; //SQLSTATE: HY000 Message: Too many tables; MySQL can only use %d tables in a join + public final static int ER_TOO_MANY_FIELDS = 1117; //SQLSTATE: HY000 Message: Too many columns + public final static int ER_TOO_BIG_ROWSIZE = 1118; //SQLSTATE: 42000 Message: Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs + public final static int ER_STACK_OVERRUN = 1119; //SQLSTATE: HY000 Message: Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld --thread_stack=#' to specify a bigger stack if needed + public final static int ER_WRONG_OUTER_JOIN = 1120; //SQLSTATE: 42000 Message: Cross dependency found in OUTER JOIN; examine your ON conditions + public final static int ER_NULL_COLUMN_IN_INDEX = 1121; //SQLSTATE: 42000 Message: Table handler doesn't support NULL in given index. Please change column '%s' to be NOT NULL or use another handler + public final static int ER_CANT_FIND_UDF = 1122; //SQLSTATE: HY000 Message: Can't load function '%s' + public final static int ER_CANT_INITIALIZE_UDF = 1123; //SQLSTATE: HY000 Message: Can't initialize function '%s'; %s + public final static int ER_UDF_NO_PATHS = 1124; //SQLSTATE: HY000 Message: No paths allowed for shared library + public final static int ER_UDF_EXISTS = 1125; //SQLSTATE: HY000 Message: Function '%s' already exists + public final static int ER_CANT_OPEN_LIBRARY = 1126; //SQLSTATE: HY000 Message: Can't open shared library '%s' (errno: %d %s) + public final static int ER_CANT_FIND_DL_ENTRY = 1127; //SQLSTATE: HY000 Message: Can't find symbol '%s' in library + public final static int ER_FUNCTION_NOT_DEFINED = 1128; //SQLSTATE: HY000 Message: Function '%s' is not defined + public final static int ER_HOST_IS_BLOCKED = 1129; //SQLSTATE: HY000 Message: Host '%s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' + public final static int ER_HOST_NOT_PRIVILEGED = 1130; //SQLSTATE: HY000 Message: Host '%s' is not allowed to connect to this MySQL server + public final static int ER_PASSWORD_ANONYMOUS_USER = 1131; //SQLSTATE: 42000 Message: You are using MySQL as an anonymous user and anonymous users are not allowed to change passwords + public final static int ER_PASSWORD_NOT_ALLOWED = 1132; //SQLSTATE: 42000 Message: You must have privileges to update tables in the mysql database to be able to change passwords for others + public final static int ER_PASSWORD_NO_MATCH = 1133; //SQLSTATE: 42000 Message: Can't find any matching row in the user table + public final static int ER_UPDATE_INFO = 1134; //SQLSTATE: HY000 Message: Rows matched: %ld Changed: %ld Warnings: %ld + public final static int ER_CANT_CREATE_THREAD = 1135; //SQLSTATE: HY000 Message: Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug + public final static int ER_WRONG_VALUE_COUNT_ON_ROW = 1136; //SQLSTATE: 21S01 Message: Column count doesn't match value count at row %ld + public final static int ER_CANT_REOPEN_TABLE = 1137; //SQLSTATE: HY000 Message: Can't reopen table: '%s' + public final static int ER_INVALID_USE_OF_NULL = 1138; //SQLSTATE: 22004 Message: Invalid use of NULL value + public final static int ER_REGEXP_ERROR = 1139; //SQLSTATE: 42000 Message: Got error '%s' from regexp + public final static int ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140; //SQLSTATE: 42000 Message: Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause + public final static int ER_NONEXISTING_GRANT = 1141; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' + public final static int ER_TABLEACCESS_DENIED_ERROR = 1142; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for table '%s' + public final static int ER_COLUMNACCESS_DENIED_ERROR = 1143; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for column '%s' in table '%s' + public final static int ER_ILLEGAL_GRANT_FOR_TABLE = 1144; //SQLSTATE: 42000 Message: Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used + public final static int ER_GRANT_WRONG_HOST_OR_USER = 1145; //SQLSTATE: 42000 Message: The host or user argument to GRANT is too long + public final static int ER_NO_SUCH_TABLE = 1146; //SQLSTATE: 42S02 Message: Table '%s.%s' doesn't exist + public final static int ER_NONEXISTING_TABLE_GRANT = 1147; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' on table '%s' + public final static int ER_NOT_ALLOWED_COMMAND = 1148; //SQLSTATE: 42000 Message: The used command is not allowed with this MySQL version + public final static int ER_SYNTAX_ERROR = 1149; //SQLSTATE: 42000 Message: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use + public final static int ER_DELAYED_CANT_CHANGE_LOCK = 1150; //SQLSTATE: HY000 Message: Delayed insert thread couldn't get requested lock for table %s + public final static int ER_TOO_MANY_DELAYED_THREADS = 1151; //SQLSTATE: HY000 Message: Too many delayed threads in use + public final static int ER_ABORTING_CONNECTION = 1152; //SQLSTATE: 08S01 Message: Aborted connection %ld to db: '%s' user: '%s' (%s) + public final static int ER_NET_PACKET_TOO_LARGE = 1153; //SQLSTATE: 08S01 Message: Got a packet bigger than 'max_allowed_packet' bytes + public final static int ER_NET_READ_ERROR_FROM_PIPE = 1154; //SQLSTATE: 08S01 Message: Got a read error from the connection pipe + public final static int ER_NET_FCNTL_ERROR = 1155; //SQLSTATE: 08S01 Message: Got an error from fcntl() + public final static int ER_NET_PACKETS_OUT_OF_ORDER = 1156; //SQLSTATE: 08S01 Message: Got packets out of order + public final static int ER_NET_UNCOMPRESS_ERROR = 1157; //SQLSTATE: 08S01 Message: Couldn't uncompress communication packet + public final static int ER_NET_READ_ERROR = 1158; //SQLSTATE: 08S01 Message: Got an error reading communication packets + public final static int ER_NET_READ_INTERRUPTED = 1159; //SQLSTATE: 08S01 Message: Got timeout reading communication packets + public final static int ER_NET_ERROR_ON_WRITE = 1160; //SQLSTATE: 08S01 Message: Got an error writing communication packets + public final static int ER_NET_WRITE_INTERRUPTED = 1161; //SQLSTATE: 08S01 Message: Got timeout writing communication packets + public final static int ER_TOO_LONG_STRING = 1162; //SQLSTATE: 42000 Message: Result string is longer than 'max_allowed_packet' bytes + public final static int ER_TABLE_CANT_HANDLE_BLOB = 1163; //SQLSTATE: 42000 Message: The used table type doesn't support BLOB/TEXT columns + public final static int ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164; //SQLSTATE: 42000 Message: The used table type doesn't support AUTO_INCREMENT columns + public final static int ER_DELAYED_INSERT_TABLE_LOCKED = 1165; //SQLSTATE: HY000 Message: INSERT DELAYED can't be used with table '%s' because it is locked with LOCK TABLES + public final static int ER_WRONG_COLUMN_NAME = 1166; //SQLSTATE: 42000 Message: Incorrect column name '%s' + public final static int ER_WRONG_KEY_COLUMN = 1167; //SQLSTATE: 42000 Message: The used storage engine can't index column '%s' + public final static int ER_WRONG_MRG_TABLE = 1168; //SQLSTATE: HY000 Message: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist + public final static int ER_DUP_UNIQUE = 1169; //SQLSTATE: 23000 Message: Can't write, because of unique constraint, to table '%s' + public final static int ER_BLOB_KEY_WITHOUT_LENGTH = 1170; //SQLSTATE: 42000 Message: BLOB/TEXT column '%s' used in key specification without a key length + public final static int ER_PRIMARY_CANT_HAVE_NULL = 1171; //SQLSTATE: 42000 Message: All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead + public final static int ER_TOO_MANY_ROWS = 1172; //SQLSTATE: 42000 Message: Result consisted of more than one row + public final static int ER_REQUIRES_PRIMARY_KEY = 1173; //SQLSTATE: 42000 Message: This table type requires a primary key + public final static int ER_NO_RAID_COMPILED = 1174; //SQLSTATE: HY000 Message: This version of MySQL is not compiled with RAID support + public final static int ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175; //SQLSTATE: HY000 Message: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column + public final static int ER_KEY_DOES_NOT_EXITS = 1176; //SQLSTATE: 42000 Message: Key '%s' doesn't exist in table '%s' + public final static int ER_CHECK_NO_SUCH_TABLE = 1177; //SQLSTATE: 42000 Message: Can't open table + public final static int ER_CHECK_NOT_IMPLEMENTED = 1178; //SQLSTATE: 42000 Message: The storage engine for the table doesn't support %s + public final static int ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179; //SQLSTATE: 25000 Message: You are not allowed to execute this command in a transaction + public final static int ER_ERROR_DURING_COMMIT = 1180; //SQLSTATE: HY000 Message: Got error %d during COMMIT + public final static int ER_ERROR_DURING_ROLLBACK = 1181; //SQLSTATE: HY000 Message: Got error %d during ROLLBACK + public final static int ER_ERROR_DURING_FLUSH_LOGS = 1182; //SQLSTATE: HY000 Message: Got error %d during FLUSH_LOGS + public final static int ER_ERROR_DURING_CHECKPOINT = 1183; //SQLSTATE: HY000 Message: Got error %d during CHECKPOINT + public final static int ER_NEW_ABORTING_CONNECTION = 1184; //SQLSTATE: 08S01 Message: Aborted connection %ld to db: '%s' user: '%s' host: '%s' (%s) + public final static int ER_DUMP_NOT_IMPLEMENTED = 1185; //SQLSTATE: HY000 Message: The storage engine for the table does not support binary table dump + public final static int ER_FLUSH_MASTER_BINLOG_CLOSED = 1186; //SQLSTATE: HY000 Message: Binlog closed, cannot RESET MASTER + public final static int ER_INDEX_REBUILD = 1187; //SQLSTATE: HY000 Message: Failed rebuilding the index of dumped table '%s' + public final static int ER_MASTER = 1188; //SQLSTATE: HY000 Message: Error from master: '%s' + public final static int ER_MASTER_NET_READ = 1189; //SQLSTATE: 08S01 Message: Net error reading from master + public final static int ER_MASTER_NET_WRITE = 1190; //SQLSTATE: 08S01 Message: Net error writing to master + public final static int ER_FT_MATCHING_KEY_NOT_FOUND = 1191; //SQLSTATE: HY000 Message: Can't find FULLTEXT index matching the column list + public final static int ER_LOCK_OR_ACTIVE_TRANSACTION = 1192; //SQLSTATE: HY000 Message: Can't execute the given command because you have active locked tables or an active transaction + public final static int ER_UNKNOWN_SYSTEM_VARIABLE = 1193; //SQLSTATE: HY000 Message: Unknown system variable '%s' + public final static int ER_CRASHED_ON_USAGE = 1194; //SQLSTATE: HY000 Message: Table '%s' is marked as crashed and should be repaired + public final static int ER_CRASHED_ON_REPAIR = 1195; //SQLSTATE: HY000 Message: Table '%s' is marked as crashed and last (automatic?) repair failed + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196; //SQLSTATE: HY000 Message: Some non-transactional changed tables couldn't be rolled back + public final static int ER_TRANS_CACHE_FULL = 1197; //SQLSTATE: HY000 Message: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again + public final static int ER_SLAVE_MUST_STOP = 1198; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave; run STOP SLAVE first + public final static int ER_SLAVE_NOT_RUNNING = 1199; //SQLSTATE: HY000 Message: This operation requires a running slave; configure slave and do START SLAVE + public final static int ER_BAD_SLAVE = 1200; //SQLSTATE: HY000 Message: The server is not configured as slave; fix in config file or with CHANGE MASTER TO + public final static int ER_MASTER_INFO = 1201; //SQLSTATE: HY000 Message: Could not initialize master info structure; more error messages can be found in the MySQL error log + public final static int ER_SLAVE_THREAD = 1202; //SQLSTATE: HY000 Message: Could not create slave thread; check system resources + public final static int ER_TOO_MANY_USER_CONNECTIONS = 1203; //SQLSTATE: 42000 Message: User %s already has more than 'max_user_connections' active connections + public final static int ER_SET_CONSTANTS_ONLY = 1204; //SQLSTATE: HY000 Message: You may only use constant expressions with SET + public final static int ER_LOCK_WAIT_TIMEOUT = 1205; //SQLSTATE: HY000 Message: Lock wait timeout exceeded; try restarting transaction + public final static int ER_LOCK_TABLE_FULL = 1206; //SQLSTATE: HY000 Message: The total number of locks exceeds the lock table size + public final static int ER_READ_ONLY_TRANSACTION = 1207; //SQLSTATE: 25000 Message: Update locks cannot be acquired during a READ UNCOMMITTED transaction + public final static int ER_DROP_DB_WITH_READ_LOCK = 1208; //SQLSTATE: HY000 Message: DROP DATABASE not allowed while thread is holding global read lock + public final static int ER_CREATE_DB_WITH_READ_LOCK = 1209; //SQLSTATE: HY000 Message: CREATE DATABASE not allowed while thread is holding global read lock + public final static int ER_WRONG_ARGUMENTS = 1210; //SQLSTATE: HY000 Message: Incorrect arguments to %s + public final static int ER_NO_PERMISSION_TO_CREATE_USER = 1211; //SQLSTATE: 42000 Message: '%s'@'%s' is not allowed to create new users + public final static int ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212; //SQLSTATE: HY000 Message: Incorrect table definition; all MERGE tables must be in the same database + public final static int ER_LOCK_DEADLOCK = 1213; //SQLSTATE: 40001 Message: Deadlock found when trying to get lock; try restarting transaction + public final static int ER_TABLE_CANT_HANDLE_FT = 1214; //SQLSTATE: HY000 Message: The used table type doesn't support FULLTEXT indexes + public final static int ER_CANNOT_ADD_FOREIGN = 1215; //SQLSTATE: HY000 Message: Cannot add foreign key constraint + public final static int ER_NO_REFERENCED_ROW = 1216; //SQLSTATE: 23000 Message: Cannot add or update a child row: a foreign key constraint fails + public final static int ER_ROW_IS_REFERENCED = 1217; //SQLSTATE: 23000 Message: Cannot delete or update a parent row: a foreign key constraint fails + public final static int ER_CONNECT_TO_MASTER = 1218; //SQLSTATE: 08S01 Message: Error connecting to master: %s + public final static int ER_QUERY_ON_MASTER = 1219; //SQLSTATE: HY000 Message: Error running query on master: %s + public final static int ER_ERROR_WHEN_EXECUTING_COMMAND = 1220; //SQLSTATE: HY000 Message: Error when executing command %s: %s + public final static int ER_WRONG_USAGE = 1221; //SQLSTATE: HY000 Message: Incorrect usage of %s and %s + public final static int ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222; //SQLSTATE: 21000 Message: The used SELECT statements have a different number of columns + public final static int ER_CANT_UPDATE_WITH_READLOCK = 1223; //SQLSTATE: HY000 Message: Can't execute the query because you have a conflicting read lock + public final static int ER_MIXING_NOT_ALLOWED = 1224; //SQLSTATE: HY000 Message: Mixing of transactional and non-transactional tables is disabled + public final static int ER_DUP_ARGUMENT = 1225; //SQLSTATE: HY000 Message: Option '%s' used twice in statement + public final static int ER_USER_LIMIT_REACHED = 1226; //SQLSTATE: 42000 Message: User '%s' has exceeded the '%s' resource (current value: %ld) + public final static int ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227; //SQLSTATE: 42000 Message: Access denied; you need (at least one of) the %s privilege(s) for this operation + public final static int ER_LOCAL_VARIABLE = 1228; //SQLSTATE: HY000 Message: Variable '%s' is a SESSION variable and can't be used with SET GLOBAL + public final static int ER_GLOBAL_VARIABLE = 1229; //SQLSTATE: HY000 Message: Variable '%s' is a GLOBAL variable and should be set with SET GLOBAL + public final static int ER_NO_DEFAULT = 1230; //SQLSTATE: 42000 Message: Variable '%s' doesn't have a default value + public final static int ER_WRONG_VALUE_FOR_VAR = 1231; //SQLSTATE: 42000 Message: Variable '%s' can't be set to the value of '%s' + public final static int ER_WRONG_TYPE_FOR_VAR = 1232; //SQLSTATE: 42000 Message: Incorrect argument type to variable '%s' + public final static int ER_VAR_CANT_BE_READ = 1233; //SQLSTATE: HY000 Message: Variable '%s' can only be set, not read + public final static int ER_CANT_USE_OPTION_HERE = 1234; //SQLSTATE: 42000 Message: Incorrect usage/placement of '%s' + public final static int ER_NOT_SUPPORTED_YET = 1235; //SQLSTATE: 42000 Message: This version of MySQL doesn't yet support '%s' + public final static int ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236; //SQLSTATE: HY000 Message: Got fatal error %d from master when reading data from binary log: '%s' + public final static int ER_SLAVE_IGNORED_TABLE = 1237; //SQLSTATE: HY000 Message: Slave SQL thread ignored the query because of replicate-*-table rules + public final static int ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238; //SQLSTATE: HY000 Message: Variable '%s' is a %s variable + public final static int ER_WRONG_FK_DEF = 1239; //SQLSTATE: 42000 Message: Incorrect foreign key definition for '%s': %s + public final static int ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240; //SQLSTATE: HY000 Message: Key reference and table reference don't match + public final static int ER_OPERAND_COLUMNS = 1241; //SQLSTATE: 21000 Message: Operand should contain %d column(s) + public final static int ER_SUBQUERY_NO_1_ROW = 1242; //SQLSTATE: 21000 Message: Subquery returns more than 1 row + public final static int ER_UNKNOWN_STMT_HANDLER = 1243; //SQLSTATE: HY000 Message: Unknown prepared statement handler (%.*s) given to %s + public final static int ER_CORRUPT_HELP_DB = 1244; //SQLSTATE: HY000 Message: Help database is corrupt or does not exist + public final static int ER_CYCLIC_REFERENCE = 1245; //SQLSTATE: HY000 Message: Cyclic reference on subqueries + public final static int ER_AUTO_CONVERT = 1246; //SQLSTATE: HY000 Message: Converting column '%s' from %s to %s + public final static int ER_ILLEGAL_REFERENCE = 1247; //SQLSTATE: 42S22 Message: Reference '%s' not supported (%s) + public final static int ER_DERIVED_MUST_HAVE_ALIAS = 1248; //SQLSTATE: 42000 Message: Every derived table must have its own alias + public final static int ER_SELECT_REDUCED = 1249; //SQLSTATE: 01000 Message: Select %u was reduced during optimization + public final static int ER_TABLENAME_NOT_ALLOWED_HERE = 1250; //SQLSTATE: 42000 Message: Table '%s' from one of the SELECTs cannot be used in %s + public final static int ER_NOT_SUPPORTED_AUTH_MODE = 1251; //SQLSTATE: 08004 Message: Client does not support authentication protocol requested by server; consider upgrading MySQL client + public final static int ER_SPATIAL_CANT_HAVE_NULL = 1252; //SQLSTATE: 42000 Message: All parts of a SPATIAL index must be NOT NULL + public final static int ER_COLLATION_CHARSET_MISMATCH = 1253; //SQLSTATE: 42000 Message: COLLATION '%s' is not valid for CHARACTER SET '%s' + public final static int ER_SLAVE_WAS_RUNNING = 1254; //SQLSTATE: HY000 Message: Slave is already running + public final static int ER_SLAVE_WAS_NOT_RUNNING = 1255; //SQLSTATE: HY000 Message: Slave already has been stopped + public final static int ER_TOO_BIG_FOR_UNCOMPRESS = 1256; //SQLSTATE: HY000 Message: Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted) + public final static int ER_ZLIB_Z_MEM_ERROR = 1257; //SQLSTATE: HY000 Message: ZLIB: Not enough memory + public final static int ER_ZLIB_Z_BUF_ERROR = 1258; //SQLSTATE: HY000 Message: ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted) + public final static int ER_ZLIB_Z_DATA_ERROR = 1259; //SQLSTATE: HY000 Message: ZLIB: Input data corrupted + public final static int ER_CUT_VALUE_GROUP_CONCAT = 1260; //SQLSTATE: HY000 Message: Row %u was cut by GROUP_CONCAT() + public final static int ER_WARN_TOO_FEW_RECORDS = 1261; //SQLSTATE: 01000 Message: Row %ld doesn't contain data for all columns + public final static int ER_WARN_TOO_MANY_RECORDS = 1262; //SQLSTATE: 01000 Message: Row %ld was truncated; it contained more data than there were input columns + public final static int ER_WARN_NULL_TO_NOTNULL = 1263; //SQLSTATE: 22004 Message: Column set to default value; NULL supplied to NOT NULL column '%s' at row %ld + public final static int ER_WARN_DATA_OUT_OF_RANGE = 1264; //SQLSTATE: 22003 Message: Out of range value for column '%s' at row %ld + public final static int ER_WARN_DATA_TRUNCATED = 1265; //SQLSTATE: 01000 Message: Data truncated for column '%s' at row %ld + public final static int ER_WARN_USING_OTHER_HANDLER = 1266; //SQLSTATE: HY000 Message: Using storage engine %s for table '%s' + public final static int ER_CANT_AGGREGATE_2COLLATIONS = 1267; //SQLSTATE: HY000 Message: Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s' + public final static int ER_DROP_USER = 1268; //SQLSTATE: HY000 Message: Cannot drop one or more of the requested users + public final static int ER_REVOKE_GRANTS = 1269; //SQLSTATE: HY000 Message: Can't revoke all privileges for one or more of the requested users + public final static int ER_CANT_AGGREGATE_3COLLATIONS = 1270; //SQLSTATE: HY000 Message: Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s' + public final static int ER_CANT_AGGREGATE_NCOLLATIONS = 1271; //SQLSTATE: HY000 Message: Illegal mix of collations for operation '%s' + public final static int ER_VARIABLE_IS_NOT_STRUCT = 1272; //SQLSTATE: HY000 Message: Variable '%s' is not a variable component (can't be used as XXXX.variable_name) + public final static int ER_UNKNOWN_COLLATION = 1273; //SQLSTATE: HY000 Message: Unknown collation: '%s' + public final static int ER_SLAVE_IGNORED_SSL_PARAMS = 1274; //SQLSTATE: HY000 Message: SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started + public final static int ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275; //SQLSTATE: HY000 Message: Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format + public final static int ER_WARN_FIELD_RESOLVED = 1276; //SQLSTATE: HY000 Message: Field or reference '%s%s%s%s%s' of SELECT #%d was resolved in SELECT #%d + public final static int ER_BAD_SLAVE_UNTIL_COND = 1277; //SQLSTATE: HY000 Message: Incorrect parameter or combination of parameters for START SLAVE UNTIL + public final static int ER_MISSING_SKIP_SLAVE = 1278; //SQLSTATE: HY000 Message: It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart + public final static int ER_UNTIL_COND_IGNORED = 1279; //SQLSTATE: HY000 Message: SQL thread is not to be started so UNTIL options are ignored + public final static int ER_WRONG_NAME_FOR_INDEX = 1280; //SQLSTATE: 42000 Message: Incorrect index name '%s' + public final static int ER_WRONG_NAME_FOR_CATALOG = 1281; //SQLSTATE: 42000 Message: Incorrect catalog name '%s' + public final static int ER_WARN_QC_RESIZE = 1282; //SQLSTATE: HY000 Message: Query cache failed to set size %lu; new query cache size is %lu + public final static int ER_BAD_FT_COLUMN = 1283; //SQLSTATE: HY000 Message: Column '%s' cannot be part of FULLTEXT index + public final static int ER_UNKNOWN_KEY_CACHE = 1284; //SQLSTATE: HY000 Message: Unknown key cache '%s' + public final static int ER_WARN_HOSTNAME_WONT_WORK = 1285; //SQLSTATE: HY000 Message: MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work + public final static int ER_UNKNOWN_STORAGE_ENGINE = 1286; //SQLSTATE: 42000 Message: Unknown storage engine '%s' + public final static int ER_WARN_DEPRECATED_SYNTAX = 1287; //SQLSTATE: HY000 Message: '%s' is deprecated and will be removed in a future release. Please use %s instead + public final static int ER_NON_UPDATABLE_TABLE = 1288; //SQLSTATE: HY000 Message: The target table %s of the %s is not updatable + public final static int ER_FEATURE_DISABLED = 1289; //SQLSTATE: HY000 Message: The '%s' feature is disabled; you need MySQL built with '%s' to have it working + public final static int ER_OPTION_PREVENTS_STATEMENT = 1290; //SQLSTATE: HY000 Message: The MySQL server is running with the %s option so it cannot execute this statement + public final static int ER_DUPLICATED_VALUE_IN_TYPE = 1291; //SQLSTATE: HY000 Message: Column '%s' has duplicated value '%s' in %s + public final static int ER_TRUNCATED_WRONG_VALUE = 1292; //SQLSTATE: 22007 Message: Truncated incorrect %s value: '%s' + public final static int ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293; //SQLSTATE: HY000 Message: Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause + public final static int ER_INVALID_ON_UPDATE = 1294; //SQLSTATE: HY000 Message: Invalid ON UPDATE clause for '%s' column + public final static int ER_UNSUPPORTED_PS = 1295; //SQLSTATE: HY000 Message: This command is not supported in the prepared statement protocol yet + public final static int ER_GET_ERRMSG = 1296; //SQLSTATE: HY000 Message: Got error %d '%s' from %s + public final static int ER_GET_TEMPORARY_ERRMSG = 1297; //SQLSTATE: HY000 Message: Got temporary error %d '%s' from %s + public final static int ER_UNKNOWN_TIME_ZONE = 1298; //SQLSTATE: HY000 Message: Unknown or incorrect time zone: '%s' + public final static int ER_WARN_INVALID_TIMESTAMP = 1299; //SQLSTATE: HY000 Message: Invalid TIMESTAMP value in column '%s' at row %ld + public final static int ER_INVALID_CHARACTER_STRING = 1300; //SQLSTATE: HY000 Message: Invalid %s character string: '%s' + public final static int ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301; //SQLSTATE: HY000 Message: Result of %s() was larger than max_allowed_packet (%ld) - truncated + public final static int ER_CONFLICTING_DECLARATIONS = 1302; //SQLSTATE: HY000 Message: Conflicting declarations: '%s%s' and '%s%s' + public final static int ER_SP_NO_RECURSIVE_CREATE = 1303; //SQLSTATE: 2F003 Message: Can't create a %s from within another stored routine + public final static int ER_SP_ALREADY_EXISTS = 1304; //SQLSTATE: 42000 Message: %s %s already exists + public final static int ER_SP_DOES_NOT_EXIST = 1305; //SQLSTATE: 42000 Message: %s %s does not exist + public final static int ER_SP_DROP_FAILED = 1306; //SQLSTATE: HY000 Message: Failed to DROP %s %s + public final static int ER_SP_STORE_FAILED = 1307; //SQLSTATE: HY000 Message: Failed to CREATE %s %s + public final static int ER_SP_LILABEL_MISMATCH = 1308; //SQLSTATE: 42000 Message: %s with no matching label: %s + public final static int ER_SP_LABEL_REDEFINE = 1309; //SQLSTATE: 42000 Message: Redefining label %s + public final static int ER_SP_LABEL_MISMATCH = 1310; //SQLSTATE: 42000 Message: End-label %s without match + public final static int ER_SP_UNINIT_VAR = 1311; //SQLSTATE: 01000 Message: Referring to uninitialized variable %s + public final static int ER_SP_BADSELECT = 1312; //SQLSTATE: 0A000 Message: PROCEDURE %s can't return a result set in the given context + public final static int ER_SP_BADRETURN = 1313; //SQLSTATE: 42000 Message: RETURN is only allowed in a FUNCTION + public final static int ER_SP_BADSTATEMENT = 1314; //SQLSTATE: 0A000 Message: %s is not allowed in stored procedures + public final static int ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315; //SQLSTATE: 42000 Message: The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored. This option will be removed in MySQL 5.6. + public final static int ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316; //SQLSTATE: 42000 Message: The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN. This option will be removed in MySQL 5.6. + public final static int ER_QUERY_INTERRUPTED = 1317; //SQLSTATE: 70100 Message: Query execution was interrupted + public final static int ER_SP_WRONG_NO_OF_ARGS = 1318; //SQLSTATE: 42000 Message: Incorrect number of arguments for %s %s; expected %u, got %u + public final static int ER_SP_COND_MISMATCH = 1319; //SQLSTATE: 42000 Message: Undefined CONDITION: %s + public final static int ER_SP_NORETURN = 1320; //SQLSTATE: 42000 Message: No RETURN found in FUNCTION %s + public final static int ER_SP_NORETURNEND = 1321; //SQLSTATE: 2F005 Message: FUNCTION %s ended without RETURN + public final static int ER_SP_BAD_CURSOR_QUERY = 1322; //SQLSTATE: 42000 Message: Cursor statement must be a SELECT + public final static int ER_SP_BAD_CURSOR_SELECT = 1323; //SQLSTATE: 42000 Message: Cursor SELECT must not have INTO + public final static int ER_SP_CURSOR_MISMATCH = 1324; //SQLSTATE: 42000 Message: Undefined CURSOR: %s + public final static int ER_SP_CURSOR_ALREADY_OPEN = 1325; //SQLSTATE: 24000 Message: Cursor is already open + public final static int ER_SP_CURSOR_NOT_OPEN = 1326; //SQLSTATE: 24000 Message: Cursor is not open + public final static int ER_SP_UNDECLARED_VAR = 1327; //SQLSTATE: 42000 Message: Undeclared variable: %s + public final static int ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328; //SQLSTATE: HY000 Message: Incorrect number of FETCH variables + public final static int ER_SP_FETCH_NO_DATA = 1329; //SQLSTATE: 02000 Message: No data - zero rows fetched, selected, or processed + public final static int ER_SP_DUP_PARAM = 1330; //SQLSTATE: 42000 Message: Duplicate parameter: %s + public final static int ER_SP_DUP_VAR = 1331; //SQLSTATE: 42000 Message: Duplicate variable: %s + public final static int ER_SP_DUP_COND = 1332; //SQLSTATE: 42000 Message: Duplicate condition: %s + public final static int ER_SP_DUP_CURS = 1333; //SQLSTATE: 42000 Message: Duplicate cursor: %s + public final static int ER_SP_CANT_ALTER = 1334; //SQLSTATE: HY000 Message: Failed to ALTER %s %s + public final static int ER_SP_SUBSELECT_NYI = 1335; //SQLSTATE: 0A000 Message: Subquery value not supported + public final static int ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336; //SQLSTATE: 0A000 Message: %s is not allowed in stored function or trigger + public final static int ER_SP_VARCOND_AFTER_CURSHNDLR = 1337; //SQLSTATE: 42000 Message: Variable or condition declaration after cursor or handler declaration + public final static int ER_SP_CURSOR_AFTER_HANDLER = 1338; //SQLSTATE: 42000 Message: Cursor declaration after handler declaration + public final static int ER_SP_CASE_NOT_FOUND = 1339; //SQLSTATE: 20000 Message: Case not found for CASE statement + public final static int ER_FPARSER_TOO_BIG_FILE = 1340; //SQLSTATE: HY000 Message: Configuration file '%s' is too big + public final static int ER_FPARSER_BAD_HEADER = 1341; //SQLSTATE: HY000 Message: Malformed file type header in file '%s' + public final static int ER_FPARSER_EOF_IN_COMMENT = 1342; //SQLSTATE: HY000 Message: Unexpected end of file while parsing comment '%s' + public final static int ER_FPARSER_ERROR_IN_PARAMETER = 1343; //SQLSTATE: HY000 Message: Error while parsing parameter '%s' (line: '%s') + public final static int ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344; //SQLSTATE: HY000 Message: Unexpected end of file while skipping unknown parameter '%s' + public final static int ER_VIEW_NO_EXPLAIN = 1345; //SQLSTATE: HY000 Message: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table + public final static int ER_FRM_UNKNOWN_TYPE = 1346; //SQLSTATE: HY000 Message: File '%s' has unknown type '%s' in its header + public final static int ER_WRONG_OBJECT = 1347; //SQLSTATE: HY000 Message: '%s.%s' is not %s + public final static int ER_NONUPDATEABLE_COLUMN = 1348; //SQLSTATE: HY000 Message: Column '%s' is not updatable + public final static int ER_VIEW_SELECT_DERIVED = 1349; //SQLSTATE: HY000 Message: View's SELECT contains a subquery in the FROM clause + public final static int ER_VIEW_SELECT_CLAUSE = 1350; //SQLSTATE: HY000 Message: View's SELECT contains a '%s' clause + public final static int ER_VIEW_SELECT_VARIABLE = 1351; //SQLSTATE: HY000 Message: View's SELECT contains a variable or parameter + public final static int ER_VIEW_SELECT_TMPTABLE = 1352; //SQLSTATE: HY000 Message: View's SELECT refers to a temporary table '%s' + public final static int ER_VIEW_WRONG_LIST = 1353; //SQLSTATE: HY000 Message: View's SELECT and view's field list have different column counts + public final static int ER_WARN_VIEW_MERGE = 1354; //SQLSTATE: HY000 Message: View merge algorithm can't be used here for now (assumed undefined algorithm) + public final static int ER_WARN_VIEW_WITHOUT_KEY = 1355; //SQLSTATE: HY000 Message: View being updated does not have complete key of underlying table in it + public final static int ER_VIEW_INVALID = 1356; //SQLSTATE: HY000 Message: View '%s.%s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them + public final static int ER_SP_NO_DROP_SP = 1357; //SQLSTATE: HY000 Message: Can't drop or alter a %s from within another stored routine + public final static int ER_SP_GOTO_IN_HNDLR = 1358; //SQLSTATE: HY000 Message: GOTO is not allowed in a stored procedure handler + public final static int ER_TRG_ALREADY_EXISTS = 1359; //SQLSTATE: HY000 Message: Trigger already exists + public final static int ER_TRG_DOES_NOT_EXIST = 1360; //SQLSTATE: HY000 Message: Trigger does not exist + public final static int ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361; //SQLSTATE: HY000 Message: Trigger's '%s' is view or temporary table + public final static int ER_TRG_CANT_CHANGE_ROW = 1362; //SQLSTATE: HY000 Message: Updating of %s row is not allowed in %strigger + public final static int ER_TRG_NO_SUCH_ROW_IN_TRG = 1363; //SQLSTATE: HY000 Message: There is no %s row in %s trigger + public final static int ER_NO_DEFAULT_FOR_FIELD = 1364; //SQLSTATE: HY000 Message: Field '%s' doesn't have a default value + public final static int ER_DIVISION_BY_ZERO = 1365; //SQLSTATE: 22012 Message: Division by 0 + public final static int ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' for column '%s' at row %ld + public final static int ER_ILLEGAL_VALUE_FOR_TYPE = 1367; //SQLSTATE: 22007 Message: Illegal %s '%s' value found during parsing + public final static int ER_VIEW_NONUPD_CHECK = 1368; //SQLSTATE: HY000 Message: CHECK OPTION on non-updatable view '%s.%s' + public final static int ER_VIEW_CHECK_FAILED = 1369; //SQLSTATE: HY000 Message: CHECK OPTION failed '%s.%s' + public final static int ER_PROCACCESS_DENIED_ERROR = 1370; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for routine '%s' + public final static int ER_RELAY_LOG_FAIL = 1371; //SQLSTATE: HY000 Message: Failed purging old relay logs: %s + public final static int ER_PASSWD_LENGTH = 1372; //SQLSTATE: HY000 Message: Password hash should be a %d-digit hexadecimal number + public final static int ER_UNKNOWN_TARGET_BINLOG = 1373; //SQLSTATE: HY000 Message: Target log not found in binlog index + public final static int ER_IO_ERR_LOG_INDEX_READ = 1374; //SQLSTATE: HY000 Message: I/O error reading log index file + public final static int ER_BINLOG_PURGE_PROHIBITED = 1375; //SQLSTATE: HY000 Message: Server configuration does not permit binlog purge + public final static int ER_FSEEK_FAIL = 1376; //SQLSTATE: HY000 Message: Failed on fseek() + public final static int ER_BINLOG_PURGE_FATAL_ERR = 1377; //SQLSTATE: HY000 Message: Fatal error during log purge + public final static int ER_LOG_IN_USE = 1378; //SQLSTATE: HY000 Message: A purgeable log is in use, will not purge + public final static int ER_LOG_PURGE_UNKNOWN_ERR = 1379; //SQLSTATE: HY000 Message: Unknown error during log purge + public final static int ER_RELAY_LOG_INIT = 1380; //SQLSTATE: HY000 Message: Failed initializing relay log position: %s + public final static int ER_NO_BINARY_LOGGING = 1381; //SQLSTATE: HY000 Message: You are not using binary logging + public final static int ER_RESERVED_SYNTAX = 1382; //SQLSTATE: HY000 Message: The '%s' syntax is reserved for purposes internal to the MySQL server + public final static int ER_WSAS_FAILED = 1383; //SQLSTATE: HY000 Message: WSAStartup Failed + public final static int ER_DIFF_GROUPS_PROC = 1384; //SQLSTATE: HY000 Message: Can't handle procedures with different groups yet + public final static int ER_NO_GROUP_FOR_PROC = 1385; //SQLSTATE: HY000 Message: Select must have a group with this procedure + public final static int ER_ORDER_WITH_PROC = 1386; //SQLSTATE: HY000 Message: Can't use ORDER clause with this procedure + public final static int ER_LOGGING_PROHIBIT_CHANGING_OF = 1387; //SQLSTATE: HY000 Message: Binary logging and replication forbid changing the global server %s + public final static int ER_NO_FILE_MAPPING = 1388; //SQLSTATE: HY000 Message: Can't map file: %s, errno: %d + public final static int ER_WRONG_MAGIC = 1389; //SQLSTATE: HY000 Message: Wrong magic in %s + public final static int ER_PS_MANY_PARAM = 1390; //SQLSTATE: HY000 Message: Prepared statement contains too many placeholders + public final static int ER_KEY_PART_0 = 1391; //SQLSTATE: HY000 Message: Key part '%s' length cannot be 0 + public final static int ER_VIEW_CHECKSUM = 1392; //SQLSTATE: HY000 Message: View text checksum failed + public final static int ER_VIEW_MULTIUPDATE = 1393; //SQLSTATE: HY000 Message: Can not modify more than one base table through a join view '%s.%s' + public final static int ER_VIEW_NO_INSERT_FIELD_LIST = 1394; //SQLSTATE: HY000 Message: Can not insert into join view '%s.%s' without fields list + public final static int ER_VIEW_DELETE_MERGE_VIEW = 1395; //SQLSTATE: HY000 Message: Can not delete from join view '%s.%s' + public final static int ER_CANNOT_USER = 1396; //SQLSTATE: HY000 Message: Operation %s failed for %s + public final static int ER_XAER_NOTA = 1397; //SQLSTATE: XAE04 Message: XAER_NOTA: Unknown XID + public final static int ER_XAER_INVAL = 1398; //SQLSTATE: XAE05 Message: XAER_INVAL: Invalid arguments (or unsupported command) + public final static int ER_XAER_RMFAIL = 1399; //SQLSTATE: XAE07 Message: XAER_RMFAIL: The command cannot be executed when global transaction is in the %s state + public final static int ER_XAER_OUTSIDE = 1400; //SQLSTATE: XAE09 Message: XAER_OUTSIDE: Some work is done outside global transaction + public final static int ER_XA_RMERR = 1401; + public final static int ER_XA_RBROLLBACK = 1402; //SQLSTATE: XA100 Message: XA_RBROLLBACK: Transaction branch was rolled back + public final static int ER_NONEXISTING_PROC_GRANT = 1403; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' on routine '%s' + public final static int ER_PROC_AUTO_GRANT_FAIL = 1404; //SQLSTATE: HY000 Message: Failed to grant EXECUTE and ALTER ROUTINE privileges + public final static int ER_PROC_AUTO_REVOKE_FAIL = 1405; //SQLSTATE: HY000 Message: Failed to revoke all privileges to dropped routine + public final static int ER_DATA_TOO_LONG = 1406; //SQLSTATE: 22001 Message: Data too long for column '%s' at row %ld + public final static int ER_SP_BAD_SQLSTATE = 1407; //SQLSTATE: 42000 Message: Bad; //SQLSTATE: '%s' + public final static int ER_STARTUP = 1408; //SQLSTATE: HY000 Message: %s: ready for connections. Version: '%s' socket: '%s' port: %d %s + public final static int ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409; //SQLSTATE: HY000 Message: Can't load value from file with fixed size rows to variable + public final static int ER_CANT_CREATE_USER_WITH_GRANT = 1410; //SQLSTATE: 42000 Message: You are not allowed to create a user with GRANT + public final static int ER_WRONG_VALUE_FOR_TYPE = 1411; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' for function %s + public final static int ER_TABLE_DEF_CHANGED = 1412; //SQLSTATE: HY000 Message: Table definition has changed, please retry transaction + public final static int ER_SP_DUP_HANDLER = 1413; //SQLSTATE: 42000 Message: Duplicate handler declared in the same block + public final static int ER_SP_NOT_VAR_ARG = 1414; //SQLSTATE: 42000 Message: OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger + public final static int ER_SP_NO_RETSET = 1415; //SQLSTATE: 0A000 Message: Not allowed to return a result set from a %s + public final static int ER_CANT_CREATE_GEOMETRY_OBJECT = 1416; //SQLSTATE: 22003 Message: Cannot get geometry object from data you send to the GEOMETRY field + public final static int ER_FAILED_ROUTINE_BREAK_BINLOG = 1417; //SQLSTATE: HY000 Message: A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes + public final static int ER_BINLOG_UNSAFE_ROUTINE = 1418; //SQLSTATE: HY000 Message: This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) + public final static int ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419; //SQLSTATE: HY000 Message: You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) + public final static int ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420; //SQLSTATE: HY000 Message: You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it. + public final static int ER_STMT_HAS_NO_OPEN_CURSOR = 1421; //SQLSTATE: HY000 Message: The statement (%lu) has no open cursor. + public final static int ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422; //SQLSTATE: HY000 Message: Explicit or implicit commit is not allowed in stored function or trigger. + public final static int ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423; //SQLSTATE: HY000 Message: Field of view '%s.%s' underlying table doesn't have a default value + public final static int ER_SP_NO_RECURSION = 1424; //SQLSTATE: HY000 Message: Recursive stored functions and triggers are not allowed. + public final static int ER_TOO_BIG_SCALE = 1425; //SQLSTATE: 42000 Message: Too big scale %d specified for column '%s'. Maximum is %lu. + public final static int ER_TOO_BIG_PRECISION = 1426; //SQLSTATE: 42000 Message: Too big precision %d specified for column '%s'. Maximum is %lu. + public final static int ER_M_BIGGER_THAN_D = 1427; //SQLSTATE: 42000 Message: For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%s'). + public final static int ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428; //SQLSTATE: HY000 Message: You can't combine write-locking of system tables with other tables or lock types + public final static int ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429; //SQLSTATE: HY000 Message: Unable to connect to foreign data source: %s + public final static int ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430; //SQLSTATE: HY000 Message: There was a problem processing the query on the foreign data source. Data source error: %s + public final static int ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431; //SQLSTATE: HY000 Message: The foreign data source you are trying to reference does not exist. Data source error: %s + public final static int ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432; //SQLSTATE: HY000 Message: Can't create federated table. The data source connection string '%s' is not in the correct format + public final static int ER_FOREIGN_DATA_STRING_INVALID = 1433; //SQLSTATE: HY000 Message: The data source connection string '%s' is not in the correct format + public final static int ER_CANT_CREATE_FEDERATED_TABLE = 1434; //SQLSTATE: HY000 Message: Can't create federated table. Foreign data src error: %s + public final static int ER_TRG_IN_WRONG_SCHEMA = 1435; //SQLSTATE: HY000 Message: Trigger in wrong schema + public final static int ER_STACK_OVERRUN_NEED_MORE = 1436; //SQLSTATE: HY000 Message: Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld --thread_stack=#' to specify a bigger stack. + public final static int ER_TOO_LONG_BODY = 1437; //SQLSTATE: 42000 Message: Routine body for '%s' is too long + public final static int ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438; //SQLSTATE: HY000 Message: Cannot drop default keycache + public final static int ER_TOO_BIG_DISPLAYWIDTH = 1439; //SQLSTATE: 42000 Message: Display width out of range for column '%s' (max = %lu) + public final static int ER_XAER_DUPID = 1440; //SQLSTATE: XAE08 Message: XAER_DUPID: The XID already exists + public final static int ER_DATETIME_FUNCTION_OVERFLOW = 1441; //SQLSTATE: 22008 Message: Datetime function: %s field overflow + public final static int ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442; //SQLSTATE: HY000 Message: Can't update table '%s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. + public final static int ER_VIEW_PREVENT_UPDATE = 1443; //SQLSTATE: HY000 Message: The definition of table '%s' prevents operation %s on table '%s'. + public final static int ER_PS_NO_RECURSION = 1444; //SQLSTATE: HY000 Message: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner + public final static int ER_SP_CANT_SET_AUTOCOMMIT = 1445; //SQLSTATE: HY000 Message: Not allowed to set autocommit from a stored function or trigger + public final static int ER_MALFORMED_DEFINER = 1446; //SQLSTATE: HY000 Message: Definer is not fully qualified + public final static int ER_VIEW_FRM_NO_USER = 1447; //SQLSTATE: HY000 Message: View '%s'.'%s' has no definer information (old table format). Current user is used as definer. Please recreate the view! + public final static int ER_VIEW_OTHER_USER = 1448; //SQLSTATE: HY000 Message: You need the SUPER privilege for creation view with '%s'@'%s' definer + public final static int ER_NO_SUCH_USER = 1449; //SQLSTATE: HY000 Message: The user specified as a definer ('%s'@'%s') does not exist + public final static int ER_FORBID_SCHEMA_CHANGE = 1450; //SQLSTATE: HY000 Message: Changing schema from '%s' to '%s' is not allowed. + public final static int ER_ROW_IS_REFERENCED_2 = 1451; //SQLSTATE: 23000 Message: Cannot delete or update a parent row: a foreign key constraint fails (%s) + public final static int ER_NO_REFERENCED_ROW_2 = 1452; //SQLSTATE: 23000 Message: Cannot add or update a child row: a foreign key constraint fails (%s) + public final static int ER_SP_BAD_VAR_SHADOW = 1453; //SQLSTATE: 42000 Message: Variable '%s' must be quoted with `...`, or renamed + public final static int ER_TRG_NO_DEFINER = 1454; //SQLSTATE: HY000 Message: No definer attribute for trigger '%s'.'%s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. + public final static int ER_OLD_FILE_FORMAT = 1455; //SQLSTATE: HY000 Message: '%s' has an old format, you should re-create the '%s' object(s) + public final static int ER_SP_RECURSION_LIMIT = 1456; //SQLSTATE: HY000 Message: Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %s + public final static int ER_SP_PROC_TABLE_CORRUPT = 1457; //SQLSTATE: HY000 Message: Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d) + public final static int ER_SP_WRONG_NAME = 1458; //SQLSTATE: 42000 Message: Incorrect routine name '%s' + public final static int ER_TABLE_NEEDS_UPGRADE = 1459; //SQLSTATE: HY000 Message: Table upgrade required. Please do "REPAIR TABLE `%s`" or dump/reload to fix it! + public final static int ER_SP_NO_AGGREGATE = 1460; //SQLSTATE: 42000 Message: AGGREGATE is not supported for stored functions + public final static int ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461; //SQLSTATE: 42000 Message: Can't create more than max_prepared_stmt_count statements (current value: %lu) + public final static int ER_VIEW_RECURSIVE = 1462; //SQLSTATE: HY000 Message: `%s`.`%s` contains view recursion + public final static int ER_NON_GROUPING_FIELD_USED = 1463; //SQLSTATE: 42000 Message: non-grouping field '%s' is used in %s clause + public final static int ER_TABLE_CANT_HANDLE_SPKEYS = 1464; //SQLSTATE: HY000 Message: The used table type doesn't support SPATIAL indexes + public final static int ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465; //SQLSTATE: HY000 Message: Triggers can not be created on system tables + public final static int ER_REMOVED_SPACES = 1466; //SQLSTATE: HY000 Message: Leading spaces are removed from name '%s' + public final static int ER_AUTOINC_READ_FAILED = 1467; //SQLSTATE: HY000 Message: Failed to read auto-increment value from storage engine + public final static int ER_USERNAME = 1468; //SQLSTATE: HY000 Message: user name + public final static int ER_HOSTNAME = 1469; //SQLSTATE: HY000 Message: host name + public final static int ER_WRONG_STRING_LENGTH = 1470; //SQLSTATE: HY000 Message: String '%s' is too long for %s (should be no longer than %d) + public final static int ER_NON_INSERTABLE_TABLE = 1471; //SQLSTATE: HY000 Message: The target table %s of the %s is not insertable-into + public final static int ER_ADMIN_WRONG_MRG_TABLE = 1472; //SQLSTATE: HY000 Message: Table '%s' is differently defined or of non-MyISAM type or doesn't exist + public final static int ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473; //SQLSTATE: HY000 Message: Too high level of nesting for select + public final static int ER_NAME_BECOMES_EMPTY = 1474; //SQLSTATE: HY000 Message: Name '%s' has become '' + public final static int ER_AMBIGUOUS_FIELD_TERM = 1475; //SQLSTATE: HY000 Message: First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY + public final static int ER_FOREIGN_SERVER_EXISTS = 1476; //SQLSTATE: HY000 Message: The foreign server, %s, you are trying to create already exists. + public final static int ER_FOREIGN_SERVER_DOESNT_EXIST = 1477; //SQLSTATE: HY000 Message: The foreign server name you are trying to reference does not exist. Data source error: %s + public final static int ER_ILLEGAL_HA_CREATE_OPTION = 1478; //SQLSTATE: HY000 Message: Table storage engine '%s' does not support the create option '%s' + public final static int ER_PARTITION_REQUIRES_VALUES_ERROR = 1479; //SQLSTATE: HY000 Message: Syntax error: %s PARTITIONING requires definition of VALUES %s for each partition + public final static int ER_PARTITION_WRONG_VALUES_ERROR = 1480; //SQLSTATE: HY000 Message: Only %s PARTITIONING can use VALUES %s in partition definition + public final static int ER_PARTITION_MAXVALUE_ERROR = 1481; //SQLSTATE: HY000 Message: MAXVALUE can only be used in last partition definition + public final static int ER_PARTITION_SUBPARTITION_ERROR = 1482; //SQLSTATE: HY000 Message: Subpartitions can only be hash partitions and by key + public final static int ER_PARTITION_SUBPART_MIX_ERROR = 1483; //SQLSTATE: HY000 Message: Must define subpartitions on all partitions if on one partition + public final static int ER_PARTITION_WRONG_NO_PART_ERROR = 1484; //SQLSTATE: HY000 Message: Wrong number of partitions defined, mismatch with previous setting + public final static int ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485; //SQLSTATE: HY000 Message: Wrong number of subpartitions defined, mismatch with previous setting + public final static int ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486; //SQLSTATE: HY000 Message: Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed + public final static int ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1487; //SQLSTATE: HY000 Message: Expression in RANGE/LIST VALUES must be constant + public final static int ER_FIELD_NOT_FOUND_PART_ERROR = 1488; //SQLSTATE: HY000 Message: Field in list of fields for partition function not found in table + public final static int ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489; //SQLSTATE: HY000 Message: List of fields is only allowed in KEY partitions + public final static int ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490; //SQLSTATE: HY000 Message: The partition info in the frm file is not consistent with what can be written into the frm file + public final static int ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491; //SQLSTATE: HY000 Message: The %s function returns the wrong type + public final static int ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492; //SQLSTATE: HY000 Message: For %s partitions each partition must be defined + public final static int ER_RANGE_NOT_INCREASING_ERROR = 1493; //SQLSTATE: HY000 Message: VALUES LESS THAN value must be strictly increasing for each partition + public final static int ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494; //SQLSTATE: HY000 Message: VALUES value must be of same type as partition function + public final static int ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495; //SQLSTATE: HY000 Message: Multiple definition of same constant in list partitioning + public final static int ER_PARTITION_ENTRY_ERROR = 1496; //SQLSTATE: HY000 Message: Partitioning can not be used stand-alone in query + public final static int ER_MIX_HANDLER_ERROR = 1497; //SQLSTATE: HY000 Message: The mix of handlers in the partitions is not allowed in this version of MySQL + public final static int ER_PARTITION_NOT_DEFINED_ERROR = 1498; //SQLSTATE: HY000 Message: For the partitioned engine it is necessary to define all %s + public final static int ER_TOO_MANY_PARTITIONS_ERROR = 1499; //SQLSTATE: HY000 Message: Too many partitions (including subpartitions) were defined + public final static int ER_SUBPARTITION_ERROR = 1500; //SQLSTATE: HY000 Message: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning + public final static int ER_CANT_CREATE_HANDLER_FILE = 1501; //SQLSTATE: HY000 Message: Failed to create specific handler file + public final static int ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502; //SQLSTATE: HY000 Message: A BLOB field is not allowed in partition function + public final static int ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503; //SQLSTATE: HY000 Message: A %s must include all columns in the table's partitioning function + public final static int ER_NO_PARTS_ERROR = 1504; //SQLSTATE: HY000 Message: Number of %s = 0 is not an allowed value + public final static int ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505; //SQLSTATE: HY000 Message: Partition management on a not partitioned table is not possible + public final static int ER_FOREIGN_KEY_ON_PARTITIONED = 1506; //SQLSTATE: HY000 Message: Foreign key clause is not yet supported in conjunction with partitioning + public final static int ER_DROP_PARTITION_NON_EXISTENT = 1507; //SQLSTATE: HY000 Message: Error in list of partitions to %s + public final static int ER_DROP_LAST_PARTITION = 1508; //SQLSTATE: HY000 Message: Cannot remove all partitions, use DROP TABLE instead + public final static int ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509; //SQLSTATE: HY000 Message: COALESCE PARTITION can only be used on HASH/KEY partitions + public final static int ER_REORG_HASH_ONLY_ON_SAME_NO = 1510; //SQLSTATE: HY000 Message: REORGANIZE PARTITION can only be used to reorganize partitions not to change their numbers + public final static int ER_REORG_NO_PARAM_ERROR = 1511; //SQLSTATE: HY000 Message: REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH PARTITIONs + public final static int ER_ONLY_ON_RANGE_LIST_PARTITION = 1512; //SQLSTATE: HY000 Message: %s PARTITION can only be used on RANGE/LIST partitions + public final static int ER_ADD_PARTITION_SUBPART_ERROR = 1513; //SQLSTATE: HY000 Message: Trying to Add partition(s) with wrong number of subpartitions + public final static int ER_ADD_PARTITION_NO_NEW_PARTITION = 1514; //SQLSTATE: HY000 Message: At least one partition must be added + public final static int ER_COALESCE_PARTITION_NO_PARTITION = 1515; //SQLSTATE: HY000 Message: At least one partition must be coalesced + public final static int ER_REORG_PARTITION_NOT_EXIST = 1516; //SQLSTATE: HY000 Message: More partitions to reorganize than there are partitions + public final static int ER_SAME_NAME_PARTITION = 1517; //SQLSTATE: HY000 Message: Duplicate partition name %s + public final static int ER_NO_BINLOG_ERROR = 1518; //SQLSTATE: HY000 Message: It is not allowed to shut off binlog on this command + public final static int ER_CONSECUTIVE_REORG_PARTITIONS = 1519; //SQLSTATE: HY000 Message: When reorganizing a set of partitions they must be in consecutive order + public final static int ER_REORG_OUTSIDE_RANGE = 1520; //SQLSTATE: HY000 Message: Reorganize of range partitions cannot change total ranges except for last partition where it can extend the range + public final static int ER_PARTITION_FUNCTION_FAILURE = 1521; //SQLSTATE: HY000 Message: Partition function not supported in this version for this handler + public final static int ER_PART_STATE_ERROR = 1522; //SQLSTATE: HY000 Message: Partition state cannot be defined from CREATE/ALTER TABLE + public final static int ER_LIMITED_PART_RANGE = 1523; //SQLSTATE: HY000 Message: The %s handler only supports 32 bit integers in VALUES + public final static int ER_PLUGIN_IS_NOT_LOADED = 1524; //SQLSTATE: HY000 Message: Plugin '%s' is not loaded + public final static int ER_WRONG_VALUE = 1525; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' + public final static int ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526; //SQLSTATE: HY000 Message: Table has no partition for value %s + public final static int ER_FILEGROUP_OPTION_ONLY_ONCE = 1527; //SQLSTATE: HY000 Message: It is not allowed to specify %s more than once + public final static int ER_CREATE_FILEGROUP_FAILED = 1528; //SQLSTATE: HY000 Message: Failed to create %s + public final static int ER_DROP_FILEGROUP_FAILED = 1529; //SQLSTATE: HY000 Message: Failed to drop %s + public final static int ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530; //SQLSTATE: HY000 Message: The handler doesn't support autoextend of tablespaces + public final static int ER_WRONG_SIZE_NUMBER = 1531; //SQLSTATE: HY000 Message: A size parameter was incorrectly specified, either number or on the form 10M + public final static int ER_SIZE_OVERFLOW_ERROR = 1532; //SQLSTATE: HY000 Message: The size number was correct but we don't allow the digit part to be more than 2 billion + public final static int ER_ALTER_FILEGROUP_FAILED = 1533; //SQLSTATE: HY000 Message: Failed to alter: %s + public final static int ER_BINLOG_ROW_LOGGING_FAILED = 1534; //SQLSTATE: HY000 Message: Writing one row to the row-based binary log failed + public final static int ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535; //SQLSTATE: HY000 Message: Table definition on master and slave does not match: %s + public final static int ER_BINLOG_ROW_RBR_TO_SBR = 1536; //SQLSTATE: HY000 Message: Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events + public final static int ER_EVENT_ALREADY_EXISTS = 1537; //SQLSTATE: HY000 Message: Event '%s' already exists + public final static int ER_EVENT_STORE_FAILED = 1538; //SQLSTATE: HY000 Message: Failed to store event %s. Error code %d from storage engine. + public final static int ER_EVENT_DOES_NOT_EXIST = 1539; //SQLSTATE: HY000 Message: Unknown event '%s' + public final static int ER_EVENT_CANT_ALTER = 1540; //SQLSTATE: HY000 Message: Failed to alter event '%s' + public final static int ER_EVENT_DROP_FAILED = 1541; //SQLSTATE: HY000 Message: Failed to drop %s + public final static int ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542; //SQLSTATE: HY000 Message: INTERVAL is either not positive or too big + public final static int ER_EVENT_ENDS_BEFORE_STARTS = 1543; //SQLSTATE: HY000 Message: ENDS is either invalid or before STARTS + public final static int ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544; //SQLSTATE: HY000 Message: Event execution time is in the past. Event has been disabled + public final static int ER_EVENT_OPEN_TABLE_FAILED = 1545; //SQLSTATE: HY000 Message: Failed to open mysql.event + public final static int ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546; //SQLSTATE: HY000 Message: No datetime expression provided + public final static int ER_COL_COUNT_DOESNT_MATCH_CORRUPTED = 1547; //SQLSTATE: HY000 Message: Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted + public final static int ER_CANNOT_LOAD_FROM_TABLE = 1548; //SQLSTATE: HY000 Message: Cannot load from mysql.%s. The table is probably corrupted + public final static int ER_EVENT_CANNOT_DELETE = 1549; //SQLSTATE: HY000 Message: Failed to delete the event from mysql.event + public final static int ER_EVENT_COMPILE_ERROR = 1550; //SQLSTATE: HY000 Message: Error during compilation of event's body + public final static int ER_EVENT_SAME_NAME = 1551; //SQLSTATE: HY000 Message: Same old and new event name + public final static int ER_EVENT_DATA_TOO_LONG = 1552; //SQLSTATE: HY000 Message: Data for column '%s' too long + public final static int ER_DROP_INDEX_FK = 1553; //SQLSTATE: HY000 Message: Cannot drop index '%s': needed in a foreign key constraint + public final static int ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554; //SQLSTATE: HY000 Message: The syntax '%s' is deprecated and will be removed in MySQL %s. Please use %s instead + public final static int ER_CANT_WRITE_LOCK_LOG_TABLE = 1555; //SQLSTATE: HY000 Message: You can't write-lock a log table. Only read access is possible + public final static int ER_CANT_LOCK_LOG_TABLE = 1556; //SQLSTATE: HY000 Message: You can't use locks with log tables. + public final static int ER_FOREIGN_DUPLICATE_KEY = 1557; //SQLSTATE: 23000 Message: Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry + public final static int ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558; //SQLSTATE: HY000 Message: Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error. + public final static int ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559; //SQLSTATE: HY000 Message: Cannot switch out of the row-based binary log format when the session has open temporary tables + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560; //SQLSTATE: HY000 Message: Cannot change the binary logging format inside a stored function or trigger + public final static int ER_NDB_CANT_SWITCH_BINLOG_FORMAT = 1561; //SQLSTATE: HY000 Message: The NDB cluster engine does not support changing the binlog format on the fly yet + public final static int ER_PARTITION_NO_TEMPORARY = 1562; //SQLSTATE: HY000 Message: Cannot create temporary table with partitions + public final static int ER_PARTITION_CONST_DOMAIN_ERROR = 1563; //SQLSTATE: HY000 Message: Partition constant is out of partition function domain + public final static int ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564; //SQLSTATE: HY000 Message: This partition function is not allowed + public final static int ER_DDL_LOG_ERROR = 1565; //SQLSTATE: HY000 Message: Error in DDL log + public final static int ER_NULL_IN_VALUES_LESS_THAN = 1566; //SQLSTATE: HY000 Message: Not allowed to use NULL value in VALUES LESS THAN + public final static int ER_WRONG_PARTITION_NAME = 1567; //SQLSTATE: HY000 Message: Incorrect partition name + public final static int ER_CANT_CHANGE_TX_ISOLATION = 1568; //SQLSTATE: 25001 Message: Transaction isolation level can't be changed while a transaction is in progress + public final static int ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569; //SQLSTATE: HY000 Message: ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%s' for key '%s' + public final static int ER_EVENT_MODIFY_QUEUE_ERROR = 1570; //SQLSTATE: HY000 Message: Internal scheduler error %d + public final static int ER_EVENT_SET_VAR_ERROR = 1571; //SQLSTATE: HY000 Message: Error during starting/stopping of the scheduler. Error code %u + public final static int ER_PARTITION_MERGE_ERROR = 1572; //SQLSTATE: HY000 Message: Engine cannot be used in partitioned tables + public final static int ER_CANT_ACTIVATE_LOG = 1573; //SQLSTATE: HY000 Message: Cannot activate '%s' log + public final static int ER_RBR_NOT_AVAILABLE = 1574; //SQLSTATE: HY000 Message: The server was not built with row-based replication + public final static int ER_BASE64_DECODE_ERROR = 1575; //SQLSTATE: HY000 Message: Decoding of base64 string failed + public final static int ER_EVENT_RECURSION_FORBIDDEN = 1576; //SQLSTATE: HY000 Message: Recursion of EVENT DDL statements is forbidden when body is present + public final static int ER_EVENTS_DB_ERROR = 1577; //SQLSTATE: HY000 Message: Cannot proceed because system tables used by Event Scheduler were found damaged at server start + public final static int ER_ONLY_INTEGERS_ALLOWED = 1578; //SQLSTATE: HY000 Message: Only integers allowed as number here + public final static int ER_UNSUPORTED_LOG_ENGINE = 1579; //SQLSTATE: HY000 Message: This storage engine cannot be used for log tables" + public final static int ER_BAD_LOG_STATEMENT = 1580; //SQLSTATE: HY000 Message: You cannot '%s' a log table if logging is enabled + public final static int ER_CANT_RENAME_LOG_TABLE = 1581; //SQLSTATE: HY000 Message: Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s' + public final static int ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582; //SQLSTATE: 42000 Message: Incorrect parameter count in the call to native function '%s' + public final static int ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583; //SQLSTATE: 42000 Message: Incorrect parameters in the call to native function '%s' + public final static int ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584; //SQLSTATE: 42000 Message: Incorrect parameters in the call to stored function '%s' + public final static int ER_NATIVE_FCT_NAME_COLLISION = 1585; //SQLSTATE: HY000 Message: This function '%s' has the same name as a native function + public final static int ER_DUP_ENTRY_WITH_KEY_NAME = 1586; //SQLSTATE: 23000 Message: Duplicate entry '%s' for key '%s' + public final static int ER_BINLOG_PURGE_EMFILE = 1587; //SQLSTATE: HY000 Message: Too many files opened, please execute the command again + public final static int ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588; //SQLSTATE: HY000 Message: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. + public final static int ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589; //SQLSTATE: HY000 Message: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. + public final static int ER_SLAVE_INCIDENT = 1590; //SQLSTATE: HY000 Message: The incident %s occured on the master. Message: %s + public final static int ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591; //SQLSTATE: HY000 Message: Table has no partition for some existing values + public final static int ER_BINLOG_UNSAFE_STATEMENT = 1592; //SQLSTATE: HY000 Message: Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. %s + public final static int ER_SLAVE_FATAL_ERROR = 1593; //SQLSTATE: HY000 Message: Fatal error: %s + public final static int ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594; //SQLSTATE: HY000 Message: Relay log read failure: %s + public final static int ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595; //SQLSTATE: HY000 Message: Relay log write failure: %s + public final static int ER_SLAVE_CREATE_EVENT_FAILURE = 1596; //SQLSTATE: HY000 Message: Failed to create %s + public final static int ER_SLAVE_MASTER_COM_FAILURE = 1597; //SQLSTATE: HY000 Message: Master command %s failed: %s + public final static int ER_BINLOG_LOGGING_IMPOSSIBLE = 1598; //SQLSTATE: HY000 Message: Binary logging not possible. Message: %s + public final static int ER_VIEW_NO_CREATION_CTX = 1599; //SQLSTATE: HY000 Message: View `%s`.`%s` has no creation context + public final static int ER_VIEW_INVALID_CREATION_CTX = 1600; //SQLSTATE: HY000 Message: Creation context of view `%s`.`%s' is invalid + public final static int ER_SR_INVALID_CREATION_CTX = 1601; //SQLSTATE: HY000 Message: Creation context of stored routine `%s`.`%s` is invalid + public final static int ER_TRG_CORRUPTED_FILE = 1602; //SQLSTATE: HY000 Message: Corrupted TRG file for table `%s`.`%s` + public final static int ER_TRG_NO_CREATION_CTX = 1603; //SQLSTATE: HY000 Message: Triggers for table `%s`.`%s` have no creation context + public final static int ER_TRG_INVALID_CREATION_CTX = 1604; //SQLSTATE: HY000 Message: Trigger creation context of table `%s`.`%s` is invalid + public final static int ER_EVENT_INVALID_CREATION_CTX = 1605; //SQLSTATE: HY000 Message: Creation context of event `%s`.`%s` is invalid + public final static int ER_TRG_CANT_OPEN_TABLE = 1606; //SQLSTATE: HY000 Message: Cannot open table for trigger `%s`.`%s` + public final static int ER_CANT_CREATE_SROUTINE = 1607; //SQLSTATE: HY000 Message: Cannot create stored routine `%s`. Check warnings + public final static int ER_NEVER_USED = 1608; //SQLSTATE: HY000 Message: Ambiguous slave modes combination. %s + public final static int ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609; //SQLSTATE: HY000 Message: The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement. + public final static int ER_SLAVE_CORRUPT_EVENT = 1610; //SQLSTATE: HY000 Message: Corrupted replication event was detected + public final static int ER_LOAD_DATA_INVALID_COLUMN = 1611; //SQLSTATE: HY000 Message: Invalid column reference (%s) in LOAD DATA + public final static int ER_LOG_PURGE_NO_FILE = 1612; //SQLSTATE: HY000 Message: Being purged log %s was not found + public final static int ER_XA_RBTIMEOUT = 1613; //SQLSTATE: XA106 Message: XA_RBTIMEOUT: Transaction branch was rolled back: took too long + public final static int ER_XA_RBDEADLOCK = 1614; //SQLSTATE: XA102 Message: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected + public final static int ER_NEED_REPREPARE = 1615; //SQLSTATE: HY000 Message: Prepared statement needs to be re-prepared + public final static int ER_DELAYED_NOT_SUPPORTED = 1616; //SQLSTATE: HY000 Message: DELAYED option not supported for table '%s' + public final static int WARN_NO_MASTER_INFO = 1617; //SQLSTATE: HY000 Message: The master info structure does not exist + public final static int WARN_OPTION_IGNORED = 1618; //SQLSTATE: HY000 Message: <%s> option ignored + public final static int WARN_PLUGIN_DELETE_BUILTIN = 1619; //SQLSTATE: HY000 Message: Built-in plugins cannot be deleted + public final static int WARN_PLUGIN_BUSY = 1620; //SQLSTATE: HY000 Message: Plugin is busy and will be uninstalled on shutdown + public final static int ER_VARIABLE_IS_READONLY = 1621; //SQLSTATE: HY000 Message: %s variable '%s' is read-only. Use SET %s to assign the value + public final static int ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622; //SQLSTATE: HY000 Message: Storage engine %s does not support rollback for this statement. Transaction rolled back and must be restarted + public final static int ER_SLAVE_HEARTBEAT_FAILURE = 1623; //SQLSTATE: HY000 Message: Unexpected master's heartbeat data: %s + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624; //SQLSTATE: HY000 Message: The requested value for the heartbeat period is either negative or exceeds the maximum allowed (%s seconds). + public final static int ER_NDB_REPLICATION_SCHEMA_ERROR = 1625; //SQLSTATE: HY000 Message: Bad schema for mysql.ndb_replication table. Message: %s + public final static int ER_CONFLICT_FN_PARSE_ERROR = 1626; //SQLSTATE: HY000 Message: Error in parsing conflict function. Message: %s + public final static int ER_EXCEPTIONS_WRITE_ERROR = 1627; //SQLSTATE: HY000 Message: Write to exceptions table failed. Message: %s" + public final static int ER_TOO_LONG_TABLE_COMMENT = 1628; //SQLSTATE: HY000 Message: Comment for table '%s' is too long (max = %lu) + public final static int ER_TOO_LONG_FIELD_COMMENT = 1629; //SQLSTATE: HY000 Message: Comment for field '%s' is too long (max = %lu) + public final static int ER_FUNC_INEXISTENT_NAME_COLLISION = 1630; //SQLSTATE: 42000 Message: FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual + public final static int ER_DATABASE_NAME = 1631; //SQLSTATE: HY000 Message: Database + public final static int ER_TABLE_NAME = 1632; //SQLSTATE: HY000 Message: Table + public final static int ER_PARTITION_NAME = 1633; //SQLSTATE: HY000 Message: Partition + public final static int ER_SUBPARTITION_NAME = 1634; //SQLSTATE: HY000 Message: Subpartition + public final static int ER_TEMPORARY_NAME = 1635; //SQLSTATE: HY000 Message: Temporary + public final static int ER_RENAMED_NAME = 1636; //SQLSTATE: HY000 Message: Renamed + public final static int ER_TOO_MANY_CONCURRENT_TRXS = 1637; //SQLSTATE: HY000 Message: Too many active concurrent transactions + public final static int WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638; //SQLSTATE: HY000 Message: Non-ASCII separator arguments are not fully supported + public final static int ER_DEBUG_SYNC_TIMEOUT = 1639; //SQLSTATE: HY000 Message: debug sync point wait timed out + public final static int ER_DEBUG_SYNC_HIT_LIMIT = 1640; //SQLSTATE: HY000 Message: debug sync point hit limit reached + public final static int ER_DUP_SIGNAL_SET = 1641; //SQLSTATE: 42000 Message: Duplicate condition information item '%s' + public final static int ER_SIGNAL_WARN = 1642; //SQLSTATE: 01000 Message: Unhandled user-defined warning condition + public final static int ER_SIGNAL_NOT_FOUND = 1643; //SQLSTATE: 02000 Message: Unhandled user-defined not found condition + public final static int ER_SIGNAL_EXCEPTION = 1644; //SQLSTATE: HY000 Message: Unhandled user-defined exception condition + public final static int ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645; //SQLSTATE: 0K000 Message: RESIGNAL when handler not active + public final static int ER_SIGNAL_BAD_CONDITION_TYPE = 1646; //SQLSTATE: HY000 Message: SIGNAL/RESIGNAL can only use a CONDITION defined with; //SQLSTATE + public final static int WARN_COND_ITEM_TRUNCATED = 1647; //SQLSTATE: HY000 Message: Data truncated for condition item '%s' + public final static int ER_COND_ITEM_TOO_LONG = 1648; //SQLSTATE: HY000 Message: Data too long for condition item '%s' + public final static int ER_UNKNOWN_LOCALE = 1649; //SQLSTATE: HY000 Message: Unknown locale: '%s' + public final static int ER_SLAVE_IGNORE_SERVER_IDS = 1650; //SQLSTATE: HY000 Message: The requested server id %d clashes with the slave startup option --replicate-same-server-id + public final static int ER_QUERY_CACHE_DISABLED = 1651; //SQLSTATE: HY000 Message: Query cache is disabled; restart the server with query_cache_type=1 to enable it + public final static int ER_SAME_NAME_PARTITION_FIELD = 1652; //SQLSTATE: HY000 Message: Duplicate partition field name '%s' + public final static int ER_PARTITION_COLUMN_LIST_ERROR = 1653; //SQLSTATE: HY000 Message: Inconsistency in usage of column lists for partitioning + public final static int ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654; //SQLSTATE: HY000 Message: Partition column values of incorrect type + public final static int ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655; //SQLSTATE: HY000 Message: Too many fields in '%s' + public final static int ER_MAXVALUE_IN_VALUES_IN = 1656; //SQLSTATE: HY000 Message: Cannot use MAXVALUE as value in VALUES IN + public final static int ER_TOO_MANY_VALUES_ERROR = 1657; //SQLSTATE: HY000 Message: Cannot have more than one value for this type of %s partitioning + public final static int ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658; //SQLSTATE: HY000 Message: Row expressions in VALUES IN only allowed for multi-field column partitioning + public final static int ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659; //SQLSTATE: HY000 Message: Field '%s' is of a not allowed type for this type of partitioning + public final static int ER_PARTITION_FIELDS_TOO_LONG = 1660; //SQLSTATE: HY000 Message: The total length of the partitioning fields is too large + public final static int ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since both row-incapable engines and statement-incapable engines are involved. + public final static int ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging. + public final static int ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is unsafe, storage engine is limited to statement-based logging, and BINLOG_FORMAT = MIXED. %s + public final static int ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is in row format and at least one table uses a storage engine limited to statement-based logging. + public final static int ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging.%s + public final static int ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT. + public final static int ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging. + public final static int ER_BINLOG_UNSAFE_LIMIT = 1668; //SQLSTATE: HY000 Message: The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. + public final static int ER_BINLOG_UNSAFE_INSERT_DELAYED = 1669; //SQLSTATE: HY000 Message: The statement is unsafe because it uses INSERT DELAYED. This is unsafe because the times when rows are inserted cannot be predicted. + public final static int ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670; //SQLSTATE: HY000 Message: The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves. + public final static int ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671; //SQLSTATE: HY000 Message: Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly. + public final static int ER_BINLOG_UNSAFE_UDF = 1672; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a UDF which may not return the same value on the slave. + public final static int ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a system variable that may have a different value on the slave. + public final static int ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a system function that may return a different value on the slave. + public final static int ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675; //SQLSTATE: HY000 Message: Statement is unsafe because it accesses a non-transactional table after accessing a transactional table within the same transaction. + public final static int ER_MESSAGE_AND_STATEMENT = 1676; //SQLSTATE: HY000 Message: %s Statement: %s + public final static int ER_SLAVE_CONVERSION_FAILED = 1677; //SQLSTATE: HY000 Message: Column %d of table '%s.%s' cannot be converted from type '%s' to type '%s' + public final static int ER_SLAVE_CANT_CREATE_CONVERSION = 1678; //SQLSTATE: HY000 Message: Can't create conversion table for table '%s.%s' + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679; //SQLSTATE: HY000 Message: Cannot modify @@session.binlog_format inside a transaction + public final static int ER_PATH_LENGTH = 1680; //SQLSTATE: HY000 Message: The path specified for %s is too long. + public final static int ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681; //SQLSTATE: HY000 Message: The syntax '%s' is deprecated and will be removed in MySQL %s. + public final static int ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682; //SQLSTATE: HY000 Message: Native table '%s'.'%s' has the wrong structure + public final static int ER_WRONG_PERFSCHEMA_USAGE = 1683; //SQLSTATE: HY000 Message: Invalid performance_schema usage. + public final static int ER_WARN_I_S_SKIPPED_TABLE = 1684; //SQLSTATE: HY000 Message: Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685; //SQLSTATE: HY000 Message: Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686; //SQLSTATE: HY000 Message: Cannot change the binlog direct flag inside a stored function or trigger + public final static int ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687; //SQLSTATE: 42000 Message: A SPATIAL index may only contain a geometrical type column + public final static int ER_TOO_LONG_INDEX_COMMENT = 1688; //SQLSTATE: HY000 Message: Comment for index '%s' is too long (max = %lu) + public final static int ER_LOCK_ABORTED = 1689; //SQLSTATE: HY000 Message: Wait on a lock was aborted due to a pending exclusive lock + public final static int ER_DATA_OUT_OF_RANGE = 1690; //SQLSTATE: 22003 Message: %s value is out of range in '%s' + public final static int ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691; //SQLSTATE: HY000 Message: A variable of a non-integer type in LIMIT clause + public final static int ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692; //SQLSTATE: HY000 Message: Mixing self-logging and non-self-logging engines in a statement is unsafe. + public final static int ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693; //SQLSTATE: HY000 Message: Statement accesses nontransactional table as well as transactional or temporary table, and writes to any of them. + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694; //SQLSTATE: HY000 Message: Cannot modify @@session.sql_log_bin inside a transaction + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695; //SQLSTATE: HY000 Message: Cannot change the sql_log_bin inside a stored function or trigger + public final static int ER_FAILED_READ_FROM_PAR_FILE = 1696; //SQLSTATE: HY000 Message: Failed to read from the .par file + public final static int ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697; //SQLSTATE: HY000 Message: VALUES value for partition '%s' must have type INT + public final static int ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698; //SQLSTATE: 28000 Message: Access denied for user '%s'@'%s' + public final static int ER_SET_PASSWORD_AUTH_PLUGIN = 1699; //SQLSTATE: HY000 Message: SET PASSWORD has no significance for users authenticating via plugins + public final static int ER_GRANT_PLUGIN_USER_EXISTS = 1700; //SQLSTATE: HY000 Message: GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists + public final static int ER_TRUNCATE_ILLEGAL_FK = 1701; //SQLSTATE: 42000 Message: Cannot truncate a table referenced in a foreign key constraint (%s) + public final static int ER_PLUGIN_IS_PERMANENT = 1702; //SQLSTATE: HY000 Message: Plugin '%s' is force_plus_permanent and can not be unloaded + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703; //SQLSTATE: HY000 Message: The requested value for the heartbeat period is less than 1 millisecond. The value is reset to 0, meaning that heartbeating will effectively be disabled. + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704; //SQLSTATE: HY000 Message: The requested value for the heartbeat period exceeds the value of `slave_net_timeout' seconds. A sensible value for the period should be less than the timeout. + public final static int ER_STMT_CACHE_FULL = 1705; //SQLSTATE: HY000 Message: Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again + + public final static int ER_MULTI_UPDATE_KEY_CONFLICT = 1706; //SQLSTATE: HY000 Message: Primary key/partition key update is not allowed since the table is updated both as '%s' and '%s'. + public final static int ER_TABLE_NEEDS_REBUILD = 1707; //SQLSTATE: HY000 Message: Table rebuild required. Please do "ALTER TABLE `%s` FORCE" or dump/reload to fix it! + public final static int WARN_OPTION_BELOW_LIMIT = 1708; //SQLSTATE: HY000 Message: The value of '%s' should be no less than the value of '%s' + public final static int ER_INDEX_COLUMN_TOO_LONG = 1709; //SQLSTATE: HY000 Message: Index column size too large. The maximum column size is %lu bytes. + public final static int ER_ERROR_IN_TRIGGER_BODY = 1710; //SQLSTATE: HY000 Message: Trigger '%s' has an error in its body: '%s' + public final static int ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711; //SQLSTATE: HY000 Message: Unknown trigger has an error in its body: '%s' + public final static int ER_INDEX_CORRUPT = 1712; //SQLSTATE: HY000 Message: Index %s is corrupted + public final static int ER_UNDO_RECORD_TOO_BIG = 1713; //SQLSTATE: HY000 Message: Undo log record is too big. + public final static int ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714; //SQLSTATE: HY000 Message: INSERT IGNORE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715; //SQLSTATE: HY000 Message: INSERT... SELECT... ON DUPLICATE KEY UPDATE is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are updated. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716; //SQLSTATE: HY000 Message: REPLACE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717; //SQLSTATE: HY000 Message: CREATE... IGNORE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718; //SQLSTATE: HY000 Message: CREATE... REPLACE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719; //SQLSTATE: HY000 Message: UPDATE IGNORE is unsafe because the order in which rows are updated determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_PLUGIN_NO_UNINSTALL = 1720; //SQLSTATE: HY000 Message: Plugin '%s' is marked as not dynamically uninstallable. You have to stop the server to uninstall it. + public final static int ER_PLUGIN_NO_INSTALL = 1721; //SQLSTATE: HY000 Message: Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it. + public final static int ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722; //SQLSTATE: HY000 Message: Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723; //SQLSTATE: HY000 Message: CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724; //SQLSTATE: HY000 Message: INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe + public final static int ER_TABLE_IN_FK_CHECK = 1725; //SQLSTATE: HY000 Message: Table is being used in foreign key check. + public final static int ER_UNSUPPORTED_ENGINE = 1726; //SQLSTATE: HY000 Message: Storage engine '%s' does not support system tables. [%s.%s] + public final static int ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727; //SQLSTATE: HY000 Message: INSERT into autoincrement field which is not the first part in the composed primary key is unsafe. + public final static int ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728; //SQLSTATE: HY000 Message: Cannot load from %s.%s. The table is probably corrupted + public final static int ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729; //SQLSTATE: HY000 Message: The requested value %u for the master delay exceeds the maximum %u + public final static int ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730; //SQLSTATE: HY000 Message: Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided) + public final static int ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731; //SQLSTATE: HY000 Message: Non matching attribute '%s' between partition and table + public final static int ER_PARTITION_EXCHANGE_PART_TABLE = 1732; //SQLSTATE: HY000 Message: Table to exchange with partition is partitioned: '%s' + public final static int ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733; //SQLSTATE: HY000 Message: Table to exchange with partition is temporary: '%s' + public final static int ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734; //SQLSTATE: HY000 Message: Subpartitioned table, use subpartition instead of partition + public final static int ER_UNKNOWN_PARTITION = 1735; //SQLSTATE: HY000 Message: Unknown partition '%s' in table '%s' + public final static int ER_TABLES_DIFFERENT_METADATA = 1736; //SQLSTATE: HY000 Message: Tables have different definitions + public final static int ER_ROW_DOES_NOT_MATCH_PARTITION = 1737; //SQLSTATE: HY000 Message: Found a row that does not match the partition + public final static int ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738; //SQLSTATE: HY000 Message: Option binlog_cache_size (%lu) is greater than max_binlog_cache_size (%lu); setting binlog_cache_size equal to max_binlog_cache_size. + public final static int ER_WARN_INDEX_NOT_APPLICABLE = 1739; //SQLSTATE: HY000 Message: Cannot use %s access on index '%s' due to type or collation conversion on field '%s' + public final static int ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740; //SQLSTATE: HY000 Message: Table to exchange with partition has foreign key references: '%s' + public final static int ER_NO_SUCH_KEY_VALUE = 1741; //SQLSTATE: HY000 Message: Key value '%s' was not found in table '%s.%s' + public final static int ER_RPL_INFO_DATA_TOO_LONG = 1742; //SQLSTATE: HY000 Message: Data for column '%s' too long + public final static int ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743; //SQLSTATE: HY000 Message: Replication event checksum verification failed while reading from network. + public final static int ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744; //SQLSTATE: HY000 Message: Replication event checksum verification failed while reading from a log file. + public final static int ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745; //SQLSTATE: HY000 Message: Option binlog_stmt_cache_size (%lu) is greater than max_binlog_stmt_cache_size (%lu); setting binlog_stmt_cache_size equal to max_binlog_stmt_cache_size. + public final static int ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746; //SQLSTATE: HY000 Message: Can't update table '%s' while '%s' is being created. + public final static int ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747; //SQLSTATE: HY000 Message: PARTITION () clause on non partitioned table + public final static int ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748; //SQLSTATE: HY000 Message: Found a row not matching the given partition set + public final static int ER_NO_SUCH_PARTITION__UNUSED = 1749; //SQLSTATE: HY000 Message: partition '%s' doesn't exist + public final static int ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750; //SQLSTATE: HY000 Message: Failure while changing the type of replication repository: %s. + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751; //SQLSTATE: HY000 Message: The creation of some temporary tables could not be rolled back. + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752; //SQLSTATE: HY000 Message: Some temporary tables were dropped, but these operations could not be rolled back. + public final static int ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753; //SQLSTATE: HY000 Message: %s is not supported in multi-threaded slave mode. %s + public final static int ER_MTS_UPDATED_DBS_GREATER_MAX = 1754; //SQLSTATE: HY000 Message: The number of modified databases exceeds the maximum %d; the database names will not be included in the replication event metadata. + public final static int ER_MTS_CANT_PARALLEL = 1755; //SQLSTATE: HY000 Message: Cannot execute the current event group in the parallel mode. Encountered event %s, relay-log name %s, position %s which prevents execution of this event group in parallel mode. Reason: %s. + public final static int ER_MTS_INCONSISTENT_DATA = 1756; //SQLSTATE: HY000 Message: %s + public final static int ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757; //SQLSTATE: HY000 Message: FULLTEXT index is not supported for partitioned tables. + public final static int ER_DA_INVALID_CONDITION_NUMBER = 1758; //SQLSTATE: 35000 Message: Invalid condition number + public final static int ER_INSECURE_PLAIN_TEXT = 1759; //SQLSTATE: HY000 Message: Sending passwords in plain text without SSL/TLS is extremely insecure. + public final static int ER_INSECURE_CHANGE_MASTER = 1760; //SQLSTATE: HY000 Message: Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives. + public final static int ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761; //SQLSTATE: 23000 Message: Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s' + public final static int ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762; //SQLSTATE: 23000 Message: Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table + public final static int ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763; //SQLSTATE: HY000 Message: Setting authentication options is not possible when only the Slave SQL Thread is being started. + public final static int ER_TABLE_HAS_NO_FT = 1764; //SQLSTATE: HY000 Message: The table does not have FULLTEXT index to support this query + public final static int ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765; //SQLSTATE: HY000 Message: The system variable %s cannot be set in stored functions or triggers. + public final static int ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766; //SQLSTATE: HY000 Message: The system variable %s cannot be set when there is an ongoing transaction. + public final static int ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767; //SQLSTATE: HY000 Message: The system variable @@SESSION.GTID_NEXT has the value %s, which is not listed in @@SESSION.GTID_NEXT_LIST. + public final static int ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL = 1768; //SQLSTATE: HY000 Message: When @@SESSION.GTID_NEXT_LIST == NULL, the system variable @@SESSION.GTID_NEXT cannot change inside a transaction. + public final static int ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769; //SQLSTATE: HY000 Message: The statement 'SET %s' cannot invoke a stored function. + public final static int ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770; //SQLSTATE: HY000 Message: The system variable @@SESSION.GTID_NEXT cannot be 'AUTOMATIC' when @@SESSION.GTID_NEXT_LIST is non-NULL. + public final static int ER_SKIPPING_LOGGED_TRANSACTION = 1771; //SQLSTATE: HY000 Message: Skipping transaction %s because it has already been executed and logged. + public final static int ER_MALFORMED_GTID_SET_SPECIFICATION = 1772; //SQLSTATE: HY000 Message: Malformed GTID set specification '%s'. + public final static int ER_MALFORMED_GTID_SET_ENCODING = 1773; //SQLSTATE: HY000 Message: Malformed GTID set encoding. + public final static int ER_MALFORMED_GTID_SPECIFICATION = 1774; //SQLSTATE: HY000 Message: Malformed GTID specification '%s'. + public final static int ER_GNO_EXHAUSTED = 1775; //SQLSTATE: HY000 Message: Impossible to generate Global Transaction Identifier: the integer component reached the maximal value. Restart the server with a new server_uuid. + public final static int ER_BAD_SLAVE_AUTO_POSITION = 1776; //SQLSTATE: HY000 Message: Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active. + public final static int ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON = 1777; //SQLSTATE: HY000 Message: CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when GTID_MODE = ON. + public final static int ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778; //SQLSTATE: HY000 Message: Cannot execute statements with implicit commit inside a transaction when GTID_NEXT != AUTOMATIC or GTID_NEXT_LIST != NULL. + public final static int ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779; //SQLSTATE: HY000 Message: GTID_MODE = ON or GTID_MODE = UPGRADE_STEP_2 requires ENFORCE_GTID_CONSISTENCY = 1. + public final static int ER_GTID_MODE_REQUIRES_BINLOG = 1780; //SQLSTATE: HY000 Message: GTID_MODE = ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates. + public final static int ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781; //SQLSTATE: HY000 Message: GTID_NEXT cannot be set to UUID:NUMBER when GTID_MODE = OFF. + public final static int ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782; //SQLSTATE: HY000 Message: GTID_NEXT cannot be set to ANONYMOUS when GTID_MODE = ON. + public final static int ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783; //SQLSTATE: HY000 Message: GTID_NEXT_LIST cannot be set to a non-NULL value when GTID_MODE = OFF. + public final static int ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF = 1784; //SQLSTATE: HY000 Message: Found a Gtid_log_event or Previous_gtids_log_event when GTID_MODE = OFF. + public final static int ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785; //SQLSTATE: HY000 Message: When ENFORCE_GTID_CONSISTENCY = 1, updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables. + public final static int ER_GTID_UNSAFE_CREATE_SELECT = 1786; //SQLSTATE: HY000 Message: CREATE TABLE ... SELECT is forbidden when ENFORCE_GTID_CONSISTENCY = 1. + public final static int ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787; //SQLSTATE: HY000 Message: When ENFORCE_GTID_CONSISTENCY = 1, the statements CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can be executed in a non-transactional context only, and require that AUTOCOMMIT = 1. + public final static int ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788; //SQLSTATE: HY000 Message: The value of GTID_MODE can only change one step at a time: OFF <-> UPGRADE_STEP_1 <-> UPGRADE_STEP_2 <-> ON. Also note that this value must be stepped up or down simultaneously on all servers; see the Manual for instructions. + public final static int ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789; //SQLSTATE: HY000 Message: The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires. + public final static int ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790; //SQLSTATE: HY000 Message: GTID_NEXT cannot be changed by a client that owns a GTID. The client owns %s. Ownership is released on COMMIT or ROLLBACK. + public final static int ER_UNKNOWN_EXPLAIN_FORMAT = 1791; //SQLSTATE: HY000 Message: Unknown EXPLAIN format name: '%s' + public final static int ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792; //SQLSTATE: 25006 Message: Cannot execute statement in a READ ONLY transaction. + public final static int ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793; //SQLSTATE: HY000 Message: Comment for table partition '%s' is too long (max = %lu) + public final static int ER_SLAVE_CONFIGURATION = 1794; //SQLSTATE: HY000 Message: Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log. + public final static int ER_INNODB_FT_LIMIT = 1795; //SQLSTATE: HY000 Message: InnoDB presently supports one FULLTEXT index creation at a time + public final static int ER_INNODB_NO_FT_TEMP_TABLE = 1796; //SQLSTATE: HY000 Message: Cannot create FULLTEXT index on temporary InnoDB table + public final static int ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797; //SQLSTATE: HY000 Message: Column '%s' is of wrong type for an InnoDB FULLTEXT index + public final static int ER_INNODB_FT_WRONG_DOCID_INDEX = 1798; //SQLSTATE: HY000 Message: Index '%s' is of wrong type for an InnoDB FULLTEXT index + public final static int ER_INNODB_ONLINE_LOG_TOO_BIG = 1799; //SQLSTATE: HY000 Message: Creating index '%s' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again. + public final static int ER_UNKNOWN_ALTER_ALGORITHM = 1800; //SQLSTATE: HY000 Message: Unknown ALGORITHM '%s' + public final static int ER_UNKNOWN_ALTER_LOCK = 1801; //SQLSTATE: HY000 Message: Unknown LOCK type '%s' + public final static int ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802; //SQLSTATE: HY000 Message: CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL. + public final static int ER_MTS_RECOVERY_FAILURE = 1803; //SQLSTATE: HY000 Message: Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MySQL error log. + public final static int ER_MTS_RESET_WORKERS = 1804; //SQLSTATE: HY000 Message: Cannot clean up worker info tables. Additional error messages can be found in the MySQL error log. + public final static int ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805; //SQLSTATE: HY000 Message: Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted + public final static int ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806; //SQLSTATE: HY000 Message: Slave must silently retry current transaction + public final static int ER_DISCARD_FK_CHECKS_RUNNING = 1807; //SQLSTATE: HY000 Message: There is a foreign key check running on table '%s'. Cannot discard the table. + public final static int ER_TABLE_SCHEMA_MISMATCH = 1808; //SQLSTATE: HY000 Message: Schema mismatch (%s) + public final static int ER_TABLE_IN_SYSTEM_TABLESPACE = 1809; //SQLSTATE: HY000 Message: Table '%s' in system tablespace + public final static int ER_IO_READ_ERROR = 1810; //SQLSTATE: HY000 Message: IO Read error: (%lu, %s) %s + public final static int ER_IO_WRITE_ERROR = 1811; //SQLSTATE: HY000 Message: IO Write error: (%lu, %s) %s + public final static int ER_TABLESPACE_MISSING = 1812; //SQLSTATE: HY000 Message: Tablespace is missing for table '%s' + public final static int ER_TABLESPACE_EXISTS = 1813; //SQLSTATE: HY000 Message: Tablespace for table '%s' exists. Please DISCARD the tablespace before IMPORT. + public final static int ER_TABLESPACE_DISCARDED = 1814; //SQLSTATE: HY000 Message: Tablespace has been discarded for table '%s' + public final static int ER_INTERNAL_ERROR = 1815; //SQLSTATE: HY000 Message: Internal error: %s + public final static int ER_INNODB_IMPORT_ERROR = 1816; //SQLSTATE: HY000 Message: ALTER TABLE '%s' IMPORT TABLESPACE failed with error %lu : '%s' + public final static int ER_INNODB_INDEX_CORRUPT = 1817; //SQLSTATE: HY000 Message: Index corrupt: %s + public final static int ER_INVALID_YEAR_COLUMN_LENGTH = 1818; //SQLSTATE: HY000 Message: YEAR(%lu) column type is deprecated. Creating YEAR(4) column instead. + public final static int ER_NOT_VALID_PASSWORD = 1819; //SQLSTATE: HY000 Message: Your password does not satisfy the current policy requirements + public final static int ER_MUST_CHANGE_PASSWORD = 1820; //SQLSTATE: HY000 Message: You must SET PASSWORD before executing this statement + public final static int ER_FK_NO_INDEX_CHILD = 1821; //SQLSTATE: HY000 Message: Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s' + public final static int ER_FK_NO_INDEX_PARENT = 1822; //SQLSTATE: HY000 Message: Failed to add the foreign key constaint. Missing index for constraint '%s' in the referenced table '%s' + public final static int ER_FK_FAIL_ADD_SYSTEM = 1823; //SQLSTATE: HY000 Message: Failed to add the foreign key constraint '%s' to system tables + public final static int ER_FK_CANNOT_OPEN_PARENT = 1824; //SQLSTATE: HY000 Message: Failed to open the referenced table '%s' + public final static int ER_FK_INCORRECT_OPTION = 1825; //SQLSTATE: HY000 Message: Failed to add the foreign key constraint on table '%s'. Incorrect options in FOREIGN KEY constraint '%s' + public final static int ER_FK_DUP_NAME = 1826; //SQLSTATE: HY000 Message: Duplicate foreign key constraint name '%s' + public final static int ER_PASSWORD_FORMAT = 1827; //SQLSTATE: HY000 Message: The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function. + public final static int ER_FK_COLUMN_CANNOT_DROP = 1828; //SQLSTATE: HY000 Message: Cannot drop column '%s': needed in a foreign key constraint '%s' + public final static int ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829; //SQLSTATE: HY000 Message: Cannot drop column '%s': needed in a foreign key constraint '%s' of table '%s' + public final static int ER_FK_COLUMN_NOT_NULL = 1830; //SQLSTATE: HY000 Message: Column '%s' cannot be NOT NULL: needed in a foreign key constraint '%s' SET NULL + public final static int ER_DUP_INDEX = 1831; //SQLSTATE: HY000 Message: Duplicate index '%s' defined on the table '%s.%s'. This is deprecated and will be disallowed in a future release. + public final static int ER_FK_COLUMN_CANNOT_CHANGE = 1832; //SQLSTATE: HY000 Message: Cannot change column '%s': used in a foreign key constraint '%s' + public final static int ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833; //SQLSTATE: HY000 Message: Cannot change column '%s': used in a foreign key constraint '%s' of table '%s' + public final static int ER_FK_CANNOT_DELETE_PARENT = 1834; //SQLSTATE: HY000 Message: Cannot delete rows from table which is parent in a foreign key constraint '%s' of table '%s' + public final static int ER_MALFORMED_PACKET = 1835; //SQLSTATE: HY000 Message: Malformed communication packet. + public final static int ER_READ_ONLY_MODE = 1836; //SQLSTATE: HY000 Message: Running in read-only mode + public final static int ER_GTID_NEXT_TYPE_UNDEFINED_GROUP = 1837; //SQLSTATE: HY000 Message: When GTID_NEXT is set to a GTID, you must explicitly set it again after a COMMIT or ROLLBACK. If you see this error message in the slave SQL thread, it means that a table in the current transaction is transactional on the master and non-transactional on the slave. In a client connection, it means that you executed SET GTID_NEXT before a transaction and forgot to set GTID_NEXT to a different identifier or to 'AUTOMATIC' after COMMIT or ROLLBACK. Current GTID_NEXT is '%s'. + public final static int ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838; //SQLSTATE: HY000 Message: The system variable %s cannot be set in stored procedures. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when GTID_MODE = ON. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when GTID_EXECUTED is empty. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when there are no ongoing transactions (not even in other clients). + public final static int ER_GTID_PURGED_WAS_CHANGED = 1842; //SQLSTATE: HY000 Message: GTID_PURGED was changed from '%s' to '%s'. + public final static int ER_GTID_EXECUTED_WAS_CHANGED = 1843; //SQLSTATE: HY000 Message: GTID_EXECUTED was changed from '%s' to '%s'. + public final static int ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT, and both replicated and non replicated tables are written to. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED = 1845; //SQLSTATE: 0A000 Message: %s is not supported for this operation. Try %s. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846; //SQLSTATE: 0A000 Message: %s is not supported. Reason: %s. Try %s. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847; //SQLSTATE: HY000 Message: COPY algorithm requires a lock + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848; //SQLSTATE: HY000 Message: Partition specific operations do not yet support LOCK/ALGORITHM + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849; //SQLSTATE: HY000 Message: Columns participating in a foreign key are renamed + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850; //SQLSTATE: HY000 Message: Cannot change column type INPLACE + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851; //SQLSTATE: HY000 Message: Adding foreign keys needs foreign_key_checks=OFF + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE = 1852; //SQLSTATE: HY000 Message: Creating unique indexes with IGNORE requires COPY algorithm to remove duplicate rows + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853; //SQLSTATE: HY000 Message: Dropping a primary key is not allowed without also adding a new primary key + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854; //SQLSTATE: HY000 Message: Adding an auto-increment column requires a lock + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855; //SQLSTATE: HY000 Message: Cannot replace hidden FTS_DOC_ID with a user-visible one + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856; //SQLSTATE: HY000 Message: Cannot drop or rename FTS_DOC_ID + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857; //SQLSTATE: HY000 Message: Fulltext index creation requires a lock + public final static int ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858; //SQLSTATE: HY000 Message: sql_slave_skip_counter can not be set when the server is running with GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction + public final static int ER_DUP_UNKNOWN_IN_INDEX = 1859; //SQLSTATE: 23000 Message: Duplicate entry for key '%s' + public final static int ER_IDENT_CAUSES_TOO_LONG_PATH = 1860; //SQLSTATE: HY000 Message: Long database name and identifier for object resulted in path length exceeding %d characters. Path: '%s'. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861; //SQLSTATE: HY000 Message: cannot silently convert NULL values, as required in this SQL_MODE; was introduced in 5.7.1. + public final static int ER_MUST_CHANGE_PASSWORD_LOGIN = 1862; //SQLSTATE: HY000 Message: Your password has expired. To log in you must change it using a client that supports expired passwords. was introduced in 5.7.1. + public final static int ER_ROW_IN_WRONG_PARTITION = 1863; //SQLSTATE: HY000 Message: Found a row in wrong partition %s; was introduced in 5.7.1. + public final static int ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864; //SQLSTATE: HY000 Message: Cannot schedule event %s, relay-log name %s, position %s to Worker thread because its size %lu exceeds %lu of slave_pending_jobs_size_max.; was introduced in 5.7.2. + + public final static int ER_INNODB_NO_FT_USES_PARSER = 1865; //SQLSTATE: HY000 Message: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table; was introduced in 5.7.2. + public final static int ER_BINLOG_LOGICAL_CORRUPTION = 1866; //SQLSTATE: HY000 Message: The binary log file '%s' is logically corrupted: %s; was introduced in 5.7.2. + public final static int ER_WARN_PURGE_LOG_IN_USE = 1867; //SQLSTATE: HY000 Message: file %s was not purged because it was being read by %d thread(s), purged only %d out of %d files. was introduced in 5.7.2. + public final static int ER_WARN_PURGE_LOG_IS_ACTIVE = 1868; //SQLSTATE: HY000 Message: file %s was not purged because it is the active log file.; was introduced in 5.7.2. + public final static int ER_AUTO_INCREMENT_CONFLICT = 1869; //SQLSTATE: HY000 Message: Auto-increment value in UPDATE conflicts with internally generated values; was introduced in 5.7.2. + public final static int WARN_ON_BLOCKHOLE_IN_RBR = 1870; //SQLSTATE: HY000 Message: Row events are not logged for %s statements that modify BLACKHOLE tables in row format. Table(s): '%s'; was introduced in 5.7.2. + public final static int ER_SLAVE_MI_INIT_REPOSITORY = 1871; //SQLSTATE: HY000 Message: Slave failed to initialize master info structure from the repository; was introduced in 5.7.2. + public final static int ER_SLAVE_RLI_INIT_REPOSITORY = 1872; //SQLSTATE: HY000 Message: Slave failed to initialize relay log info structure from the repository; was introduced in 5.7.2. + public final static int ER_ACCESS_DENIED_CHANGE_USER_ERROR = 1873; //SQLSTATE: 28000 Message: Access denied trying to change to user '%s'@'%s' (using password: %s). Disconnecting. was introduced in 5.7.2. + public final static int ER_INNODB_READ_ONLY = 1874; //SQLSTATE: HY000 Message: InnoDB is in read only mode.; was introduced in 5.7.2. + public final static int ER_STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875; //SQLSTATE: HY000 Message: STOP SLAVE command execution is incomplete: Slave SQL thread got the stop signal, thread is busy, SQL thread will stop once the current task is complete.; was introduced in 5.7.2. + public final static int ER_STOP_SLAVE_IO_THREAD_TIMEOUT = 1876; //SQLSTATE: HY000 Message: STOP SLAVE command execution is incomplete: Slave IO thread got the stop signal, thread is busy, IO thread will stop once the current task is complete.; was introduced in 5.7.2. + public final static int ER_TABLE_CORRUPT = 1877; //SQLSTATE: HY000 Message: Operation cannot be performed. The table '%s.%s' is missing, corrupt or contains bad data.; was introduced in 5.7.2. + public final static int ER_TEMP_FILE_WRITE_FAILURE = 1878; //SQLSTATE: HY000 Message: Temporary file write failure.; was introduced in 5.7.3. + public final static int ER_INNODB_FT_AUX_NOT_HEX_ID = 1879; //SQLSTATE: HY000 Message: Upgrade index name failed, please use create index(alter table) algorithm copy to rebuild index.; was introduced in 5.7.4. + public final static int ER_OLD_TEMPORALS_UPGRADED = 1880; //SQLSTATE: HY000 Message: TIME/TIMESTAMP/DATETIME columns of old format have been upgraded to the new format.; was introduced in 5.7.4. + public final static int ER_INNODB_FORCED_RECOVERY = 1881; //SQLSTATE: HY000 Message: Operation not allowed when innodb_forced_recovery > 0.; was introduced in 5.7.4. + public final static int ER_AES_INVALID_IV = 1882; //SQLSTATE: HY000 Message: The initialization vector supplied to %s is too short. Must be at least %d bytes long; was introduced in 5.7.4. + public final static int ER_FILE_CORRUPT = 1883; //SQLSTATE: HY000 Message: File %s is corrupted + public final static int ER_ERROR_ON_MASTER = 1884; //SQLSTATE: HY000 Message: Query partially completed on the master (error on master: %d) and was aborted. There is a chance that your master is inconsistent at this point. If you are sure that your master is ok, run this query manually on the slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;. Query:'%s' + public final static int ER_INCONSISTENT_ERROR = 1885; //SQLSTATE: HY000 Message: Query caused different errors on master and slave. Error on master: message (format)='%s' error code=%d; Error on slave:actual message='%s', error code=%d. Default database:'%s'. Query:'%s' + public final static int ER_STORAGE_ENGINE_NOT_LOADED = 1886; //SQLSTATE: HY000 Message: Storage engine for table '%s'.'%s' is not loaded. + public final static int ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER = 1887; //SQLSTATE: 0Z002 Message: GET STACKED DIAGNOSTICS when handler not active + public final static int ER_WARN_LEGACY_SYNTAX_CONVERTED = 1888; //SQLSTATE: HY000 Message: %s is no longer supported. The statement was converted to %s. + public final static int ER_BINLOG_UNSAFE_FULLTEXT_PLUGIN = 1889; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a fulltext parser plugin which may not return the same value on the slave.; was introduced in 5.7.1. + public final static int ER_CANNOT_DISCARD_TEMPORARY_TABLE = 1890; //SQLSTATE: HY000 Message: Cannot DISCARD/IMPORT tablespace associated with temporary table; was introduced in 5.7.1. + public final static int ER_FK_DEPTH_EXCEEDED = 1891; //SQLSTATE: HY000 Message: Foreign key cascade delete/update exceeds max depth of %d.; was introduced in 5.7.2. + public final static int ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2 = 1892; //SQLSTATE: HY000 Message: Column count of %s.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error.; was introduced in 5.7.2. + public final static int ER_WARN_TRIGGER_DOESNT_HAVE_CREATED = 1893; //SQLSTATE: HY000 Message: Trigger %s.%s.%s does not have CREATED attribute.; was introduced in 5.7.2. + public final static int ER_REFERENCED_TRG_DOES_NOT_EXIST = 1894; //SQLSTATE: HY000 Message: Referenced trigger '%s' for the given action time and event type does not exist.; was introduced in 5.7.2. + public final static int ER_EXPLAIN_NOT_SUPPORTED = 1895; //SQLSTATE: HY000 Message: EXPLAIN FOR CONNECTION command is supported only for SELECT/UPDATE/INSERT/DELETE/REPLACE; was introduced in 5.7.2. + public final static int ER_INVALID_FIELD_SIZE = 1896; //SQLSTATE: HY000 Message: Invalid size for column '%s'.; was introduced in 5.7.2. + public final static int ER_MISSING_HA_CREATE_OPTION = 1897; //SQLSTATE: HY000 Message: Table storage engine '%s' found required create option missing; was introduced in 5.7.2. + public final static int ER_ENGINE_OUT_OF_MEMORY = 1898; //SQLSTATE: HY000 Message: Out of memory in storage engine '%s'.; was introduced in 5.7.3. + public final static int ER_PASSWORD_EXPIRE_ANONYMOUS_USER = 1899; //SQLSTATE: HY000 Message: The password for anonymous user cannot be expired.; was introduced in 5.7.3. + public final static int ER_SLAVE_SQL_THREAD_MUST_STOP = 1900; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave sql thread; run STOP SLAVE SQL_THREAD first; was introduced in 5.7.3. + public final static int ER_NO_FT_MATERIALIZED_SUBQUERY = 1901; //SQLSTATE: HY000 Message: Cannot create FULLTEXT index on materialized subquery; was introduced in 5.7.4. + public final static int ER_INNODB_UNDO_LOG_FULL = 1902; //SQLSTATE: HY000 Message: Undo Log error: %s; was introduced in 5.7.4. + public final static int ER_INVALID_ARGUMENT_FOR_LOGARITHM = 1903; //SQLSTATE: 2201E Message: Invalid argument for logarithm; was introduced in 5.7.4. + public final static int ER_SLAVE_IO_THREAD_MUST_STOP = 1904; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD first.; was introduced in 5.7.4. + public final static int ER_WARN_OPEN_TEMP_TABLES_MUST_BE_ZERO = 1905; //SQLSTATE: HY000 Message: This operation may not be safe when the slave has temporary tables. The tables will be kept open until the server restarts or until the tables are deleted by any replicated DROP statement. Suggest to wait until slave_open_temp_tables = 0.; was introduced in 5.7.4. + public final static int ER_WARN_ONLY_MASTER_LOG_FILE_NO_POS = 1906; //SQLSTATE: HY000 Message: CHANGE MASTER TO with a MASTER_LOG_FILE clause but no MASTER_LOG_POS clause may not be safe. The old position value may not be valid for the new binary log file.; was introduced in 5.7.4. + public final static int ER_QUERY_TIMEOUT = 1907; //SQLSTATE: HY000 Message: Query execution was interrupted, max_statement_time exceeded; was introduced in 5.7.4. + public final static int ER_NON_RO_SELECT_DISABLE_TIMER = 1908; //SQLSTATE: HY000 Message: Select is not a read only statement, disabling timer; was introduced in 5.7.4. + public final static int ER_DUP_LIST_ENTRY = 1909; //SQLSTATE: HY000 Message: Duplicate entry '%s'.; was introduced in 5.7.4. + public final static int ER_SQL_MODE_NO_EFFECT = 1910; //SQLSTATE: HY000 Message: '%s' mode no longer has any effect. Use STRICT_ALL_TABLES or STRICT_TRANS_TABLES instead.; was introduced in 5.7.4. + + // Connector/J-specific errors outside the space of server errors. + public static final int ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION = 1000001; + public static final int ERROR_CODE_REPLICATION_CONNECTION_WITH_NO_HOSTS = 1000002; + + private MysqlErrorNumbers() { + // prevent instantiation + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlIO.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlIO.java new file mode 100644 index 0000000..7878863 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlIO.java @@ -0,0 +1,5043 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.ref.SoftReference; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.security.NoSuchAlgorithmException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.zip.Deflater; + +import com.mysql.jdbc.authentication.MysqlClearPasswordPlugin; +import com.mysql.jdbc.authentication.MysqlNativePasswordPlugin; +import com.mysql.jdbc.authentication.MysqlOldPasswordPlugin; +import com.mysql.jdbc.authentication.Sha256PasswordPlugin; +import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.log.LogUtils; +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.profiler.ProfilerEventHandler; +import com.mysql.jdbc.util.ReadAheadInputStream; +import com.mysql.jdbc.util.ResultSetUtil; + +/** + * This class is used by Connection for communicating with the MySQL server. + */ +public class MysqlIO { + private static final String CODE_PAGE_1252 = "Cp1252"; + protected static final int NULL_LENGTH = ~0; + protected static final int COMP_HEADER_LENGTH = 3; + protected static final int MIN_COMPRESS_LEN = 50; + protected static final int HEADER_LENGTH = 4; + protected static final int AUTH_411_OVERHEAD = 33; + public static final int SEED_LENGTH = 20; + private static int maxBufferSize = 65535; + + private static final String NONE = "none"; + + private static final int CLIENT_LONG_PASSWORD = 0x00000001; /* new more secure passwords */ + private static final int CLIENT_FOUND_ROWS = 0x00000002; + private static final int CLIENT_LONG_FLAG = 0x00000004; /* Get all column flags */ + protected static final int CLIENT_CONNECT_WITH_DB = 0x00000008; + private static final int CLIENT_COMPRESS = 0x00000020; /* Can use compression protcol */ + private static final int CLIENT_LOCAL_FILES = 0x00000080; /* Can use LOAD DATA LOCAL */ + private static final int CLIENT_PROTOCOL_41 = 0x00000200; // for > 4.1.1 + private static final int CLIENT_INTERACTIVE = 0x00000400; + protected static final int CLIENT_SSL = 0x00000800; + private static final int CLIENT_TRANSACTIONS = 0x00002000; // Client knows about transactions + protected static final int CLIENT_RESERVED = 0x00004000; // for 4.1.0 only + protected static final int CLIENT_SECURE_CONNECTION = 0x00008000; + private static final int CLIENT_MULTI_STATEMENTS = 0x00010000; // Enable/disable multiquery support + private static final int CLIENT_MULTI_RESULTS = 0x00020000; // Enable/disable multi-results + private static final int CLIENT_PLUGIN_AUTH = 0x00080000; + private static final int CLIENT_CONNECT_ATTRS = 0x00100000; + private static final int CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; + private static final int CLIENT_CAN_HANDLE_EXPIRED_PASSWORD = 0x00400000; + private static final int CLIENT_SESSION_TRACK = 0x00800000; + private static final int CLIENT_DEPRECATE_EOF = 0x01000000; + + private static final int SERVER_STATUS_IN_TRANS = 1; + private static final int SERVER_STATUS_AUTOCOMMIT = 2; // Server in auto_commit mode + static final int SERVER_MORE_RESULTS_EXISTS = 8; // Multi query - next query exists + private static final int SERVER_QUERY_NO_GOOD_INDEX_USED = 16; + private static final int SERVER_QUERY_NO_INDEX_USED = 32; + private static final int SERVER_QUERY_WAS_SLOW = 2048; + private static final int SERVER_STATUS_CURSOR_EXISTS = 64; + private static final String FALSE_SCRAMBLE = "xxxxxxxx"; + protected static final int MAX_QUERY_SIZE_TO_LOG = 1024; // truncate logging of queries at 1K + protected static final int MAX_QUERY_SIZE_TO_EXPLAIN = 1024 * 1024; // don't explain queries above 1MB + protected static final int INITIAL_PACKET_SIZE = 1024; + /** + * We store the platform 'encoding' here, only used to avoid munging filenames for LOAD DATA LOCAL INFILE... + */ + private static String jvmPlatformCharset = null; + + /** + * We need to have a 'marker' for all-zero datetimes so that ResultSet can decide what to do based on connection setting + */ + protected final static String ZERO_DATE_VALUE_MARKER = "0000-00-00"; + protected final static String ZERO_DATETIME_VALUE_MARKER = "0000-00-00 00:00:00"; + + private static final String EXPLAINABLE_STATEMENT = "SELECT"; + private static final String[] EXPLAINABLE_STATEMENT_EXTENSION = new String[] { "INSERT", "UPDATE", "REPLACE", "DELETE" }; + + static { + OutputStreamWriter outWriter = null; + + // + // Use the I/O system to get the encoding (if possible), to avoid security restrictions on System.getProperty("file.encoding") in applets (why is that + // restricted?) + // + try { + outWriter = new OutputStreamWriter(new ByteArrayOutputStream()); + jvmPlatformCharset = outWriter.getEncoding(); + } finally { + try { + if (outWriter != null) { + outWriter.close(); + } + } catch (IOException ioEx) { + // ignore + } + } + } + + /** Max number of bytes to dump when tracing the protocol */ + private final static int MAX_PACKET_DUMP_LENGTH = 1024; + private boolean packetSequenceReset = false; + protected int serverCharsetIndex; + + // + // Use this when reading in rows to avoid thousands of new() calls, because the byte arrays just get copied out of the packet anyway + // + private Buffer reusablePacket = null; + private Buffer sendPacket = null; + private Buffer sharedSendPacket = null; + + /** Data to the server */ + protected BufferedOutputStream mysqlOutput = null; + protected MySQLConnection connection; + private Deflater deflater = null; + protected InputStream mysqlInput = null; + private LinkedList packetDebugRingBuffer = null; + private RowData streamingData = null; + + /** The connection to the server */ + public Socket mysqlConnection = null; + protected SocketFactory socketFactory = null; + + // + // Packet used for 'LOAD DATA LOCAL INFILE' + // + // We use a SoftReference, so that we don't penalize intermittent use of this feature + // + private SoftReference loadFileBufRef; + + // + // Used to send large packets to the server versions 4+ + // We use a SoftReference, so that we don't penalize intermittent use of this feature + // + private SoftReference splitBufRef; + private SoftReference compressBufRef; + protected String host = null; + protected String seed; + private String serverVersion = null; + private String socketFactoryClassName = null; + private byte[] packetHeaderBuf = new byte[4]; + private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag? + private boolean hadWarnings = false; + private boolean has41NewNewProt = false; + + /** Does the server support long column info? */ + private boolean hasLongColumnInfo = false; + private boolean isInteractiveClient = false; + private boolean logSlowQueries = false; + + /** + * Does the character set of this connection match the character set of the + * platform + */ + private boolean platformDbCharsetMatches = true; // changed once we've connected. + private boolean profileSql = false; + private boolean queryBadIndexUsed = false; + private boolean queryNoIndexUsed = false; + private boolean serverQueryWasSlow = false; + + /** Should we use 4.1 protocol extensions? */ + private boolean use41Extensions = false; + private boolean useCompression = false; + private boolean useNewLargePackets = false; + private boolean useNewUpdateCounts = false; // should we use the new larger update counts? + private byte packetSequence = 0; + private byte compressedPacketSequence = 0; + private byte readPacketSequence = -1; + private boolean checkPacketSequence = false; + private byte protocolVersion = 0; + private int maxAllowedPacket = 1024 * 1024; + protected int maxThreeBytes = 255 * 255 * 255; + protected int port = 3306; + protected int serverCapabilities; + private int serverMajorVersion = 0; + private int serverMinorVersion = 0; + private int oldServerStatus = 0; + private int serverStatus = 0; + private int serverSubMinorVersion = 0; + private int warningCount = 0; + protected long clientParam = 0; + protected long lastPacketSentTimeMs = 0; + protected long lastPacketReceivedTimeMs = 0; + private boolean traceProtocol = false; + private boolean enablePacketDebug = false; + private boolean useConnectWithDb; + private boolean needToGrabQueryFromPacket; + private boolean autoGenerateTestcaseScript; + private long threadId; + private boolean useNanosForElapsedTime; + private long slowQueryThreshold; + private String queryTimingUnits; + private boolean useDirectRowUnpack = true; + private int useBufferRowSizeThreshold; + private int commandCount = 0; + private List statementInterceptors; + private ExceptionInterceptor exceptionInterceptor; + private int authPluginDataLength = 0; + + /** + * Constructor: Connect to the MySQL server and setup a stream connection. + * + * @param host + * the hostname to connect to + * @param port + * the port number that the server is listening on + * @param props + * the Properties from DriverManager.getConnection() + * @param socketFactoryClassName + * the socket factory to use + * @param conn + * the Connection that is creating us + * @param socketTimeout + * the timeout to set for the socket (0 means no + * timeout) + * + * @throws IOException + * if an IOException occurs during connect. + * @throws SQLException + * if a database access error occurs. + */ + public MysqlIO(String host, int port, Properties props, String socketFactoryClassName, MySQLConnection conn, int socketTimeout, + int useBufferRowSizeThreshold) throws IOException, SQLException { + this.connection = conn; + + if (this.connection.getEnablePacketDebug()) { + this.packetDebugRingBuffer = new LinkedList(); + } + this.traceProtocol = this.connection.getTraceProtocol(); + + this.useAutoSlowLog = this.connection.getAutoSlowLog(); + + this.useBufferRowSizeThreshold = useBufferRowSizeThreshold; + this.useDirectRowUnpack = this.connection.getUseDirectRowUnpack(); + + this.logSlowQueries = this.connection.getLogSlowQueries(); + + this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE); + this.sendPacket = new Buffer(INITIAL_PACKET_SIZE); + + this.port = port; + this.host = host; + + this.socketFactoryClassName = socketFactoryClassName; + this.socketFactory = createSocketFactory(); + this.exceptionInterceptor = this.connection.getExceptionInterceptor(); + + try { + this.mysqlConnection = this.socketFactory.connect(this.host, this.port, props); + + if (socketTimeout != 0) { + try { + this.mysqlConnection.setSoTimeout(socketTimeout); + } catch (Exception ex) { + /* Ignore if the platform does not support it */ + } + } + + this.mysqlConnection = this.socketFactory.beforeHandshake(); + + if (this.connection.getUseReadAheadInput()) { + this.mysqlInput = new ReadAheadInputStream(this.mysqlConnection.getInputStream(), 16384, this.connection.getTraceProtocol(), + this.connection.getLog()); + } else if (this.connection.useUnbufferedInput()) { + this.mysqlInput = this.mysqlConnection.getInputStream(); + } else { + this.mysqlInput = new BufferedInputStream(this.mysqlConnection.getInputStream(), 16384); + } + + this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection.getOutputStream(), 16384); + + this.isInteractiveClient = this.connection.getInteractiveClient(); + this.profileSql = this.connection.getProfileSql(); + this.autoGenerateTestcaseScript = this.connection.getAutoGenerateTestcaseScript(); + + this.needToGrabQueryFromPacket = (this.profileSql || this.logSlowQueries || this.autoGenerateTestcaseScript); + + if (this.connection.getUseNanosForElapsedTime() && TimeUtil.nanoTimeAvailable()) { + this.useNanosForElapsedTime = true; + + this.queryTimingUnits = Messages.getString("Nanoseconds"); + } else { + this.queryTimingUnits = Messages.getString("Milliseconds"); + } + + if (this.connection.getLogSlowQueries()) { + calculateSlowQueryThreshold(); + } + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, 0, 0, ioEx, getExceptionInterceptor()); + } + } + + /** + * Does the server send back extra column info? + * + * @return true if so + */ + public boolean hasLongColumnInfo() { + return this.hasLongColumnInfo; + } + + protected boolean isDataAvailable() throws SQLException { + try { + return this.mysqlInput.available() > 0; + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + /** + * @return Returns the lastPacketSentTimeMs. + */ + protected long getLastPacketSentTimeMs() { + return this.lastPacketSentTimeMs; + } + + protected long getLastPacketReceivedTimeMs() { + return this.lastPacketReceivedTimeMs; + } + + /** + * Build a result set. Delegates to buildResultSetWithRows() to build a + * JDBC-version-specific ResultSet, given rows as byte data, and field + * information. + * + * @param callingStatement + * @param columnCount + * the number of columns in the result set + * @param maxRows + * the maximum number of rows to read (-1 means all rows) + * @param resultSetType + * (TYPE_FORWARD_ONLY, TYPE_SCROLL_????) + * @param resultSetConcurrency + * the type of result set (CONCUR_UPDATABLE or + * READ_ONLY) + * @param streamResults + * should the result set be read all at once, or + * streamed? + * @param catalog + * the database name in use when the result set was created + * @param isBinaryEncoded + * is this result set in native encoding? + * @param unpackFieldInfo + * should we read MYSQL_FIELD info (if available)? + * + * @return a result set + * + * @throws SQLException + * if a database access error occurs + */ + protected ResultSetImpl getResultSet(StatementImpl callingStatement, long columnCount, int maxRows, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, boolean isBinaryEncoded, Field[] metadataFromCache) throws SQLException { + Buffer packet; // The packet from the server + Field[] fields = null; + + // Read in the column information + + if (metadataFromCache == null /* we want the metadata from the server */) { + fields = new Field[(int) columnCount]; + + for (int i = 0; i < columnCount; i++) { + Buffer fieldPacket = null; + + fieldPacket = readPacket(); + fields[i] = unpackField(fieldPacket, false); + } + } else { + for (int i = 0; i < columnCount; i++) { + skipPacket(); + } + } + + // There is no EOF packet after fields when CLIENT_DEPRECATE_EOF is set + if (!isEOFDeprecated() || + // if we asked to use cursor then there should be an OK packet here + (this.connection.versionMeetsMinimum(5, 0, 2) && callingStatement != null && isBinaryEncoded && callingStatement.isCursorRequired())) { + + packet = reuseAndReadPacket(this.reusablePacket); + readServerStatusForResultSets(packet); + } + + // + // Handle cursor-based fetch first + // + + if (this.connection.versionMeetsMinimum(5, 0, 2) && this.connection.getUseCursorFetch() && isBinaryEncoded && callingStatement != null + && callingStatement.getFetchSize() != 0 && callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) { + ServerPreparedStatement prepStmt = (com.mysql.jdbc.ServerPreparedStatement) callingStatement; + + boolean usingCursor = true; + + // + // Server versions 5.0.5 or newer will only open a cursor and set this flag if they can, otherwise they punt and go back to mysql_store_results() + // behavior + // + + if (this.connection.versionMeetsMinimum(5, 0, 5)) { + usingCursor = (this.serverStatus & SERVER_STATUS_CURSOR_EXISTS) != 0; + } + + if (usingCursor) { + RowData rows = new RowDataCursor(this, prepStmt, fields); + + ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, fields, rows, resultSetType, resultSetConcurrency, isBinaryEncoded); + + if (usingCursor) { + rs.setFetchSize(callingStatement.getFetchSize()); + } + + return rs; + } + } + + RowData rowData = null; + + if (!streamResults) { + rowData = readSingleRowSet(columnCount, maxRows, resultSetConcurrency, isBinaryEncoded, (metadataFromCache == null) ? fields : metadataFromCache); + } else { + rowData = new RowDataDynamic(this, (int) columnCount, (metadataFromCache == null) ? fields : metadataFromCache, isBinaryEncoded); + this.streamingData = rowData; + } + + ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, (metadataFromCache == null) ? fields : metadataFromCache, rowData, resultSetType, + resultSetConcurrency, isBinaryEncoded); + + return rs; + } + + // We do this to break the chain between MysqlIO and Connection, so that we can have PhantomReferences on connections that let the driver clean up the + // socket connection without having to use finalize() somewhere (which although more straightforward, is horribly inefficent). + protected NetworkResources getNetworkResources() { + return new NetworkResources(this.mysqlConnection, this.mysqlInput, this.mysqlOutput); + } + + /** + * Forcibly closes the underlying socket to MySQL. + */ + protected final void forceClose() { + try { + getNetworkResources().forceClose(); + } finally { + this.mysqlConnection = null; + this.mysqlInput = null; + this.mysqlOutput = null; + } + } + + /** + * Reads and discards a single MySQL packet from the input stream. + * + * @throws SQLException + * if the network fails while skipping the + * packet. + */ + protected final void skipPacket() throws SQLException { + try { + + int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.1")); + } + + int packetLength = (this.packetHeaderBuf[0] & 0xff) + ((this.packetHeaderBuf[1] & 0xff) << 8) + ((this.packetHeaderBuf[2] & 0xff) << 16); + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.2")); + traceMessageBuf.append(packetLength); + traceMessageBuf.append(Messages.getString("MysqlIO.3")); + traceMessageBuf.append(StringUtils.dumpAsHex(this.packetHeaderBuf, 4)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + byte multiPacketSeq = this.packetHeaderBuf[3]; + + if (!this.packetSequenceReset) { + if (this.enablePacketDebug && this.checkPacketSequence) { + checkPacketSequencing(multiPacketSeq); + } + } else { + this.packetSequenceReset = false; + } + + this.readPacketSequence = multiPacketSeq; + + skipFully(this.mysqlInput, packetLength); + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } catch (OutOfMemoryError oom) { + try { + this.connection.realClose(false, false, true, oom); + } catch (Exception ex) { + } + throw oom; + } + } + + /** + * Read one packet from the MySQL server + * + * @return the packet from the server. + * + * @throws SQLException + * @throws CommunicationsException + */ + protected final Buffer readPacket() throws SQLException { + try { + + int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.1")); + } + + int packetLength = (this.packetHeaderBuf[0] & 0xff) + ((this.packetHeaderBuf[1] & 0xff) << 8) + ((this.packetHeaderBuf[2] & 0xff) << 16); + + if (packetLength > this.maxAllowedPacket) { + throw new PacketTooBigException(packetLength, this.maxAllowedPacket); + } + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.2")); + traceMessageBuf.append(packetLength); + traceMessageBuf.append(Messages.getString("MysqlIO.3")); + traceMessageBuf.append(StringUtils.dumpAsHex(this.packetHeaderBuf, 4)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + byte multiPacketSeq = this.packetHeaderBuf[3]; + + if (!this.packetSequenceReset) { + if (this.enablePacketDebug && this.checkPacketSequence) { + checkPacketSequencing(multiPacketSeq); + } + } else { + this.packetSequenceReset = false; + } + + this.readPacketSequence = multiPacketSeq; + + // Read data + byte[] buffer = new byte[packetLength]; + int numBytesRead = readFully(this.mysqlInput, buffer, 0, packetLength); + + if (numBytesRead != packetLength) { + throw new IOException("Short read, expected " + packetLength + " bytes, only read " + numBytesRead); + } + + Buffer packet = new Buffer(buffer); + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.4")); + traceMessageBuf.append(getPacketDumpToLog(packet, packetLength)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + if (this.enablePacketDebug) { + enqueuePacketForDebugging(false, false, 0, this.packetHeaderBuf, packet); + } + + if (this.connection.getMaintainTimeStats()) { + this.lastPacketReceivedTimeMs = System.currentTimeMillis(); + } + + return packet; + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } catch (OutOfMemoryError oom) { + try { + this.connection.realClose(false, false, true, oom); + } catch (Exception ex) { + } + throw oom; + } + } + + /** + * Unpacks the Field information from the given packet. Understands pre 4.1 + * and post 4.1 server version field packet structures. + * + * @param packet + * the packet containing the field information + * @param extractDefaultValues + * should default values be extracted? + * + * @return the unpacked field + * + * @throws SQLException + */ + protected final Field unpackField(Buffer packet, boolean extractDefaultValues) throws SQLException { + if (this.use41Extensions) { + // we only store the position of the string and + // materialize only if needed... + if (this.has41NewNewProt) { + // Not used yet, 5.0? + int catalogNameStart = packet.getPosition() + 1; + int catalogNameLength = packet.fastSkipLenString(); + catalogNameStart = adjustStartForFieldLength(catalogNameStart, catalogNameLength); + } + + int databaseNameStart = packet.getPosition() + 1; + int databaseNameLength = packet.fastSkipLenString(); + databaseNameStart = adjustStartForFieldLength(databaseNameStart, databaseNameLength); + + int tableNameStart = packet.getPosition() + 1; + int tableNameLength = packet.fastSkipLenString(); + tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength); + + // orgTableName is never used so skip + int originalTableNameStart = packet.getPosition() + 1; + int originalTableNameLength = packet.fastSkipLenString(); + originalTableNameStart = adjustStartForFieldLength(originalTableNameStart, originalTableNameLength); + + // we only store the position again... + int nameStart = packet.getPosition() + 1; + int nameLength = packet.fastSkipLenString(); + + nameStart = adjustStartForFieldLength(nameStart, nameLength); + + // orgColName is not required so skip... + int originalColumnNameStart = packet.getPosition() + 1; + int originalColumnNameLength = packet.fastSkipLenString(); + originalColumnNameStart = adjustStartForFieldLength(originalColumnNameStart, originalColumnNameLength); + + packet.readByte(); + + short charSetNumber = (short) packet.readInt(); + + long colLength = 0; + + if (this.has41NewNewProt) { + colLength = packet.readLong(); + } else { + colLength = packet.readLongInt(); + } + + int colType = packet.readByte() & 0xff; + + short colFlag = 0; + + if (this.hasLongColumnInfo) { + colFlag = (short) packet.readInt(); + } else { + colFlag = (short) (packet.readByte() & 0xff); + } + + int colDecimals = packet.readByte() & 0xff; + + int defaultValueStart = -1; + int defaultValueLength = -1; + + if (extractDefaultValues) { + defaultValueStart = packet.getPosition() + 1; + defaultValueLength = packet.fastSkipLenString(); + } + + Field field = new Field(this.connection, packet.getByteBuffer(), databaseNameStart, databaseNameLength, tableNameStart, tableNameLength, + originalTableNameStart, originalTableNameLength, nameStart, nameLength, originalColumnNameStart, originalColumnNameLength, colLength, + colType, colFlag, colDecimals, defaultValueStart, defaultValueLength, charSetNumber); + + return field; + } + + int tableNameStart = packet.getPosition() + 1; + int tableNameLength = packet.fastSkipLenString(); + tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength); + + int nameStart = packet.getPosition() + 1; + int nameLength = packet.fastSkipLenString(); + nameStart = adjustStartForFieldLength(nameStart, nameLength); + + int colLength = packet.readnBytes(); + int colType = packet.readnBytes(); + packet.readByte(); // We know it's currently 2 + + short colFlag = 0; + + if (this.hasLongColumnInfo) { + colFlag = (short) (packet.readInt()); + } else { + colFlag = (short) (packet.readByte() & 0xff); + } + + int colDecimals = (packet.readByte() & 0xff); + + if (this.colDecimalNeedsBump) { + colDecimals++; + } + + Field field = new Field(this.connection, packet.getByteBuffer(), nameStart, nameLength, tableNameStart, tableNameLength, colLength, colType, colFlag, + colDecimals); + + return field; + } + + private int adjustStartForFieldLength(int nameStart, int nameLength) { + if (nameLength < 251) { + return nameStart; + } + + if (nameLength >= 251 && nameLength < 65536) { + return nameStart + 2; + } + + if (nameLength >= 65536 && nameLength < 16777216) { + return nameStart + 3; + } + + return nameStart + 8; + } + + protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) { + if (this.use41Extensions && this.connection.getElideSetAutoCommits()) { + boolean autoCommitModeOnServer = ((this.serverStatus & SERVER_STATUS_AUTOCOMMIT) != 0); + + if (!autoCommitFlag && versionMeetsMinimum(5, 0, 0)) { + // Just to be safe, check if a transaction is in progress on the server.... + // if so, then we must be in autoCommit == false + // therefore return the opposite of transaction status + return !inTransactionOnServer(); + } + + return autoCommitModeOnServer != autoCommitFlag; + } + + return true; + } + + protected boolean inTransactionOnServer() { + return (this.serverStatus & SERVER_STATUS_IN_TRANS) != 0; + } + + /** + * Re-authenticates as the given user and password + * + * @param userName + * @param password + * @param database + * + * @throws SQLException + */ + protected void changeUser(String userName, String password, String database) throws SQLException { + this.packetSequence = -1; + this.compressedPacketSequence = -1; + + int passwordLength = 16; + int userLength = (userName != null) ? userName.length() : 0; + int databaseLength = (database != null) ? database.length() : 0; + + int packLength = ((userLength + passwordLength + databaseLength) * 3) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD; + + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + + proceedHandshakeWithPluggableAuthentication(userName, password, database, null); + + } else if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { + Buffer changeUserPacket = new Buffer(packLength + 1); + changeUserPacket.writeByte((byte) MysqlDefs.COM_CHANGE_USER); + + if (versionMeetsMinimum(4, 1, 1)) { + secureAuth411(changeUserPacket, packLength, userName, password, database, false); + } else { + secureAuth(changeUserPacket, packLength, userName, password, database, false); + } + } else { + // Passwords can be 16 chars long + Buffer packet = new Buffer(packLength); + packet.writeByte((byte) MysqlDefs.COM_CHANGE_USER); + + // User/Password data + packet.writeString(userName); + + if (this.protocolVersion > 9) { + packet.writeString(Util.newCrypt(password, this.seed, this.connection.getPasswordCharacterEncoding())); + } else { + packet.writeString(Util.oldCrypt(password, this.seed)); + } + + boolean localUseConnectWithDb = this.useConnectWithDb && (database != null && database.length() > 0); + + if (localUseConnectWithDb) { + packet.writeString(database); + } else { + //Not needed, old server does not require \0 + //packet.writeString(""); + } + + send(packet, packet.getPosition()); + checkErrorPacket(); + + if (!localUseConnectWithDb) { + changeDatabaseTo(database); + } + } + } + + /** + * Checks for errors in the reply packet, and if none, returns the reply + * packet, ready for reading + * + * @return a packet ready for reading. + * + * @throws SQLException + * is the packet is an error packet + */ + protected Buffer checkErrorPacket() throws SQLException { + return checkErrorPacket(-1); + } + + /** + * Determines if the database charset is the same as the platform charset + */ + protected void checkForCharsetMismatch() { + if (this.connection.getUseUnicode() && (this.connection.getEncoding() != null)) { + String encodingToCheck = jvmPlatformCharset; + + if (encodingToCheck == null) { + encodingToCheck = System.getProperty("file.encoding"); + } + + if (encodingToCheck == null) { + this.platformDbCharsetMatches = false; + } else { + this.platformDbCharsetMatches = encodingToCheck.equals(this.connection.getEncoding()); + } + } + } + + protected void clearInputStream() throws SQLException { + try { + int len; + + // Due to a bug in some older Linux kernels (fixed after the patch "tcp: fix FIONREAD/SIOCINQ"), our SocketInputStream.available() may return 1 even + // if there is no data in the Stream, so, we need to check if InputStream.skip() actually skipped anything. + while ((len = this.mysqlInput.available()) > 0 && this.mysqlInput.skip(len) > 0) { + continue; + } + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + protected void resetReadPacketSequence() { + this.readPacketSequence = 0; + } + + protected void dumpPacketRingBuffer() throws SQLException { + if ((this.packetDebugRingBuffer != null) && this.connection.getEnablePacketDebug()) { + StringBuilder dumpBuffer = new StringBuilder(); + + dumpBuffer.append("Last " + this.packetDebugRingBuffer.size() + " packets received from server, from oldest->newest:\n"); + dumpBuffer.append("\n"); + + for (Iterator ringBufIter = this.packetDebugRingBuffer.iterator(); ringBufIter.hasNext();) { + dumpBuffer.append(ringBufIter.next()); + dumpBuffer.append("\n"); + } + + this.connection.getLog().logTrace(dumpBuffer.toString()); + } + } + + /** + * Runs an 'EXPLAIN' on the given query and dumps the results to the log + * + * @param querySQL + * @param truncatedQuery + * + * @throws SQLException + */ + protected void explainSlowQuery(byte[] querySQL, String truncatedQuery) throws SQLException { + if (StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, EXPLAINABLE_STATEMENT) + || (versionMeetsMinimum(5, 6, 3) && StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, EXPLAINABLE_STATEMENT_EXTENSION) != -1)) { + + PreparedStatement stmt = null; + java.sql.ResultSet rs = null; + + try { + stmt = (PreparedStatement) this.connection.clientPrepareStatement("EXPLAIN ?"); + stmt.setBytesNoEscapeNoQuotes(1, querySQL); + rs = stmt.executeQuery(); + + StringBuilder explainResults = new StringBuilder(Messages.getString("MysqlIO.8") + truncatedQuery + Messages.getString("MysqlIO.9")); + + ResultSetUtil.appendResultSetSlashGStyle(explainResults, rs); + + this.connection.getLog().logWarn(explainResults.toString()); + } catch (SQLException sqlEx) { + } finally { + if (rs != null) { + rs.close(); + } + + if (stmt != null) { + stmt.close(); + } + } + } + } + + static int getMaxBuf() { + return maxBufferSize; + } + + /** + * Get the major version of the MySQL server we are talking to. + */ + final int getServerMajorVersion() { + return this.serverMajorVersion; + } + + /** + * Get the minor version of the MySQL server we are talking to. + */ + final int getServerMinorVersion() { + return this.serverMinorVersion; + } + + /** + * Get the sub-minor version of the MySQL server we are talking to. + */ + final int getServerSubMinorVersion() { + return this.serverSubMinorVersion; + } + + /** + * Get the version string of the server we are talking to + */ + String getServerVersion() { + return this.serverVersion; + } + + /** + * Initialize communications with the MySQL server. Handles logging on, and + * handling initial connection errors. + * + * @param user + * @param password + * @param database + * + * @throws SQLException + * @throws CommunicationsException + */ + void doHandshake(String user, String password, String database) throws SQLException { + // Read the first packet + this.checkPacketSequence = false; + this.readPacketSequence = 0; + + Buffer buf = readPacket(); + + // Get the protocol version + this.protocolVersion = buf.readByte(); + + if (this.protocolVersion == -1) { + try { + this.mysqlConnection.close(); + } catch (Exception e) { + // ignore + } + + int errno = 2000; + + errno = buf.readInt(); + + String serverErrorMessage = buf.readString("ASCII", getExceptionInterceptor()); + + StringBuilder errorBuf = new StringBuilder(Messages.getString("MysqlIO.10")); + errorBuf.append(serverErrorMessage); + errorBuf.append("\""); + + String xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes()); + + throw SQLError.createSQLException(SQLError.get(xOpen) + ", " + errorBuf.toString(), xOpen, errno, getExceptionInterceptor()); + } + + this.serverVersion = buf.readString("ASCII", getExceptionInterceptor()); + + // Parse the server version into major/minor/subminor + int point = this.serverVersion.indexOf('.'); + + if (point != -1) { + try { + int n = Integer.parseInt(this.serverVersion.substring(0, point)); + this.serverMajorVersion = n; + } catch (NumberFormatException NFE1) { + // ignore + } + + String remaining = this.serverVersion.substring(point + 1, this.serverVersion.length()); + point = remaining.indexOf('.'); + + if (point != -1) { + try { + int n = Integer.parseInt(remaining.substring(0, point)); + this.serverMinorVersion = n; + } catch (NumberFormatException nfe) { + // ignore + } + + remaining = remaining.substring(point + 1, remaining.length()); + + int pos = 0; + + while (pos < remaining.length()) { + if ((remaining.charAt(pos) < '0') || (remaining.charAt(pos) > '9')) { + break; + } + + pos++; + } + + try { + int n = Integer.parseInt(remaining.substring(0, pos)); + this.serverSubMinorVersion = n; + } catch (NumberFormatException nfe) { + // ignore + } + } + } + + if (versionMeetsMinimum(4, 0, 8)) { + this.maxThreeBytes = (256 * 256 * 256) - 1; + this.useNewLargePackets = true; + } else { + this.maxThreeBytes = 255 * 255 * 255; + this.useNewLargePackets = false; + } + + this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0); + this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog + this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5); + + // read connection id + this.threadId = buf.readLong(); + + if (this.protocolVersion > 9) { + // read auth-plugin-data-part-1 (string[8]) + this.seed = buf.readString("ASCII", getExceptionInterceptor(), 8); + // read filler ([00]) + buf.readByte(); + } else { + // read scramble (string[NUL]) + this.seed = buf.readString("ASCII", getExceptionInterceptor()); + } + + this.serverCapabilities = 0; + + // read capability flags (lower 2 bytes) + if (buf.getPosition() < buf.getBufLength()) { + this.serverCapabilities = buf.readInt(); + } + + if ((versionMeetsMinimum(4, 1, 1) || ((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_PROTOCOL_41) != 0))) { + + /* New protocol with 16 bytes to describe server characteristics */ + // read character set (1 byte) + this.serverCharsetIndex = buf.readByte() & 0xff; + // read status flags (2 bytes) + this.serverStatus = buf.readInt(); + checkTransactionState(0); + + // read capability flags (upper 2 bytes) + this.serverCapabilities |= buf.readInt() << 16; + + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + // read length of auth-plugin-data (1 byte) + this.authPluginDataLength = buf.readByte() & 0xff; + } else { + // read filler ([00]) + buf.readByte(); + } + // next 10 bytes are reserved (all [00]) + buf.setPosition(buf.getPosition() + 10); + + if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { + String seedPart2; + StringBuilder newSeed; + // read string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8)) + if (this.authPluginDataLength > 0) { + // TODO: disabled the following check for further clarification + // if (this.authPluginDataLength < 21) { + // forceClose(); + // throw SQLError.createSQLException(Messages.getString("MysqlIO.103"), + // SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + // } + seedPart2 = buf.readString("ASCII", getExceptionInterceptor(), this.authPluginDataLength - 8); + newSeed = new StringBuilder(this.authPluginDataLength); + } else { + seedPart2 = buf.readString("ASCII", getExceptionInterceptor()); + newSeed = new StringBuilder(SEED_LENGTH); + } + newSeed.append(this.seed); + newSeed.append(seedPart2); + this.seed = newSeed.toString(); + } + } + + if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) && this.connection.getUseCompression()) { + this.clientParam |= CLIENT_COMPRESS; + } + + this.useConnectWithDb = (database != null) && (database.length() > 0) && !this.connection.getCreateDatabaseIfNotExist(); + + if (this.useConnectWithDb) { + this.clientParam |= CLIENT_CONNECT_WITH_DB; + } + + // Changing SSL defaults for 5.7+ server: useSSL=true, requireSSL=false, verifyServerCertificate=false + if (versionMeetsMinimum(5, 7, 0) && !this.connection.getUseSSL() && !this.connection.isUseSSLExplicit()) { + this.connection.setUseSSL(true); + this.connection.setVerifyServerCertificate(false); + this.connection.getLog().logWarn(Messages.getString("MysqlIO.SSLWarning")); + } + + // check SSL availability + if (((this.serverCapabilities & CLIENT_SSL) == 0) && this.connection.getUseSSL()) { + if (this.connection.getRequireSSL()) { + this.connection.close(); + forceClose(); + throw SQLError.createSQLException(Messages.getString("MysqlIO.15"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, + getExceptionInterceptor()); + } + + this.connection.setUseSSL(false); + } + + if ((this.serverCapabilities & CLIENT_LONG_FLAG) != 0) { + // We understand other column flags, as well + this.clientParam |= CLIENT_LONG_FLAG; + this.hasLongColumnInfo = true; + } + + // return FOUND rows + if (!this.connection.getUseAffectedRows()) { + this.clientParam |= CLIENT_FOUND_ROWS; + } + + if (this.connection.getAllowLoadLocalInfile()) { + this.clientParam |= CLIENT_LOCAL_FILES; + } + + if (this.isInteractiveClient) { + this.clientParam |= CLIENT_INTERACTIVE; + } + + if ((this.serverCapabilities & CLIENT_SESSION_TRACK) != 0) { + // TODO MYSQLCONNJ-437 + // this.clientParam |= CLIENT_SESSION_TRACK; + } + + if ((this.serverCapabilities & CLIENT_DEPRECATE_EOF) != 0) { + this.clientParam |= CLIENT_DEPRECATE_EOF; + } + + // + // switch to pluggable authentication if available + // + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + proceedHandshakeWithPluggableAuthentication(user, password, database, buf); + return; + } + + // Authenticate + if (this.protocolVersion > 9) { + this.clientParam |= CLIENT_LONG_PASSWORD; // for long passwords + } else { + this.clientParam &= ~CLIENT_LONG_PASSWORD; + } + + // + // 4.1 has some differences in the protocol + // + if ((versionMeetsMinimum(4, 1, 0) || ((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_RESERVED) != 0))) { + if ((versionMeetsMinimum(4, 1, 1) || ((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_PROTOCOL_41) != 0))) { + this.clientParam |= CLIENT_PROTOCOL_41; + this.has41NewNewProt = true; + + // Need this to get server status values + this.clientParam |= CLIENT_TRANSACTIONS; + + // We always allow multiple result sets + this.clientParam |= CLIENT_MULTI_RESULTS; + + // We allow the user to configure whether + // or not they want to support multiple queries + // (by default, this is disabled). + if (this.connection.getAllowMultiQueries()) { + this.clientParam |= CLIENT_MULTI_STATEMENTS; + } + } else { + this.clientParam |= CLIENT_RESERVED; + this.has41NewNewProt = false; + } + + this.use41Extensions = true; + } + + int passwordLength = 16; + int userLength = (user != null) ? user.length() : 0; + int databaseLength = (database != null) ? database.length() : 0; + + int packLength = ((userLength + passwordLength + databaseLength) * 3) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD; + + Buffer packet = null; + + if (!this.connection.getUseSSL()) { + if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { + this.clientParam |= CLIENT_SECURE_CONNECTION; + + if ((versionMeetsMinimum(4, 1, 1) || ((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_PROTOCOL_41) != 0))) { + secureAuth411(null, packLength, user, password, database, true); + } else { + secureAuth(null, packLength, user, password, database, true); + } + } else { + // Passwords can be 16 chars long + packet = new Buffer(packLength); + + if ((this.clientParam & CLIENT_RESERVED) != 0) { + if ((versionMeetsMinimum(4, 1, 1) || ((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_PROTOCOL_41) != 0))) { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + + // charset, JDBC will connect as 'latin1', and use 'SET NAMES' to change to the desired charset after the connection is established. + packet.writeByte((byte) 8); + + // Set of bytes reserved for future use. + packet.writeBytesNoNull(new byte[23]); + } else { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + } + } else { + packet.writeInt((int) this.clientParam); + packet.writeLongInt(this.maxThreeBytes); + } + + // User/Password data + packet.writeString(user, CODE_PAGE_1252, this.connection); + + if (this.protocolVersion > 9) { + packet.writeString(Util.newCrypt(password, this.seed, this.connection.getPasswordCharacterEncoding()), CODE_PAGE_1252, this.connection); + } else { + packet.writeString(Util.oldCrypt(password, this.seed), CODE_PAGE_1252, this.connection); + } + + if (this.useConnectWithDb) { + packet.writeString(database, CODE_PAGE_1252, this.connection); + } + + send(packet, packet.getPosition()); + } + } else { + negotiateSSLConnection(user, password, database, packLength); + + if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { + if (versionMeetsMinimum(4, 1, 1)) { + secureAuth411(null, packLength, user, password, database, true); + } else { + secureAuth411(null, packLength, user, password, database, true); + } + } else { + + packet = new Buffer(packLength); + + if (this.use41Extensions) { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + } else { + packet.writeInt((int) this.clientParam); + packet.writeLongInt(this.maxThreeBytes); + } + + // User/Password data + packet.writeString(user); + + if (this.protocolVersion > 9) { + packet.writeString(Util.newCrypt(password, this.seed, this.connection.getPasswordCharacterEncoding())); + } else { + packet.writeString(Util.oldCrypt(password, this.seed)); + } + + if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0) && (database != null) && (database.length() > 0)) { + packet.writeString(database); + } + + send(packet, packet.getPosition()); + } + } + + // Check for errors, not for 4.1.1 or newer, as the new auth protocol doesn't work that way (see secureAuth411() for more details...) + if (!(versionMeetsMinimum(4, 1, 1)) || !((this.protocolVersion > 9) && (this.serverCapabilities & CLIENT_PROTOCOL_41) != 0)) { + checkErrorPacket(); + } + + // + // Can't enable compression until after handshake + // + if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) && this.connection.getUseCompression() && !(this.mysqlInput instanceof CompressedInputStream)) { + // The following matches with ZLIB's compress() + this.deflater = new Deflater(); + this.useCompression = true; + this.mysqlInput = new CompressedInputStream(this.connection, this.mysqlInput); + } + + if (!this.useConnectWithDb) { + changeDatabaseTo(database); + } + + try { + this.mysqlConnection = this.socketFactory.afterHandshake(); + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + /** + * Contains instances of authentication plugins which implements {@link AuthenticationPlugin} interface. Key values are mysql + * protocol plugin names, for example "mysql_native_password" and + * "mysql_old_password" for built-in plugins. + */ + private Map authenticationPlugins = null; + /** + * Contains names of classes or mechanisms ("mysql_native_password" + * for example) of authentication plugins which must be disabled. + */ + private List disabledAuthenticationPlugins = null; + /** + * Name of class for default authentication plugin in client + */ + private String clientDefaultAuthenticationPlugin = null; + /** + * Protocol name of default authentication plugin in client + */ + private String clientDefaultAuthenticationPluginName = null; + /** + * Protocol name of default authentication plugin in server + */ + private String serverDefaultAuthenticationPluginName = null; + + /** + * Fill the {@link MysqlIO#authenticationPlugins} map. + * First this method fill the map with instances of {@link MysqlOldPasswordPlugin}, {@link MysqlNativePasswordPlugin}, {@link MysqlClearPasswordPlugin} and + * {@link Sha256PasswordPlugin}. + * Then it gets instances of plugins listed in "authenticationPlugins" connection property by + * {@link Util#loadExtensions(Connection, Properties, String, String, ExceptionInterceptor)} call and adds them to the map too. + * + * The key for the map entry is getted by {@link AuthenticationPlugin#getProtocolPluginName()}. + * Thus it is possible to replace built-in plugin with custom one, to do it custom plugin should return value + * "mysql_native_password", "mysql_old_password", "mysql_clear_password" or "sha256_password" from it's own getProtocolPluginName() method. + * + * All plugin instances in the map are initialized by {@link Extension#init(Connection, Properties)} call + * with this.connection and this.connection.getProperties() values. + * + * @throws SQLException + */ + private void loadAuthenticationPlugins() throws SQLException { + + // default plugin + this.clientDefaultAuthenticationPlugin = this.connection.getDefaultAuthenticationPlugin(); + if (this.clientDefaultAuthenticationPlugin == null || "".equals(this.clientDefaultAuthenticationPlugin.trim())) { + throw SQLError.createSQLException( + Messages.getString("Connection.BadDefaultAuthenticationPlugin", new Object[] { this.clientDefaultAuthenticationPlugin }), + getExceptionInterceptor()); + } + + // disabled plugins + String disabledPlugins = this.connection.getDisabledAuthenticationPlugins(); + if (disabledPlugins != null && !"".equals(disabledPlugins)) { + this.disabledAuthenticationPlugins = new ArrayList(); + List pluginsToDisable = StringUtils.split(disabledPlugins, ",", true); + Iterator iter = pluginsToDisable.iterator(); + while (iter.hasNext()) { + this.disabledAuthenticationPlugins.add(iter.next()); + } + } + + this.authenticationPlugins = new HashMap(); + + // embedded plugins + AuthenticationPlugin plugin = new MysqlOldPasswordPlugin(); + plugin.init(this.connection, this.connection.getProperties()); + boolean defaultIsFound = addAuthenticationPlugin(plugin); + + plugin = new MysqlNativePasswordPlugin(); + plugin.init(this.connection, this.connection.getProperties()); + if (addAuthenticationPlugin(plugin)) { + defaultIsFound = true; + } + + plugin = new MysqlClearPasswordPlugin(); + plugin.init(this.connection, this.connection.getProperties()); + if (addAuthenticationPlugin(plugin)) { + defaultIsFound = true; + } + + plugin = new Sha256PasswordPlugin(); + plugin.init(this.connection, this.connection.getProperties()); + if (addAuthenticationPlugin(plugin)) { + defaultIsFound = true; + } + + // plugins from authenticationPluginClasses connection parameter + String authenticationPluginClasses = this.connection.getAuthenticationPlugins(); + if (authenticationPluginClasses != null && !"".equals(authenticationPluginClasses)) { + + List plugins = Util.loadExtensions(this.connection, this.connection.getProperties(), authenticationPluginClasses, + "Connection.BadAuthenticationPlugin", getExceptionInterceptor()); + + for (Extension object : plugins) { + plugin = (AuthenticationPlugin) object; + if (addAuthenticationPlugin(plugin)) { + defaultIsFound = true; + } + } + } + + // check if default plugin is listed + if (!defaultIsFound) { + throw SQLError.createSQLException( + Messages.getString("Connection.DefaultAuthenticationPluginIsNotListed", new Object[] { this.clientDefaultAuthenticationPlugin }), + getExceptionInterceptor()); + } + + } + + /** + * Add plugin to {@link MysqlIO#authenticationPlugins} if it is not disabled by + * "disabledAuthenticationPlugins" property, check is it a default plugin. + * + * @param plugin + * Instance of AuthenticationPlugin + * @return True if plugin is default, false if plugin is not default. + * @throws SQLException + * if plugin is default but disabled. + */ + private boolean addAuthenticationPlugin(AuthenticationPlugin plugin) throws SQLException { + boolean isDefault = false; + String pluginClassName = plugin.getClass().getName(); + String pluginProtocolName = plugin.getProtocolPluginName(); + boolean disabledByClassName = this.disabledAuthenticationPlugins != null && this.disabledAuthenticationPlugins.contains(pluginClassName); + boolean disabledByMechanism = this.disabledAuthenticationPlugins != null && this.disabledAuthenticationPlugins.contains(pluginProtocolName); + + if (disabledByClassName || disabledByMechanism) { + // if disabled then check is it default + if (this.clientDefaultAuthenticationPlugin.equals(pluginClassName)) { + throw SQLError.createSQLException(Messages.getString("Connection.BadDisabledAuthenticationPlugin", + new Object[] { disabledByClassName ? pluginClassName : pluginProtocolName }), getExceptionInterceptor()); + } + } else { + this.authenticationPlugins.put(pluginProtocolName, plugin); + if (this.clientDefaultAuthenticationPlugin.equals(pluginClassName)) { + this.clientDefaultAuthenticationPluginName = pluginProtocolName; + isDefault = true; + } + } + return isDefault; + } + + /** + * Get authentication plugin instance from {@link MysqlIO#authenticationPlugins} map by + * pluginName key. If such plugin is found it's {@link AuthenticationPlugin#isReusable()} method + * is checked, when it's false this method returns a new instance of plugin + * and the same instance otherwise. + * + * If plugin is not found method returns null, in such case the subsequent behavior + * of handshake process depends on type of last packet received from server: + * if it was Auth Challenge Packet then handshake will proceed with default plugin, + * if it was Auth Method Switch Request Packet then handshake will be interrupted with exception. + * + * @param pluginName + * mysql protocol plugin names, for example "mysql_native_password" and "mysql_old_password" for built-in plugins + * @return null if plugin is not found or authentication plugin instance initialized with current connection properties + * @throws SQLException + */ + private AuthenticationPlugin getAuthenticationPlugin(String pluginName) throws SQLException { + + AuthenticationPlugin plugin = this.authenticationPlugins.get(pluginName); + + if (plugin != null && !plugin.isReusable()) { + try { + plugin = plugin.getClass().newInstance(); + plugin.init(this.connection, this.connection.getProperties()); + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("Connection.BadAuthenticationPlugin", new Object[] { plugin.getClass().getName() }), getExceptionInterceptor()); + sqlEx.initCause(t); + throw sqlEx; + } + } + + return plugin; + } + + /** + * Check if given plugin requires confidentiality, but connection is without SSL + * + * @param plugin + * @throws SQLException + */ + private void checkConfidentiality(AuthenticationPlugin plugin) throws SQLException { + if (plugin.requiresConfidentiality() && !isSSLEstablished()) { + throw SQLError.createSQLException(Messages.getString("Connection.AuthenticationPluginRequiresSSL", new Object[] { plugin.getProtocolPluginName() }), + getExceptionInterceptor()); + } + } + + /** + * Performs an authentication handshake to authorize connection to a + * given database as a given MySQL user. This can happen upon initial + * connection to the server, after receiving Auth Challenge Packet, or + * at any moment during the connection life-time via a Change User + * request. + * + * This method is aware of pluggable authentication and will use + * registered authentication plugins as requested by the server. + * + * @param user + * the MySQL user account to log into + * @param password + * authentication data for the user account (depends + * on authentication method used - can be empty) + * @param database + * database to connect to (can be empty) + * @param challenge + * the Auth Challenge Packet received from server if + * this method is used during the initial connection. + * Otherwise null. + * + * @throws SQLException + */ + private void proceedHandshakeWithPluggableAuthentication(String user, String password, String database, Buffer challenge) throws SQLException { + if (this.authenticationPlugins == null) { + loadAuthenticationPlugins(); + } + + boolean skipPassword = false; + int passwordLength = 16; + int userLength = (user != null) ? user.length() : 0; + int databaseLength = (database != null) ? database.length() : 0; + + int packLength = ((userLength + passwordLength + databaseLength) * 3) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD; + + AuthenticationPlugin plugin = null; + Buffer fromServer = null; + ArrayList toServer = new ArrayList(); + boolean done = false; + Buffer last_sent = null; + + boolean old_raw_challenge = false; + + int counter = 100; + + while (0 < counter--) { + + if (!done) { + + if (challenge != null) { + + if (challenge.isOKPacket()) { + throw SQLError.createSQLException( + Messages.getString("Connection.UnexpectedAuthenticationApproval", new Object[] { plugin.getProtocolPluginName() }), + getExceptionInterceptor()); + } + + // read Auth Challenge Packet + + this.clientParam |= CLIENT_PLUGIN_AUTH | CLIENT_LONG_PASSWORD | CLIENT_PROTOCOL_41 | CLIENT_TRANSACTIONS // Need this to get server status values + | CLIENT_MULTI_RESULTS // We always allow multiple result sets + | CLIENT_SECURE_CONNECTION; // protocol with pluggable authentication always support this + + // We allow the user to configure whether or not they want to support multiple queries (by default, this is disabled). + if (this.connection.getAllowMultiQueries()) { + this.clientParam |= CLIENT_MULTI_STATEMENTS; + } + + if (((this.serverCapabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORD) != 0) && !this.connection.getDisconnectOnExpiredPasswords()) { + this.clientParam |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORD; + } + if (((this.serverCapabilities & CLIENT_CONNECT_ATTRS) != 0) && !NONE.equals(this.connection.getConnectionAttributes())) { + this.clientParam |= CLIENT_CONNECT_ATTRS; + } + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) != 0) { + this.clientParam |= CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA; + } + + this.has41NewNewProt = true; + this.use41Extensions = true; + + if (this.connection.getUseSSL()) { + negotiateSSLConnection(user, password, database, packLength); + } + + String pluginName = null; + // Due to Bug#59453 the auth-plugin-name is missing the terminating NUL-char in versions prior to 5.5.10 and 5.6.2. + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + if (!versionMeetsMinimum(5, 5, 10) || versionMeetsMinimum(5, 6, 0) && !versionMeetsMinimum(5, 6, 2)) { + pluginName = challenge.readString("ASCII", getExceptionInterceptor(), this.authPluginDataLength); + } else { + pluginName = challenge.readString("ASCII", getExceptionInterceptor()); + } + } + + plugin = getAuthenticationPlugin(pluginName); + if (plugin == null) { + /* + * Use default if there is no plugin for pluginName. + */ + plugin = getAuthenticationPlugin(this.clientDefaultAuthenticationPluginName); + } else if (pluginName.equals(Sha256PasswordPlugin.PLUGIN_NAME) && !isSSLEstablished() && this.connection.getServerRSAPublicKeyFile() == null + && !this.connection.getAllowPublicKeyRetrieval()) { + /* + * Fall back to default if plugin is 'sha256_password' but required conditions for this to work aren't met. If default is other than + * 'sha256_password' this will result in an immediate authentication switch request, allowing for other plugins to authenticate + * successfully. If default is 'sha256_password' then the authentication will fail as expected. In both cases user's password won't be + * sent to avoid subjecting it to lesser security levels. + */ + plugin = getAuthenticationPlugin(this.clientDefaultAuthenticationPluginName); + skipPassword = !this.clientDefaultAuthenticationPluginName.equals(pluginName); + } + + this.serverDefaultAuthenticationPluginName = plugin.getProtocolPluginName(); + + checkConfidentiality(plugin); + fromServer = new Buffer(StringUtils.getBytes(this.seed)); + } else { + // no challenge so this is a changeUser call + plugin = getAuthenticationPlugin(this.serverDefaultAuthenticationPluginName == null ? this.clientDefaultAuthenticationPluginName + : this.serverDefaultAuthenticationPluginName); + + checkConfidentiality(plugin); + + // Servers not affected by Bug#70865 expect the Change User Request containing a correct answer + // to seed sent by the server during the initial handshake, thus we reuse it here. + // Servers affected by Bug#70865 will just ignore it and send the Auth Switch. + fromServer = new Buffer(StringUtils.getBytes(this.seed)); + } + + } else { + + // read packet from server and check if it's an ERROR packet + challenge = checkErrorPacket(); + old_raw_challenge = false; + this.packetSequence++; + this.compressedPacketSequence++; + + if (plugin == null) { + // this shouldn't happen in normal handshake packets exchange, + // we do it just to ensure that we don't get NPE in other case + plugin = getAuthenticationPlugin(this.serverDefaultAuthenticationPluginName != null ? this.serverDefaultAuthenticationPluginName + : this.clientDefaultAuthenticationPluginName); + } + + if (challenge.isOKPacket()) { + // if OK packet then finish handshake + plugin.destroy(); + break; + + } else if (challenge.isAuthMethodSwitchRequestPacket()) { + skipPassword = false; + + // read Auth Method Switch Request Packet + String pluginName = challenge.readString("ASCII", getExceptionInterceptor()); + + // get new plugin + if (!plugin.getProtocolPluginName().equals(pluginName)) { + plugin.destroy(); + plugin = getAuthenticationPlugin(pluginName); + // if plugin is not found for pluginName throw exception + if (plugin == null) { + throw SQLError.createSQLException(Messages.getString("Connection.BadAuthenticationPlugin", new Object[] { pluginName }), + getExceptionInterceptor()); + } + } + + checkConfidentiality(plugin); + fromServer = new Buffer(StringUtils.getBytes(challenge.readString("ASCII", getExceptionInterceptor()))); + + } else { + // read raw packet + if (versionMeetsMinimum(5, 5, 16)) { + fromServer = new Buffer(challenge.getBytes(challenge.getPosition(), challenge.getBufLength() - challenge.getPosition())); + } else { + old_raw_challenge = true; + fromServer = new Buffer(challenge.getBytes(challenge.getPosition() - 1, challenge.getBufLength() - challenge.getPosition() + 1)); + } + } + + } + + // call plugin + try { + plugin.setAuthenticationParameters(user, skipPassword ? null : password); + done = plugin.nextAuthenticationStep(fromServer, toServer); + } catch (SQLException e) { + throw SQLError.createSQLException(e.getMessage(), e.getSQLState(), e, getExceptionInterceptor()); + } + + // send response + if (toServer.size() > 0) { + if (challenge == null) { + String enc = getEncodingForHandshake(); + + // write COM_CHANGE_USER Packet + last_sent = new Buffer(packLength + 1); + last_sent.writeByte((byte) MysqlDefs.COM_CHANGE_USER); + + // User/Password data + last_sent.writeString(user, enc, this.connection); + + // 'auth-response-len' is limited to one Byte but, in case of success, COM_CHANGE_USER will be followed by an AuthSwitchRequest anyway + if (toServer.get(0).getBufLength() < 256) { + // non-mysql servers may use this information to authenticate without requiring another round-trip + last_sent.writeByte((byte) toServer.get(0).getBufLength()); + last_sent.writeBytesNoNull(toServer.get(0).getByteBuffer(), 0, toServer.get(0).getBufLength()); + } else { + last_sent.writeByte((byte) 0); + } + + if (this.useConnectWithDb) { + last_sent.writeString(database, enc, this.connection); + } else { + /* For empty database */ + last_sent.writeByte((byte) 0); + } + + appendCharsetByteForHandshake(last_sent, enc); + // two (little-endian) bytes for charset in this packet + last_sent.writeByte((byte) 0); + + // plugin name + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + last_sent.writeString(plugin.getProtocolPluginName(), enc, this.connection); + } + + // connection attributes + if ((this.clientParam & CLIENT_CONNECT_ATTRS) != 0) { + sendConnectionAttributes(last_sent, enc, this.connection); + last_sent.writeByte((byte) 0); + } + + send(last_sent, last_sent.getPosition()); + + } else if (challenge.isAuthMethodSwitchRequestPacket()) { + // write Auth Method Switch Response Packet + last_sent = new Buffer(toServer.get(0).getBufLength() + HEADER_LENGTH); + last_sent.writeBytesNoNull(toServer.get(0).getByteBuffer(), 0, toServer.get(0).getBufLength()); + send(last_sent, last_sent.getPosition()); + + } else if (challenge.isRawPacket() || old_raw_challenge) { + // write raw packet(s) + for (Buffer buffer : toServer) { + last_sent = new Buffer(buffer.getBufLength() + HEADER_LENGTH); + last_sent.writeBytesNoNull(buffer.getByteBuffer(), 0, toServer.get(0).getBufLength()); + send(last_sent, last_sent.getPosition()); + } + + } else { + // write Auth Response Packet + String enc = getEncodingForHandshake(); + + last_sent = new Buffer(packLength); + last_sent.writeLong(this.clientParam); + last_sent.writeLong(this.maxThreeBytes); + + appendCharsetByteForHandshake(last_sent, enc); + + last_sent.writeBytesNoNull(new byte[23]); // Set of bytes reserved for future use. + + // User/Password data + last_sent.writeString(user, enc, this.connection); + + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) != 0) { + // send lenenc-int length of auth-response and string[n] auth-response + last_sent.writeLenBytes(toServer.get(0).getBytes(toServer.get(0).getBufLength())); + } else { + // send 1 byte length of auth-response and string[n] auth-response + last_sent.writeByte((byte) toServer.get(0).getBufLength()); + last_sent.writeBytesNoNull(toServer.get(0).getByteBuffer(), 0, toServer.get(0).getBufLength()); + } + + if (this.useConnectWithDb) { + last_sent.writeString(database, enc, this.connection); + } else { + /* For empty database */ + last_sent.writeByte((byte) 0); + } + + if ((this.serverCapabilities & CLIENT_PLUGIN_AUTH) != 0) { + last_sent.writeString(plugin.getProtocolPluginName(), enc, this.connection); + } + + // connection attributes + if (((this.clientParam & CLIENT_CONNECT_ATTRS) != 0)) { + sendConnectionAttributes(last_sent, enc, this.connection); + } + + send(last_sent, last_sent.getPosition()); + } + + } + + } + + if (counter == 0) { + throw SQLError.createSQLException(Messages.getString("CommunicationsException.TooManyAuthenticationPluginNegotiations"), getExceptionInterceptor()); + } + + // + // Can't enable compression until after handshake + // + if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) && this.connection.getUseCompression() && !(this.mysqlInput instanceof CompressedInputStream)) { + // The following matches with ZLIB's compress() + this.deflater = new Deflater(); + this.useCompression = true; + this.mysqlInput = new CompressedInputStream(this.connection, this.mysqlInput); + } + + if (!this.useConnectWithDb) { + changeDatabaseTo(database); + } + + try { + this.mysqlConnection = this.socketFactory.afterHandshake(); + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + private Properties getConnectionAttributesAsProperties(String atts) throws SQLException { + + Properties props = new Properties(); + + if (atts != null) { + String[] pairs = atts.split(","); + for (String pair : pairs) { + int keyEnd = pair.indexOf(":"); + if (keyEnd > 0 && (keyEnd + 1) < pair.length()) { + props.setProperty(pair.substring(0, keyEnd), pair.substring(keyEnd + 1)); + } + } + } + + // Leaving disabled until standard values are defined + // props.setProperty("_os", NonRegisteringDriver.OS); + // props.setProperty("_platform", NonRegisteringDriver.PLATFORM); + props.setProperty("_client_name", NonRegisteringDriver.NAME); + props.setProperty("_client_version", NonRegisteringDriver.VERSION); + props.setProperty("_runtime_vendor", NonRegisteringDriver.RUNTIME_VENDOR); + props.setProperty("_runtime_version", NonRegisteringDriver.RUNTIME_VERSION); + props.setProperty("_client_license", NonRegisteringDriver.LICENSE); + + return props; + } + + private void sendConnectionAttributes(Buffer buf, String enc, MySQLConnection conn) throws SQLException { + String atts = conn.getConnectionAttributes(); + + Buffer lb = new Buffer(100); + try { + + Properties props = getConnectionAttributesAsProperties(atts); + + for (Object key : props.keySet()) { + lb.writeLenString((String) key, enc, conn.getServerCharset(), null, conn.parserKnowsUnicode(), conn); + lb.writeLenString(props.getProperty((String) key), enc, conn.getServerCharset(), null, conn.parserKnowsUnicode(), conn); + } + + } catch (UnsupportedEncodingException e) { + + } + + buf.writeByte((byte) (lb.getPosition() - 4)); + buf.writeBytesNoNull(lb.getByteBuffer(), 4, lb.getBufLength() - 4); + + } + + private void changeDatabaseTo(String database) throws SQLException { + if (database == null || database.length() == 0) { + return; + } + + try { + sendCommand(MysqlDefs.INIT_DB, database, null, false, null, 0); + } catch (Exception ex) { + if (this.connection.getCreateDatabaseIfNotExist()) { + sendCommand(MysqlDefs.QUERY, "CREATE DATABASE IF NOT EXISTS " + database, null, false, null, 0); + sendCommand(MysqlDefs.INIT_DB, database, null, false, null, 0); + } else { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ex, + getExceptionInterceptor()); + } + } + } + + /** + * Retrieve one row from the MySQL server. Note: this method is not + * thread-safe, but it is only called from methods that are guarded by + * synchronizing on this object. + * + * @param fields + * @param columnCount + * @param isBinaryEncoded + * @param resultSetConcurrency + * @param b + * + * @throws SQLException + */ + final ResultSetRow nextRow(Field[] fields, int columnCount, boolean isBinaryEncoded, int resultSetConcurrency, boolean useBufferRowIfPossible, + boolean useBufferRowExplicit, boolean canReuseRowPacketForBufferRow, Buffer existingRowPacket) throws SQLException { + + if (this.useDirectRowUnpack && existingRowPacket == null && !isBinaryEncoded && !useBufferRowIfPossible && !useBufferRowExplicit) { + return nextRowFast(fields, columnCount, isBinaryEncoded, resultSetConcurrency, useBufferRowIfPossible, useBufferRowExplicit, + canReuseRowPacketForBufferRow); + } + + Buffer rowPacket = null; + + if (existingRowPacket == null) { + rowPacket = checkErrorPacket(); + + if (!useBufferRowExplicit && useBufferRowIfPossible) { + if (rowPacket.getBufLength() > this.useBufferRowSizeThreshold) { + useBufferRowExplicit = true; + } + } + } else { + // We attempted to do nextRowFast(), but the packet was a multipacket, so we couldn't unpack it directly + rowPacket = existingRowPacket; + checkErrorPacket(existingRowPacket); + } + + if (!isBinaryEncoded) { + // + // Didn't read an error, so re-position to beginning of packet in order to read result set data + // + rowPacket.setPosition(rowPacket.getPosition() - 1); + + if (!(!isEOFDeprecated() && rowPacket.isEOFPacket() || isEOFDeprecated() && rowPacket.isResultSetOKPacket())) { + if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE || (!useBufferRowIfPossible && !useBufferRowExplicit)) { + + byte[][] rowData = new byte[columnCount][]; + + for (int i = 0; i < columnCount; i++) { + rowData[i] = rowPacket.readLenByteArray(0); + } + + return new ByteArrayRow(rowData, getExceptionInterceptor()); + } + + if (!canReuseRowPacketForBufferRow) { + this.reusablePacket = new Buffer(rowPacket.getBufLength()); + } + + return new BufferRow(rowPacket, fields, false, getExceptionInterceptor()); + + } + + readServerStatusForResultSets(rowPacket); + + return null; + } + + // + // Handle binary-encoded data for server-side PreparedStatements... + // + if (!(!isEOFDeprecated() && rowPacket.isEOFPacket() || isEOFDeprecated() && rowPacket.isResultSetOKPacket())) { + if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE || (!useBufferRowIfPossible && !useBufferRowExplicit)) { + return unpackBinaryResultSetRow(fields, rowPacket, resultSetConcurrency); + } + + if (!canReuseRowPacketForBufferRow) { + this.reusablePacket = new Buffer(rowPacket.getBufLength()); + } + + return new BufferRow(rowPacket, fields, true, getExceptionInterceptor()); + } + + rowPacket.setPosition(rowPacket.getPosition() - 1); + readServerStatusForResultSets(rowPacket); + + return null; + } + + final ResultSetRow nextRowFast(Field[] fields, int columnCount, boolean isBinaryEncoded, int resultSetConcurrency, boolean useBufferRowIfPossible, + boolean useBufferRowExplicit, boolean canReuseRowPacket) throws SQLException { + try { + int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new RuntimeException(Messages.getString("MysqlIO.43")); + } + + int packetLength = (this.packetHeaderBuf[0] & 0xff) + ((this.packetHeaderBuf[1] & 0xff) << 8) + ((this.packetHeaderBuf[2] & 0xff) << 16); + + // Have we stumbled upon a multi-packet? + if (packetLength == this.maxThreeBytes) { + reuseAndReadPacket(this.reusablePacket, packetLength); + + // Go back to "old" way which uses packets + return nextRow(fields, columnCount, isBinaryEncoded, resultSetConcurrency, useBufferRowIfPossible, useBufferRowExplicit, canReuseRowPacket, + this.reusablePacket); + } + + // Does this go over the threshold where we should use a BufferRow? + + if (packetLength > this.useBufferRowSizeThreshold) { + reuseAndReadPacket(this.reusablePacket, packetLength); + + // Go back to "old" way which uses packets + return nextRow(fields, columnCount, isBinaryEncoded, resultSetConcurrency, true, true, false, this.reusablePacket); + } + + int remaining = packetLength; + + boolean firstTime = true; + + byte[][] rowData = null; + + for (int i = 0; i < columnCount; i++) { + + int sw = this.mysqlInput.read() & 0xff; + remaining--; + + if (firstTime) { + if (sw == Buffer.TYPE_ID_ERROR) { + // error packet - we assemble it whole for "fidelity" in case we ever need an entire packet in checkErrorPacket() but we could've gotten + // away with just writing the error code and message in it (for now). + Buffer errorPacket = new Buffer(packetLength + HEADER_LENGTH); + errorPacket.setPosition(0); + errorPacket.writeByte(this.packetHeaderBuf[0]); + errorPacket.writeByte(this.packetHeaderBuf[1]); + errorPacket.writeByte(this.packetHeaderBuf[2]); + errorPacket.writeByte((byte) 1); + errorPacket.writeByte((byte) sw); + readFully(this.mysqlInput, errorPacket.getByteBuffer(), 5, packetLength - 1); + errorPacket.setPosition(4); + checkErrorPacket(errorPacket); + } + + if (sw == Buffer.TYPE_ID_EOF && packetLength < 16777215) { + // Both EOF and OK packets have the same 0xfe signature in result sets. + + // OK packet length limit restricted to MAX_PACKET_LENGTH value (256L*256L*256L-1) as any length greater + // than this value will have first byte of OK packet to be 254 thus does not provide a means to identify + // if this is OK or EOF packet. + // Thus we need to check the packet length to distinguish between OK packet and ResultsetRow packet starting with 0xfe + if (this.use41Extensions) { + if (isEOFDeprecated()) { + // read OK packet + remaining -= skipLengthEncodedInteger(this.mysqlInput); // affected_rows + remaining -= skipLengthEncodedInteger(this.mysqlInput); // last_insert_id + + this.oldServerStatus = this.serverStatus; + this.serverStatus = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8); + checkTransactionState(this.oldServerStatus); + remaining -= 2; + + this.warningCount = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8); + remaining -= 2; + + if (this.warningCount > 0) { + this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand() + } + + } else { + // read EOF packet + this.warningCount = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8); + remaining -= 2; + + if (this.warningCount > 0) { + this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand() + } + + this.oldServerStatus = this.serverStatus; + + this.serverStatus = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8); + checkTransactionState(this.oldServerStatus); + + remaining -= 2; + } + + setServerSlowQueryFlags(); + + if (remaining > 0) { + skipFully(this.mysqlInput, remaining); + } + } + + return null; // last data packet + } + + rowData = new byte[columnCount][]; + + firstTime = false; + } + + int len = 0; + + switch (sw) { + case 251: + len = NULL_LENGTH; + break; + + case 252: + len = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8); + remaining -= 2; + break; + + case 253: + len = (this.mysqlInput.read() & 0xff) | ((this.mysqlInput.read() & 0xff) << 8) | ((this.mysqlInput.read() & 0xff) << 16); + + remaining -= 3; + break; + + case 254: + len = (int) ((this.mysqlInput.read() & 0xff) | ((long) (this.mysqlInput.read() & 0xff) << 8) + | ((long) (this.mysqlInput.read() & 0xff) << 16) | ((long) (this.mysqlInput.read() & 0xff) << 24) + | ((long) (this.mysqlInput.read() & 0xff) << 32) | ((long) (this.mysqlInput.read() & 0xff) << 40) + | ((long) (this.mysqlInput.read() & 0xff) << 48) | ((long) (this.mysqlInput.read() & 0xff) << 56)); + remaining -= 8; + break; + + default: + len = sw; + } + + if (len == NULL_LENGTH) { + rowData[i] = null; + } else if (len == 0) { + rowData[i] = Constants.EMPTY_BYTE_ARRAY; + } else { + rowData[i] = new byte[len]; + + int bytesRead = readFully(this.mysqlInput, rowData[i], 0, len); + + if (bytesRead != len) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, + new IOException(Messages.getString("MysqlIO.43")), getExceptionInterceptor()); + } + + remaining -= bytesRead; + } + } + + if (remaining > 0) { + skipFully(this.mysqlInput, remaining); + } + + return new ByteArrayRow(rowData, getExceptionInterceptor()); + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + /** + * Log-off of the MySQL server and close the socket. + * + * @throws SQLException + */ + final void quit() throws SQLException { + try { + // we're not going to read the response, fixes BUG#56979 Improper connection closing logic leads to TIME_WAIT sockets on server + + try { + if (!this.mysqlConnection.isClosed()) { + try { + this.mysqlConnection.shutdownInput(); + } catch (UnsupportedOperationException ex) { + // ignore, some sockets do not support this method + } + } + } catch (IOException ioEx) { + this.connection.getLog().logWarn("Caught while disconnecting...", ioEx); + } + + Buffer packet = new Buffer(6); + this.packetSequence = -1; + this.compressedPacketSequence = -1; + packet.writeByte((byte) MysqlDefs.QUIT); + send(packet, packet.getPosition()); + } finally { + forceClose(); + } + } + + /** + * Returns the packet used for sending data (used by PreparedStatement) + * Guarded by external synchronization on a mutex. + * + * @return A packet to send data with + */ + Buffer getSharedSendPacket() { + if (this.sharedSendPacket == null) { + this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE); + } + + return this.sharedSendPacket; + } + + void closeStreamer(RowData streamer) throws SQLException { + if (this.streamingData == null) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.17") + streamer + Messages.getString("MysqlIO.18"), getExceptionInterceptor()); + } + + if (streamer != this.streamingData) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.19") + streamer + Messages.getString("MysqlIO.20") + Messages.getString("MysqlIO.21") + + Messages.getString("MysqlIO.22"), getExceptionInterceptor()); + } + + this.streamingData = null; + } + + boolean tackOnMoreStreamingResults(ResultSetImpl addingTo) throws SQLException { + if ((this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0) { + + boolean moreRowSetsExist = true; + ResultSetImpl currentResultSet = addingTo; + boolean firstTime = true; + + while (moreRowSetsExist) { + if (!firstTime && currentResultSet.reallyResult()) { + break; + } + + firstTime = false; + + Buffer fieldPacket = checkErrorPacket(); + fieldPacket.setPosition(0); + + java.sql.Statement owningStatement = addingTo.getStatement(); + + int maxRows = owningStatement.getMaxRows(); + + // fixme for catalog, isBinary + + ResultSetImpl newResultSet = readResultsForQueryOrUpdate((StatementImpl) owningStatement, maxRows, owningStatement.getResultSetType(), + owningStatement.getResultSetConcurrency(), true, owningStatement.getConnection().getCatalog(), fieldPacket, addingTo.isBinaryEncoded, + -1L, null); + + currentResultSet.setNextResultSet(newResultSet); + + currentResultSet = newResultSet; + + moreRowSetsExist = (this.serverStatus & MysqlIO.SERVER_MORE_RESULTS_EXISTS) != 0; + + if (!currentResultSet.reallyResult() && !moreRowSetsExist) { + // special case, we can stop "streaming" + return false; + } + } + + return true; + } + + return false; + } + + ResultSetImpl readAllResults(StatementImpl callingStatement, int maxRows, int resultSetType, int resultSetConcurrency, boolean streamResults, + String catalog, Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount, Field[] metadataFromCache) throws SQLException { + resultPacket.setPosition(resultPacket.getPosition() - 1); + + ResultSetImpl topLevelResultSet = readResultsForQueryOrUpdate(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, + resultPacket, isBinaryEncoded, preSentColumnCount, metadataFromCache); + + ResultSetImpl currentResultSet = topLevelResultSet; + + boolean checkForMoreResults = ((this.clientParam & CLIENT_MULTI_RESULTS) != 0); + + boolean serverHasMoreResults = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0; + + // + // TODO: We need to support streaming of multiple result sets + // + if (serverHasMoreResults && streamResults) { + //clearInputStream(); + // + //throw SQLError.createSQLException(Messages.getString("MysqlIO.23"), + //SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); + if (topLevelResultSet.getUpdateCount() != -1) { + tackOnMoreStreamingResults(topLevelResultSet); + } + + reclaimLargeReusablePacket(); + + return topLevelResultSet; + } + + boolean moreRowSetsExist = checkForMoreResults & serverHasMoreResults; + + while (moreRowSetsExist) { + Buffer fieldPacket = checkErrorPacket(); + fieldPacket.setPosition(0); + + ResultSetImpl newResultSet = readResultsForQueryOrUpdate(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, + fieldPacket, isBinaryEncoded, preSentColumnCount, metadataFromCache); + + currentResultSet.setNextResultSet(newResultSet); + + currentResultSet = newResultSet; + + moreRowSetsExist = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0; + } + + if (!streamResults) { + clearInputStream(); + } + + reclaimLargeReusablePacket(); + + return topLevelResultSet; + } + + /** + * Sets the buffer size to max-buf + */ + void resetMaxBuf() { + this.maxAllowedPacket = this.connection.getMaxAllowedPacket(); + } + + /** + * Send a command to the MySQL server If data is to be sent with command, + * it should be put in extraData. + * + * Raw packets can be sent by setting queryPacket to something other + * than null. + * + * @param command + * the MySQL protocol 'command' from MysqlDefs + * @param extraData + * any 'string' data for the command + * @param queryPacket + * a packet pre-loaded with data for the protocol (i.e. + * from a client-side prepared statement). + * @param skipCheck + * do not call checkErrorPacket() if true + * @param extraDataCharEncoding + * the character encoding of the extraData + * parameter. + * + * @return the response packet from the server + * + * @throws SQLException + * if an I/O error or SQL error occurs + */ + + final Buffer sendCommand(int command, String extraData, Buffer queryPacket, boolean skipCheck, String extraDataCharEncoding, int timeoutMillis) + throws SQLException { + this.commandCount++; + + // + // We cache these locally, per-command, as the checks for them are in very 'hot' sections of the I/O code and we save 10-15% in overall performance by + // doing this... + // + this.enablePacketDebug = this.connection.getEnablePacketDebug(); + this.readPacketSequence = 0; + + int oldTimeout = 0; + + if (timeoutMillis != 0) { + try { + oldTimeout = this.mysqlConnection.getSoTimeout(); + this.mysqlConnection.setSoTimeout(timeoutMillis); + } catch (SocketException e) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, e, + getExceptionInterceptor()); + } + } + + try { + + checkForOutstandingStreamingData(); + + // Clear serverStatus...this value is guarded by an external mutex, as you can only ever be processing one command at a time + this.oldServerStatus = this.serverStatus; + this.serverStatus = 0; + this.hadWarnings = false; + this.warningCount = 0; + + this.queryNoIndexUsed = false; + this.queryBadIndexUsed = false; + this.serverQueryWasSlow = false; + + // + // Compressed input stream needs cleared at beginning of each command execution... + // + if (this.useCompression) { + int bytesLeft = this.mysqlInput.available(); + + if (bytesLeft > 0) { + this.mysqlInput.skip(bytesLeft); + } + } + + try { + clearInputStream(); + + // + // PreparedStatements construct their own packets, for efficiency's sake. + // + // If this is a generic query, we need to re-use the sending packet. + // + if (queryPacket == null) { + int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1 + ((extraData != null) ? extraData.length() : 0) + 2; + + if (this.sendPacket == null) { + this.sendPacket = new Buffer(packLength); + } + + this.packetSequence = -1; + this.compressedPacketSequence = -1; + this.readPacketSequence = 0; + this.checkPacketSequence = true; + this.sendPacket.clear(); + + this.sendPacket.writeByte((byte) command); + + if ((command == MysqlDefs.INIT_DB) || (command == MysqlDefs.CREATE_DB) || (command == MysqlDefs.DROP_DB) || (command == MysqlDefs.QUERY) + || (command == MysqlDefs.COM_PREPARE)) { + if (extraDataCharEncoding == null) { + this.sendPacket.writeStringNoNull(extraData); + } else { + this.sendPacket.writeStringNoNull(extraData, extraDataCharEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), this.connection); + } + } else if (command == MysqlDefs.PROCESS_KILL) { + long id = Long.parseLong(extraData); + this.sendPacket.writeLong(id); + } + + send(this.sendPacket, this.sendPacket.getPosition()); + } else { + this.packetSequence = -1; + this.compressedPacketSequence = -1; + send(queryPacket, queryPacket.getPosition()); // packet passed by PreparedStatement + } + } catch (SQLException sqlEx) { + // don't wrap SQLExceptions + throw sqlEx; + } catch (Exception ex) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ex, + getExceptionInterceptor()); + } + + Buffer returnPacket = null; + + if (!skipCheck) { + if ((command == MysqlDefs.COM_EXECUTE) || (command == MysqlDefs.COM_RESET_STMT)) { + this.readPacketSequence = 0; + this.packetSequenceReset = true; + } + + returnPacket = checkErrorPacket(command); + } + + return returnPacket; + } catch (IOException ioEx) { + preserveOldTransactionState(); + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + + } catch (SQLException e) { + preserveOldTransactionState(); + throw e; + + } finally { + if (timeoutMillis != 0) { + try { + this.mysqlConnection.setSoTimeout(oldTimeout); + } catch (SocketException e) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, e, + getExceptionInterceptor()); + } + } + } + } + + private int statementExecutionDepth = 0; + private boolean useAutoSlowLog; + + protected boolean shouldIntercept() { + return this.statementInterceptors != null; + } + + /** + * Send a query stored in a packet directly to the server. + * + * @param callingStatement + * @param resultSetConcurrency + * @param characterEncoding + * @param queryPacket + * @param maxRows + * @param conn + * @param resultSetType + * @param resultSetConcurrency + * @param streamResults + * @param catalog + * @param unpackFieldInfo + * should we read MYSQL_FIELD info (if available)? + * + * @throws Exception + */ + final ResultSetInternalMethods sqlQueryDirect(StatementImpl callingStatement, String query, String characterEncoding, Buffer queryPacket, int maxRows, + int resultSetType, int resultSetConcurrency, boolean streamResults, String catalog, Field[] cachedMetadata) throws Exception { + this.statementExecutionDepth++; + + try { + if (this.statementInterceptors != null) { + ResultSetInternalMethods interceptedResults = invokeStatementInterceptorsPre(query, callingStatement, false); + + if (interceptedResults != null) { + return interceptedResults; + } + } + + long queryStartTime = 0; + long queryEndTime = 0; + + String statementComment = this.connection.getStatementComment(); + + if (this.connection.getIncludeThreadNamesAsStatementComment()) { + statementComment = (statementComment != null ? statementComment + ", " : "") + "java thread: " + Thread.currentThread().getName(); + } + + if (query != null) { + // We don't know exactly how many bytes we're going to get from the query. Since we're dealing with Unicode, the max is 2, so pad it + // (2 * query) + space for headers + int packLength = HEADER_LENGTH + 1 + (query.length() * 3) + 2; + + byte[] commentAsBytes = null; + + if (statementComment != null) { + commentAsBytes = StringUtils.getBytes(statementComment, null, characterEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + + packLength += commentAsBytes.length; + packLength += 6; // for /*[space] [space]*/ + } + + if (this.sendPacket == null) { + this.sendPacket = new Buffer(packLength); + } else { + this.sendPacket.clear(); + } + + this.sendPacket.writeByte((byte) MysqlDefs.QUERY); + + if (commentAsBytes != null) { + this.sendPacket.writeBytesNoNull(Constants.SLASH_STAR_SPACE_AS_BYTES); + this.sendPacket.writeBytesNoNull(commentAsBytes); + this.sendPacket.writeBytesNoNull(Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES); + } + + if (characterEncoding != null) { + if (this.platformDbCharsetMatches) { + this.sendPacket.writeStringNoNull(query, characterEncoding, this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), + this.connection); + } else { + if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) { + this.sendPacket.writeBytesNoNull(StringUtils.getBytes(query)); + } else { + this.sendPacket.writeStringNoNull(query, characterEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), this.connection); + } + } + } else { + this.sendPacket.writeStringNoNull(query); + } + + queryPacket = this.sendPacket; + } + + byte[] queryBuf = null; + int oldPacketPosition = 0; + + if (this.needToGrabQueryFromPacket) { + queryBuf = queryPacket.getByteBuffer(); + + // save the packet position + oldPacketPosition = queryPacket.getPosition(); + + queryStartTime = getCurrentTimeNanosOrMillis(); + } + + if (this.autoGenerateTestcaseScript) { + String testcaseQuery = null; + + if (query != null) { + if (statementComment != null) { + testcaseQuery = "/* " + statementComment + " */ " + query; + } else { + testcaseQuery = query; + } + } else { + testcaseQuery = StringUtils.toString(queryBuf, 5, (oldPacketPosition - 5)); + } + + StringBuilder debugBuf = new StringBuilder(testcaseQuery.length() + 32); + this.connection.generateConnectionCommentBlock(debugBuf); + debugBuf.append(testcaseQuery); + debugBuf.append(';'); + this.connection.dumpTestcaseQuery(debugBuf.toString()); + } + + // Send query command and sql query string + Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket, false, null, 0); + + long fetchBeginTime = 0; + long fetchEndTime = 0; + + String profileQueryToLog = null; + + boolean queryWasSlow = false; + + if (this.profileSql || this.logSlowQueries) { + queryEndTime = getCurrentTimeNanosOrMillis(); + + boolean shouldExtractQuery = false; + + if (this.profileSql) { + shouldExtractQuery = true; + } else if (this.logSlowQueries) { + long queryTime = queryEndTime - queryStartTime; + + boolean logSlow = false; + + if (!this.useAutoSlowLog) { + logSlow = queryTime > this.connection.getSlowQueryThresholdMillis(); + } else { + logSlow = this.connection.isAbonormallyLongQuery(queryTime); + + this.connection.reportQueryTime(queryTime); + } + + if (logSlow) { + shouldExtractQuery = true; + queryWasSlow = true; + } + } + + if (shouldExtractQuery) { + // Extract the actual query from the network packet + boolean truncated = false; + + int extractPosition = oldPacketPosition; + + if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) { + extractPosition = this.connection.getMaxQuerySizeToLog() + 5; + truncated = true; + } + + profileQueryToLog = StringUtils.toString(queryBuf, 5, (extractPosition - 5)); + + if (truncated) { + profileQueryToLog += Messages.getString("MysqlIO.25"); + } + } + + fetchBeginTime = queryEndTime; + } + + ResultSetInternalMethods rs = readAllResults(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, resultPacket, + false, -1L, cachedMetadata); + + if (queryWasSlow && !this.serverQueryWasSlow /* don't log slow queries twice */) { + StringBuilder mesgBuf = new StringBuilder(48 + profileQueryToLog.length()); + + mesgBuf.append(Messages.getString("MysqlIO.SlowQuery", + new Object[] { String.valueOf(this.useAutoSlowLog ? " 95% of all queries " : this.slowQueryThreshold), this.queryTimingUnits, + Long.valueOf(queryEndTime - queryStartTime) })); + mesgBuf.append(profileQueryToLog); + + ProfilerEventHandler eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (int) (queryEndTime - queryStartTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), + mesgBuf.toString())); + + if (this.connection.getExplainSlowQueries()) { + if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) { + explainSlowQuery(queryPacket.getBytes(5, (oldPacketPosition - 5)), profileQueryToLog); + } else { + this.connection.getLog().logWarn(Messages.getString("MysqlIO.28") + MAX_QUERY_SIZE_TO_EXPLAIN + Messages.getString("MysqlIO.29")); + } + } + } + + if (this.logSlowQueries) { + + ProfilerEventHandler eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + + if (this.queryBadIndexUsed && this.profileSql) { + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), + Messages.getString("MysqlIO.33") + profileQueryToLog)); + } + + if (this.queryNoIndexUsed && this.profileSql) { + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), + Messages.getString("MysqlIO.35") + profileQueryToLog)); + } + + if (this.serverQueryWasSlow && this.profileSql) { + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), + Messages.getString("MysqlIO.ServerSlowQuery") + profileQueryToLog)); + } + } + + if (this.profileSql) { + fetchEndTime = getCurrentTimeNanosOrMillis(); + + ProfilerEventHandler eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), profileQueryToLog)); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH, "", catalog, this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() : 999, ((ResultSetImpl) rs).resultId, System.currentTimeMillis(), + (fetchEndTime - fetchBeginTime), this.queryTimingUnits, null, LogUtils.findCallingClassAndMethod(new Throwable()), null)); + } + + if (this.hadWarnings) { + scanForAndThrowDataTruncation(); + } + + if (this.statementInterceptors != null) { + ResultSetInternalMethods interceptedResults = invokeStatementInterceptorsPost(query, callingStatement, rs, false, null); + + if (interceptedResults != null) { + rs = interceptedResults; + } + } + + return rs; + } catch (SQLException sqlEx) { + if (this.statementInterceptors != null) { + invokeStatementInterceptorsPost(query, callingStatement, null, false, sqlEx); // we don't do anything with the result set in this case + } + + if (callingStatement != null) { + synchronized (callingStatement.cancelTimeoutMutex) { + if (callingStatement.wasCancelled) { + SQLException cause = null; + + if (callingStatement.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + callingStatement.resetCancelledState(); + + throw cause; + } + } + } + + throw sqlEx; + } finally { + this.statementExecutionDepth--; + } + } + + ResultSetInternalMethods invokeStatementInterceptorsPre(String sql, Statement interceptedStatement, boolean forceExecute) throws SQLException { + ResultSetInternalMethods previousResultSet = null; + + for (int i = 0, s = this.statementInterceptors.size(); i < s; i++) { + StatementInterceptorV2 interceptor = this.statementInterceptors.get(i); + + boolean executeTopLevelOnly = interceptor.executeTopLevelOnly(); + boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly); + + if (shouldExecute) { + String sqlToInterceptor = sql; + + //if (interceptedStatement instanceof PreparedStatement) { + // sqlToInterceptor = ((PreparedStatement) interceptedStatement) + // .asSql(); + //} + + ResultSetInternalMethods interceptedResultSet = interceptor.preProcess(sqlToInterceptor, interceptedStatement, this.connection); + + if (interceptedResultSet != null) { + previousResultSet = interceptedResultSet; + } + } + } + + return previousResultSet; + } + + ResultSetInternalMethods invokeStatementInterceptorsPost(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, + boolean forceExecute, SQLException statementException) throws SQLException { + + for (int i = 0, s = this.statementInterceptors.size(); i < s; i++) { + StatementInterceptorV2 interceptor = this.statementInterceptors.get(i); + + boolean executeTopLevelOnly = interceptor.executeTopLevelOnly(); + boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly); + + if (shouldExecute) { + String sqlToInterceptor = sql; + + ResultSetInternalMethods interceptedResultSet = interceptor.postProcess(sqlToInterceptor, interceptedStatement, originalResultSet, + this.connection, this.warningCount, this.queryNoIndexUsed, this.queryBadIndexUsed, statementException); + + if (interceptedResultSet != null) { + originalResultSet = interceptedResultSet; + } + } + } + + return originalResultSet; + } + + private void calculateSlowQueryThreshold() { + this.slowQueryThreshold = this.connection.getSlowQueryThresholdMillis(); + + if (this.connection.getUseNanosForElapsedTime()) { + long nanosThreshold = this.connection.getSlowQueryThresholdNanos(); + + if (nanosThreshold != 0) { + this.slowQueryThreshold = nanosThreshold; + } else { + this.slowQueryThreshold *= 1000000; // 1 million millis in a nano + } + } + } + + protected long getCurrentTimeNanosOrMillis() { + if (this.useNanosForElapsedTime) { + return TimeUtil.getCurrentTimeNanosOrMillis(); + } + + return System.currentTimeMillis(); + } + + /** + * Returns the host this IO is connected to + */ + String getHost() { + return this.host; + } + + /** + * Is the version of the MySQL server we are connected to the given + * version? + * + * @param major + * the major version + * @param minor + * the minor version + * @param subminor + * the subminor version + * + * @return true if the version of the MySQL server we are connected is the + * given version + */ + boolean isVersion(int major, int minor, int subminor) { + return ((major == getServerMajorVersion()) && (minor == getServerMinorVersion()) && (subminor == getServerSubMinorVersion())); + } + + /** + * Does the version of the MySQL server we are connected to meet the given + * minimums? + * + * @param major + * @param minor + * @param subminor + */ + boolean versionMeetsMinimum(int major, int minor, int subminor) { + if (getServerMajorVersion() >= major) { + if (getServerMajorVersion() == major) { + if (getServerMinorVersion() >= minor) { + if (getServerMinorVersion() == minor) { + return (getServerSubMinorVersion() >= subminor); + } + + // newer than major.minor + return true; + } + + // older than major.minor + return false; + } + + // newer than major + return true; + } + + return false; + } + + /** + * Returns the hex dump of the given packet, truncated to + * MAX_PACKET_DUMP_LENGTH if packetLength exceeds that value. + * + * @param packetToDump + * the packet to dump in hex + * @param packetLength + * the number of bytes to dump + * + * @return the hex dump of the given packet + */ + private final static String getPacketDumpToLog(Buffer packetToDump, int packetLength) { + if (packetLength < MAX_PACKET_DUMP_LENGTH) { + return packetToDump.dump(packetLength); + } + + StringBuilder packetDumpBuf = new StringBuilder(MAX_PACKET_DUMP_LENGTH * 4); + packetDumpBuf.append(packetToDump.dump(MAX_PACKET_DUMP_LENGTH)); + packetDumpBuf.append(Messages.getString("MysqlIO.36")); + packetDumpBuf.append(MAX_PACKET_DUMP_LENGTH); + packetDumpBuf.append(Messages.getString("MysqlIO.37")); + + return packetDumpBuf.toString(); + } + + private final int readFully(InputStream in, byte[] b, int off, int len) throws IOException { + if (len < 0) { + throw new IndexOutOfBoundsException(); + } + + int n = 0; + + while (n < len) { + int count = in.read(b, off + n, len - n); + + if (count < 0) { + throw new EOFException(Messages.getString("MysqlIO.EOF", new Object[] { Integer.valueOf(len), Integer.valueOf(n) })); + } + + n += count; + } + + return n; + } + + private final long skipFully(InputStream in, long len) throws IOException { + if (len < 0) { + throw new IOException("Negative skip length not allowed"); + } + + long n = 0; + + while (n < len) { + long count = in.skip(len - n); + + if (count < 0) { + throw new EOFException(Messages.getString("MysqlIO.EOF", new Object[] { Long.valueOf(len), Long.valueOf(n) })); + } + + n += count; + } + + return n; + } + + private final int skipLengthEncodedInteger(InputStream in) throws IOException { + int sw = in.read() & 0xff; + + switch (sw) { + case 252: + return (int) skipFully(in, 2) + 1; + + case 253: + return (int) skipFully(in, 3) + 1; + + case 254: + return (int) skipFully(in, 8) + 1; + + default: + return 1; + } + } + + /** + * Reads one result set off of the wire, if the result is actually an + * update count, creates an update-count only result set. + * + * @param callingStatement + * @param maxRows + * the maximum rows to return in the result set. + * @param resultSetType + * scrollability + * @param resultSetConcurrency + * updatability + * @param streamResults + * should the driver leave the results on the wire, + * and read them only when needed? + * @param catalog + * the catalog in use + * @param resultPacket + * the first packet of information in the result set + * @param isBinaryEncoded + * is this result set from a prepared statement? + * @param preSentColumnCount + * do we already know the number of columns? + * @param unpackFieldInfo + * should we unpack the field information? + * + * @return a result set that either represents the rows, or an update count + * + * @throws SQLException + * if an error occurs while reading the rows + */ + protected final ResultSetImpl readResultsForQueryOrUpdate(StatementImpl callingStatement, int maxRows, int resultSetType, int resultSetConcurrency, + boolean streamResults, String catalog, Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount, Field[] metadataFromCache) + throws SQLException { + long columnCount = resultPacket.readFieldLength(); + + if (columnCount == 0) { + return buildResultSetWithUpdates(callingStatement, resultPacket); + } else if (columnCount == Buffer.NULL_LENGTH) { + String charEncoding = null; + + if (this.connection.getUseUnicode()) { + charEncoding = this.connection.getEncoding(); + } + + String fileName = null; + + if (this.platformDbCharsetMatches) { + fileName = ((charEncoding != null) ? resultPacket.readString(charEncoding, getExceptionInterceptor()) : resultPacket.readString()); + } else { + fileName = resultPacket.readString(); + } + + return sendFileToServer(callingStatement, fileName); + } else { + com.mysql.jdbc.ResultSetImpl results = getResultSet(callingStatement, columnCount, maxRows, resultSetType, resultSetConcurrency, streamResults, + catalog, isBinaryEncoded, metadataFromCache); + + return results; + } + } + + private int alignPacketSize(int a, int l) { + return ((((a) + (l)) - 1) & ~((l) - 1)); + } + + private com.mysql.jdbc.ResultSetImpl buildResultSetWithRows(StatementImpl callingStatement, String catalog, com.mysql.jdbc.Field[] fields, RowData rows, + int resultSetType, int resultSetConcurrency, boolean isBinaryEncoded) throws SQLException { + ResultSetImpl rs = null; + + switch (resultSetConcurrency) { + case java.sql.ResultSet.CONCUR_READ_ONLY: + rs = com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows, this.connection, callingStatement, false); + + if (isBinaryEncoded) { + rs.setBinaryEncoded(); + } + + break; + + case java.sql.ResultSet.CONCUR_UPDATABLE: + rs = com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows, this.connection, callingStatement, true); + + break; + + default: + return com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows, this.connection, callingStatement, false); + } + + rs.setResultSetType(resultSetType); + rs.setResultSetConcurrency(resultSetConcurrency); + + return rs; + } + + private com.mysql.jdbc.ResultSetImpl buildResultSetWithUpdates(StatementImpl callingStatement, Buffer resultPacket) throws SQLException { + long updateCount = -1; + long updateID = -1; + String info = null; + + try { + if (this.useNewUpdateCounts) { + updateCount = resultPacket.newReadLength(); + updateID = resultPacket.newReadLength(); + } else { + updateCount = resultPacket.readLength(); + updateID = resultPacket.readLength(); + } + + if (this.use41Extensions) { + // oldStatus set in sendCommand() + this.serverStatus = resultPacket.readInt(); + + checkTransactionState(this.oldServerStatus); + + this.warningCount = resultPacket.readInt(); + + if (this.warningCount > 0) { + this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand() + } + + resultPacket.readByte(); // advance pointer + + setServerSlowQueryFlags(); + } + + if (this.connection.isReadInfoMsgEnabled()) { + info = resultPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor()); + } + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(SQLError.get(SQLError.SQL_STATE_GENERAL_ERROR), SQLError.SQL_STATE_GENERAL_ERROR, -1, + getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + + ResultSetInternalMethods updateRs = com.mysql.jdbc.ResultSetImpl.getInstance(updateCount, updateID, this.connection, callingStatement); + + if (info != null) { + ((com.mysql.jdbc.ResultSetImpl) updateRs).setServerInfo(info); + } + + return (com.mysql.jdbc.ResultSetImpl) updateRs; + } + + private void setServerSlowQueryFlags() { + this.queryBadIndexUsed = (this.serverStatus & SERVER_QUERY_NO_GOOD_INDEX_USED) != 0; + this.queryNoIndexUsed = (this.serverStatus & SERVER_QUERY_NO_INDEX_USED) != 0; + this.serverQueryWasSlow = (this.serverStatus & SERVER_QUERY_WAS_SLOW) != 0; + } + + private void checkForOutstandingStreamingData() throws SQLException { + if (this.streamingData != null) { + boolean shouldClobber = this.connection.getClobberStreamingResults(); + + if (!shouldClobber) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.39") + this.streamingData + Messages.getString("MysqlIO.40") + + Messages.getString("MysqlIO.41") + Messages.getString("MysqlIO.42"), getExceptionInterceptor()); + } + + // Close the result set + this.streamingData.getOwner().realClose(false); + + // clear any pending data.... + clearInputStream(); + } + } + + /** + * @param packet + * original uncompressed MySQL packet + * @param offset + * begin of MySQL packet header + * @param packetLen + * real length of packet + * @return compressed packet with header + * @throws SQLException + */ + private Buffer compressPacket(Buffer packet, int offset, int packetLen) throws SQLException { + + // uncompressed payload by default + int compressedLength = packetLen; + int uncompressedLength = 0; + byte[] compressedBytes = null; + int offsetWrite = offset; + + if (packetLen < MIN_COMPRESS_LEN) { + compressedBytes = packet.getByteBuffer(); + + } else { + byte[] bytesToCompress = packet.getByteBuffer(); + compressedBytes = new byte[bytesToCompress.length * 2]; + + if (this.deflater == null) { + this.deflater = new Deflater(); + } + this.deflater.reset(); + this.deflater.setInput(bytesToCompress, offset, packetLen); + this.deflater.finish(); + + compressedLength = this.deflater.deflate(compressedBytes); + + if (compressedLength > packetLen) { + // if compressed data is greater then uncompressed then send uncompressed + compressedBytes = packet.getByteBuffer(); + compressedLength = packetLen; + } else { + uncompressedLength = packetLen; + offsetWrite = 0; + } + } + + Buffer compressedPacket = new Buffer(HEADER_LENGTH + COMP_HEADER_LENGTH + compressedLength); + + compressedPacket.setPosition(0); + compressedPacket.writeLongInt(compressedLength); + compressedPacket.writeByte(this.compressedPacketSequence); + compressedPacket.writeLongInt(uncompressedLength); + compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite, compressedLength); + + return compressedPacket; + } + + private final void readServerStatusForResultSets(Buffer rowPacket) throws SQLException { + if (this.use41Extensions) { + rowPacket.readByte(); // skips the 'last packet' flag + + if (isEOFDeprecated()) { + // read OK packet + rowPacket.newReadLength(); // affected_rows + rowPacket.newReadLength(); // last_insert_id + + this.oldServerStatus = this.serverStatus; + this.serverStatus = rowPacket.readInt(); + checkTransactionState(this.oldServerStatus); + + this.warningCount = rowPacket.readInt(); + if (this.warningCount > 0) { + this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand() + } + + rowPacket.readByte(); // advance pointer + + if (this.connection.isReadInfoMsgEnabled()) { + rowPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor()); // info + } + + } else { + // read EOF packet + this.warningCount = rowPacket.readInt(); + if (this.warningCount > 0) { + this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand() + } + + this.oldServerStatus = this.serverStatus; + this.serverStatus = rowPacket.readInt(); + checkTransactionState(this.oldServerStatus); + + } + setServerSlowQueryFlags(); + } + } + + private SocketFactory createSocketFactory() throws SQLException { + try { + if (this.socketFactoryClassName == null) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.75"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, + getExceptionInterceptor()); + } + + return (SocketFactory) (Class.forName(this.socketFactoryClassName).newInstance()); + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("MysqlIO.76") + this.socketFactoryClassName + Messages.getString("MysqlIO.77"), + SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + private void enqueuePacketForDebugging(boolean isPacketBeingSent, boolean isPacketReused, int sendLength, byte[] header, Buffer packet) + throws SQLException { + if ((this.packetDebugRingBuffer.size() + 1) > this.connection.getPacketDebugBufferSize()) { + this.packetDebugRingBuffer.removeFirst(); + } + + StringBuilder packetDump = null; + + if (!isPacketBeingSent) { + int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH, packet.getBufLength()); + + Buffer packetToDump = new Buffer(4 + bytesToDump); + + packetToDump.setPosition(0); + packetToDump.writeBytesNoNull(header); + packetToDump.writeBytesNoNull(packet.getBytes(0, bytesToDump)); + + String packetPayload = packetToDump.dump(bytesToDump); + + packetDump = new StringBuilder(96 + packetPayload.length()); + + packetDump.append("Server "); + + packetDump.append(isPacketReused ? "(re-used) " : "(new) "); + + packetDump.append(packet.toSuperString()); + packetDump.append(" --------------------> Client\n"); + packetDump.append("\nPacket payload:\n\n"); + packetDump.append(packetPayload); + + if (bytesToDump == MAX_PACKET_DUMP_LENGTH) { + packetDump.append("\nNote: Packet of " + packet.getBufLength() + " bytes truncated to " + MAX_PACKET_DUMP_LENGTH + " bytes.\n"); + } + } else { + int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH, sendLength); + + String packetPayload = packet.dump(bytesToDump); + + packetDump = new StringBuilder(64 + 4 + packetPayload.length()); + + packetDump.append("Client "); + packetDump.append(packet.toSuperString()); + packetDump.append("--------------------> Server\n"); + packetDump.append("\nPacket payload:\n\n"); + packetDump.append(packetPayload); + + if (bytesToDump == MAX_PACKET_DUMP_LENGTH) { + packetDump.append("\nNote: Packet of " + sendLength + " bytes truncated to " + MAX_PACKET_DUMP_LENGTH + " bytes.\n"); + } + } + + this.packetDebugRingBuffer.addLast(packetDump); + } + + private RowData readSingleRowSet(long columnCount, int maxRows, int resultSetConcurrency, boolean isBinaryEncoded, Field[] fields) throws SQLException { + RowData rowData; + ArrayList rows = new ArrayList(); + + boolean useBufferRowExplicit = useBufferRowExplicit(fields); + + // Now read the data + ResultSetRow row = nextRow(fields, (int) columnCount, isBinaryEncoded, resultSetConcurrency, false, useBufferRowExplicit, false, null); + + int rowCount = 0; + + if (row != null) { + rows.add(row); + rowCount = 1; + } + + while (row != null) { + row = nextRow(fields, (int) columnCount, isBinaryEncoded, resultSetConcurrency, false, useBufferRowExplicit, false, null); + + if (row != null) { + if ((maxRows == -1) || (rowCount < maxRows)) { + rows.add(row); + rowCount++; + } + } + } + + rowData = new RowDataStatic(rows); + + return rowData; + } + + public static boolean useBufferRowExplicit(Field[] fields) { + if (fields == null) { + return false; + } + + for (int i = 0; i < fields.length; i++) { + switch (fields[i].getSQLType()) { + case Types.BLOB: + case Types.CLOB: + case Types.LONGVARBINARY: + case Types.LONGVARCHAR: + return true; + } + } + + return false; + } + + /** + * Don't hold on to overly-large packets + */ + private void reclaimLargeReusablePacket() { + if ((this.reusablePacket != null) && (this.reusablePacket.getCapacity() > 1048576)) { + this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE); + } + } + + /** + * Re-use a packet to read from the MySQL server + * + * @param reuse + * @throws SQLException + */ + private final Buffer reuseAndReadPacket(Buffer reuse) throws SQLException { + return reuseAndReadPacket(reuse, -1); + } + + private final Buffer reuseAndReadPacket(Buffer reuse, int existingPacketLength) throws SQLException { + + try { + reuse.setWasMultiPacket(false); + int packetLength = 0; + + if (existingPacketLength == -1) { + int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.43")); + } + + packetLength = (this.packetHeaderBuf[0] & 0xff) + ((this.packetHeaderBuf[1] & 0xff) << 8) + ((this.packetHeaderBuf[2] & 0xff) << 16); + } else { + packetLength = existingPacketLength; + } + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.44")); + traceMessageBuf.append(packetLength); + traceMessageBuf.append(Messages.getString("MysqlIO.45")); + traceMessageBuf.append(StringUtils.dumpAsHex(this.packetHeaderBuf, 4)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + byte multiPacketSeq = this.packetHeaderBuf[3]; + + if (!this.packetSequenceReset) { + if (this.enablePacketDebug && this.checkPacketSequence) { + checkPacketSequencing(multiPacketSeq); + } + } else { + this.packetSequenceReset = false; + } + + this.readPacketSequence = multiPacketSeq; + + // Set the Buffer to it's original state + reuse.setPosition(0); + + // Do we need to re-alloc the byte buffer? + // + // Note: We actually check the length of the buffer, rather than getBufLength(), because getBufLength() is not necesarily the actual length of the + // byte array used as the buffer + if (reuse.getByteBuffer().length <= packetLength) { + reuse.setByteBuffer(new byte[packetLength + 1]); + } + + // Set the new length + reuse.setBufLength(packetLength); + + // Read the data from the server + int numBytesRead = readFully(this.mysqlInput, reuse.getByteBuffer(), 0, packetLength); + + if (numBytesRead != packetLength) { + throw new IOException("Short read, expected " + packetLength + " bytes, only read " + numBytesRead); + } + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.46")); + traceMessageBuf.append(getPacketDumpToLog(reuse, packetLength)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + if (this.enablePacketDebug) { + enqueuePacketForDebugging(false, true, 0, this.packetHeaderBuf, reuse); + } + + boolean isMultiPacket = false; + + if (packetLength == this.maxThreeBytes) { + reuse.setPosition(this.maxThreeBytes); + + // it's multi-packet + isMultiPacket = true; + + packetLength = readRemainingMultiPackets(reuse, multiPacketSeq); + } + + if (!isMultiPacket) { + reuse.getByteBuffer()[packetLength] = 0; // Null-termination + } + + if (this.connection.getMaintainTimeStats()) { + this.lastPacketReceivedTimeMs = System.currentTimeMillis(); + } + + return reuse; + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } catch (OutOfMemoryError oom) { + try { + // _Try_ this + clearInputStream(); + } catch (Exception ex) { + } + try { + this.connection.realClose(false, false, true, oom); + } catch (Exception ex) { + } + throw oom; + } + + } + + private int readRemainingMultiPackets(Buffer reuse, byte multiPacketSeq) throws IOException, SQLException { + int packetLength = -1; + Buffer multiPacket = null; + + do { + final int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, 0, 4); + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.47")); + } + + packetLength = (this.packetHeaderBuf[0] & 0xff) + ((this.packetHeaderBuf[1] & 0xff) << 8) + ((this.packetHeaderBuf[2] & 0xff) << 16); + if (multiPacket == null) { + multiPacket = new Buffer(packetLength); + } + + if (!this.useNewLargePackets && (packetLength == 1)) { + clearInputStream(); + break; + } + + multiPacketSeq++; + if (multiPacketSeq != this.packetHeaderBuf[3]) { + throw new IOException(Messages.getString("MysqlIO.49")); + } + + // Set the Buffer to it's original state + multiPacket.setPosition(0); + + // Set the new length + multiPacket.setBufLength(packetLength); + + // Read the data from the server + byte[] byteBuf = multiPacket.getByteBuffer(); + int lengthToWrite = packetLength; + + int bytesRead = readFully(this.mysqlInput, byteBuf, 0, packetLength); + + if (bytesRead != lengthToWrite) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, + SQLError.createSQLException(Messages.getString("MysqlIO.50") + lengthToWrite + Messages.getString("MysqlIO.51") + bytesRead + ".", + getExceptionInterceptor()), + getExceptionInterceptor()); + } + + reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite); + } while (packetLength == this.maxThreeBytes); + + reuse.setPosition(0); + reuse.setWasMultiPacket(true); + return packetLength; + } + + /** + * @param multiPacketSeq + * @throws CommunicationsException + */ + private void checkPacketSequencing(byte multiPacketSeq) throws SQLException { + if ((multiPacketSeq == -128) && (this.readPacketSequence != 127)) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, + new IOException("Packets out of order, expected packet # -128, but received packet # " + multiPacketSeq), getExceptionInterceptor()); + } + + if ((this.readPacketSequence == -1) && (multiPacketSeq != 0)) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, + new IOException("Packets out of order, expected packet # -1, but received packet # " + multiPacketSeq), getExceptionInterceptor()); + } + + if ((multiPacketSeq != -128) && (this.readPacketSequence != -1) && (multiPacketSeq != (this.readPacketSequence + 1))) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, + new IOException("Packets out of order, expected packet # " + (this.readPacketSequence + 1) + ", but received packet # " + multiPacketSeq), + getExceptionInterceptor()); + } + } + + void enableMultiQueries() throws SQLException { + Buffer buf = getSharedSendPacket(); + + buf.clear(); + buf.writeByte((byte) MysqlDefs.COM_SET_OPTION); + buf.writeInt(0); + sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null, 0); + } + + void disableMultiQueries() throws SQLException { + Buffer buf = getSharedSendPacket(); + + buf.clear(); + buf.writeByte((byte) MysqlDefs.COM_SET_OPTION); + buf.writeInt(1); + sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null, 0); + } + + /** + * @param packet + * @param packetLen + * length of header + payload + * @throws SQLException + */ + private final void send(Buffer packet, int packetLen) throws SQLException { + try { + if (this.maxAllowedPacket > 0 && packetLen > this.maxAllowedPacket) { + throw new PacketTooBigException(packetLen, this.maxAllowedPacket); + } + + if ((this.serverMajorVersion >= 4) && (packetLen - HEADER_LENGTH >= this.maxThreeBytes + || (this.useCompression && packetLen - HEADER_LENGTH >= this.maxThreeBytes - COMP_HEADER_LENGTH))) { + sendSplitPackets(packet, packetLen); + + } else { + this.packetSequence++; + + Buffer packetToSend = packet; + packetToSend.setPosition(0); + packetToSend.writeLongInt(packetLen - HEADER_LENGTH); + packetToSend.writeByte(this.packetSequence); + + if (this.useCompression) { + this.compressedPacketSequence++; + int originalPacketLen = packetLen; + + packetToSend = compressPacket(packetToSend, 0, packetLen); + packetLen = packetToSend.getPosition(); + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.57")); + traceMessageBuf.append(getPacketDumpToLog(packetToSend, packetLen)); + traceMessageBuf.append(Messages.getString("MysqlIO.58")); + traceMessageBuf.append(getPacketDumpToLog(packet, originalPacketLen)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + } else { + + if (this.traceProtocol) { + StringBuilder traceMessageBuf = new StringBuilder(); + + traceMessageBuf.append(Messages.getString("MysqlIO.59")); + traceMessageBuf.append("host: '"); + traceMessageBuf.append(this.host); + traceMessageBuf.append("' threadId: '"); + traceMessageBuf.append(this.threadId); + traceMessageBuf.append("'\n"); + traceMessageBuf.append(packetToSend.dump(packetLen)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + } + + this.mysqlOutput.write(packetToSend.getByteBuffer(), 0, packetLen); + this.mysqlOutput.flush(); + } + + if (this.enablePacketDebug) { + enqueuePacketForDebugging(true, false, packetLen + 5, this.packetHeaderBuf, packet); + } + + // + // Don't hold on to large packets + // + if (packet == this.sharedSendPacket) { + reclaimLargeSharedSendPacket(); + } + + if (this.connection.getMaintainTimeStats()) { + this.lastPacketSentTimeMs = System.currentTimeMillis(); + } + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + /** + * Reads and sends a file to the server for LOAD DATA LOCAL INFILE + * + * @param callingStatement + * @param fileName + * the file name to send. + * + * @throws SQLException + */ + private final ResultSetImpl sendFileToServer(StatementImpl callingStatement, String fileName) throws SQLException { + + if (this.useCompression) { + this.compressedPacketSequence++; + } + + Buffer filePacket = (this.loadFileBufRef == null) ? null : this.loadFileBufRef.get(); + + int bigPacketLength = Math.min(this.connection.getMaxAllowedPacket() - (HEADER_LENGTH * 3), + alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096) - (HEADER_LENGTH * 3)); + + int oneMeg = 1024 * 1024; + + int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3), alignPacketSize(oneMeg - 16, 4096) - (HEADER_LENGTH * 3)); + + int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength); + + if (filePacket == null) { + try { + filePacket = new Buffer((packetLength + HEADER_LENGTH)); + this.loadFileBufRef = new SoftReference(filePacket); + } catch (OutOfMemoryError oom) { + throw SQLError.createSQLException( + "Could not allocate packet of " + packetLength + " bytes required for LOAD DATA LOCAL INFILE operation." + + " Try increasing max heap allocation for JVM or decreasing server variable 'max_allowed_packet'", + SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE, getExceptionInterceptor()); + + } + } + + filePacket.clear(); + send(filePacket, 0); + + byte[] fileBuf = new byte[packetLength]; + + BufferedInputStream fileIn = null; + + try { + if (!this.connection.getAllowLoadLocalInfile()) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.LoadDataLocalNotAllowed"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + InputStream hookedStream = null; + + if (callingStatement != null) { + hookedStream = callingStatement.getLocalInfileInputStream(); + } + + if (hookedStream != null) { + fileIn = new BufferedInputStream(hookedStream); + } else if (!this.connection.getAllowUrlInLocalInfile()) { + fileIn = new BufferedInputStream(new FileInputStream(fileName)); + } else { + // First look for ':' + if (fileName.indexOf(':') != -1) { + try { + URL urlFromFileName = new URL(fileName); + fileIn = new BufferedInputStream(urlFromFileName.openStream()); + } catch (MalformedURLException badUrlEx) { + // we fall back to trying this as a file input stream + fileIn = new BufferedInputStream(new FileInputStream(fileName)); + } + } else { + fileIn = new BufferedInputStream(new FileInputStream(fileName)); + } + } + + int bytesRead = 0; + + while ((bytesRead = fileIn.read(fileBuf)) != -1) { + filePacket.clear(); + filePacket.writeBytesNoNull(fileBuf, 0, bytesRead); + send(filePacket, filePacket.getPosition()); + } + } catch (IOException ioEx) { + StringBuilder messageBuf = new StringBuilder(Messages.getString("MysqlIO.60")); + + if (fileName != null && !this.connection.getParanoid()) { + messageBuf.append("'"); + messageBuf.append(fileName); + messageBuf.append("'"); + } + + messageBuf.append(Messages.getString("MysqlIO.63")); + + if (!this.connection.getParanoid()) { + messageBuf.append(Messages.getString("MysqlIO.64")); + messageBuf.append(Util.stackTraceToString(ioEx)); + } + + throw SQLError.createSQLException(messageBuf.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } finally { + if (fileIn != null) { + try { + fileIn.close(); + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("MysqlIO.65"), SQLError.SQL_STATE_GENERAL_ERROR, ex, + getExceptionInterceptor()); + + throw sqlEx; + } + + fileIn = null; + } else { + // file open failed, but server needs one packet + filePacket.clear(); + send(filePacket, filePacket.getPosition()); + checkErrorPacket(); // to clear response off of queue + } + } + + // send empty packet to mark EOF + filePacket.clear(); + send(filePacket, filePacket.getPosition()); + + Buffer resultPacket = checkErrorPacket(); + + return buildResultSetWithUpdates(callingStatement, resultPacket); + } + + /** + * Checks for errors in the reply packet, and if none, returns the reply + * packet, ready for reading + * + * @param command + * the command being issued (if used) + * + * @throws SQLException + * if an error packet was received + * @throws CommunicationsException + */ + private Buffer checkErrorPacket(int command) throws SQLException { + //int statusCode = 0; + Buffer resultPacket = null; + this.serverStatus = 0; + + try { + // Check return value, if we get a java.io.EOFException, the server has gone away. We'll pass it on up the exception chain and let someone higher up + // decide what to do (barf, reconnect, etc). + resultPacket = reuseAndReadPacket(this.reusablePacket); + } catch (SQLException sqlEx) { + // Don't wrap SQL Exceptions + throw sqlEx; + } catch (Exception fallThru) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, fallThru, + getExceptionInterceptor()); + } + + checkErrorPacket(resultPacket); + + return resultPacket; + } + + private void checkErrorPacket(Buffer resultPacket) throws SQLException { + + int statusCode = resultPacket.readByte(); + + // Error handling + if (statusCode == (byte) 0xff) { + String serverErrorMessage; + int errno = 2000; + + if (this.protocolVersion > 9) { + errno = resultPacket.readInt(); + + String xOpen = null; + + serverErrorMessage = resultPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor()); + + if (serverErrorMessage.charAt(0) == '#') { + + // we have an SQLState + if (serverErrorMessage.length() > 6) { + xOpen = serverErrorMessage.substring(1, 6); + serverErrorMessage = serverErrorMessage.substring(6); + + if (xOpen.equals("HY000")) { + xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes()); + } + } else { + xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes()); + } + } else { + xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes()); + } + + clearInputStream(); + + StringBuilder errorBuf = new StringBuilder(); + + String xOpenErrorMessage = SQLError.get(xOpen); + + if (!this.connection.getUseOnlyServerErrorMessages()) { + if (xOpenErrorMessage != null) { + errorBuf.append(xOpenErrorMessage); + errorBuf.append(Messages.getString("MysqlIO.68")); + } + } + + errorBuf.append(serverErrorMessage); + + if (!this.connection.getUseOnlyServerErrorMessages()) { + if (xOpenErrorMessage != null) { + errorBuf.append("\""); + } + } + + appendDeadlockStatusInformation(xOpen, errorBuf); + + if (xOpen != null && xOpen.startsWith("22")) { + throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0, errno); + } + throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno, false, getExceptionInterceptor(), this.connection); + } + + serverErrorMessage = resultPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor()); + clearInputStream(); + + if (serverErrorMessage.indexOf(Messages.getString("MysqlIO.70")) != -1) { + throw SQLError.createSQLException(SQLError.get(SQLError.SQL_STATE_COLUMN_NOT_FOUND) + ", " + serverErrorMessage, + SQLError.SQL_STATE_COLUMN_NOT_FOUND, -1, false, getExceptionInterceptor(), this.connection); + } + + StringBuilder errorBuf = new StringBuilder(Messages.getString("MysqlIO.72")); + errorBuf.append(serverErrorMessage); + errorBuf.append("\""); + + throw SQLError.createSQLException(SQLError.get(SQLError.SQL_STATE_GENERAL_ERROR) + ", " + errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1, + false, getExceptionInterceptor(), this.connection); + } + } + + private void appendDeadlockStatusInformation(String xOpen, StringBuilder errorBuf) throws SQLException { + if (this.connection.getIncludeInnodbStatusInDeadlockExceptions() && xOpen != null && (xOpen.startsWith("40") || xOpen.startsWith("41")) + && this.streamingData == null) { + ResultSet rs = null; + + try { + rs = sqlQueryDirect(null, "SHOW ENGINE INNODB STATUS", this.connection.getEncoding(), null, -1, ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, false, this.connection.getCatalog(), null); + + if (rs.next()) { + errorBuf.append("\n\n"); + errorBuf.append(rs.getString("Status")); + } else { + errorBuf.append("\n\n"); + errorBuf.append(Messages.getString("MysqlIO.NoInnoDBStatusFound")); + } + } catch (Exception ex) { + errorBuf.append("\n\n"); + errorBuf.append(Messages.getString("MysqlIO.InnoDBStatusFailed")); + errorBuf.append("\n\n"); + errorBuf.append(Util.stackTraceToString(ex)); + } finally { + if (rs != null) { + rs.close(); + } + } + } + + if (this.connection.getIncludeThreadDumpInDeadlockExceptions()) { + errorBuf.append("\n\n*** Java threads running at time of deadlock ***\n\n"); + + ThreadMXBean threadMBean = ManagementFactory.getThreadMXBean(); + long[] threadIds = threadMBean.getAllThreadIds(); + + ThreadInfo[] threads = threadMBean.getThreadInfo(threadIds, Integer.MAX_VALUE); + List activeThreads = new ArrayList(); + + for (ThreadInfo info : threads) { + if (info != null) { + activeThreads.add(info); + } + } + + for (ThreadInfo threadInfo : activeThreads) { + // "Thread-60" daemon prio=1 tid=0x093569c0 nid=0x1b99 in Object.wait() + + errorBuf.append('"'); + errorBuf.append(threadInfo.getThreadName()); + errorBuf.append("\" tid="); + errorBuf.append(threadInfo.getThreadId()); + errorBuf.append(" "); + errorBuf.append(threadInfo.getThreadState()); + + if (threadInfo.getLockName() != null) { + errorBuf.append(" on lock=" + threadInfo.getLockName()); + } + if (threadInfo.isSuspended()) { + errorBuf.append(" (suspended)"); + } + if (threadInfo.isInNative()) { + errorBuf.append(" (running in native)"); + } + + StackTraceElement[] stackTrace = threadInfo.getStackTrace(); + + if (stackTrace.length > 0) { + errorBuf.append(" in "); + errorBuf.append(stackTrace[0].getClassName()); + errorBuf.append("."); + errorBuf.append(stackTrace[0].getMethodName()); + errorBuf.append("()"); + } + + errorBuf.append("\n"); + + if (threadInfo.getLockOwnerName() != null) { + errorBuf.append("\t owned by " + threadInfo.getLockOwnerName() + " Id=" + threadInfo.getLockOwnerId()); + errorBuf.append("\n"); + } + + for (int j = 0; j < stackTrace.length; j++) { + StackTraceElement ste = stackTrace[j]; + errorBuf.append("\tat " + ste.toString()); + errorBuf.append("\n"); + } + } + } + } + + /** + * Sends a large packet to the server as a series of smaller packets + * + * @param packet + * + * @throws SQLException + * @throws CommunicationsException + */ + private final void sendSplitPackets(Buffer packet, int packetLen) throws SQLException { + try { + Buffer packetToSend = (this.splitBufRef == null) ? null : this.splitBufRef.get(); + Buffer toCompress = (!this.useCompression || this.compressBufRef == null) ? null : this.compressBufRef.get(); + + // + // Store this packet in a soft reference...It can be re-used if not GC'd (so clients that use it frequently won't have to re-alloc the 16M buffer), + // but we don't penalize infrequent users of large packets by keeping 16M allocated all of the time + // + if (packetToSend == null) { + packetToSend = new Buffer((this.maxThreeBytes + HEADER_LENGTH)); + this.splitBufRef = new SoftReference(packetToSend); + } + if (this.useCompression) { + int cbuflen = packetLen + ((packetLen / this.maxThreeBytes) + 1) * HEADER_LENGTH; + if (toCompress == null) { + toCompress = new Buffer(cbuflen); + this.compressBufRef = new SoftReference(toCompress); + } else if (toCompress.getBufLength() < cbuflen) { + toCompress.setPosition(toCompress.getBufLength()); + toCompress.ensureCapacity(cbuflen - toCompress.getBufLength()); + } + } + + int len = packetLen - HEADER_LENGTH; // payload length left + int splitSize = this.maxThreeBytes; + int originalPacketPos = HEADER_LENGTH; + byte[] origPacketBytes = packet.getByteBuffer(); + + int toCompressPosition = 0; + + // split to MySQL packets + while (len >= 0) { + this.packetSequence++; + + if (len < splitSize) { + splitSize = len; + } + + packetToSend.setPosition(0); + packetToSend.writeLongInt(splitSize); + packetToSend.writeByte(this.packetSequence); + if (len > 0) { + System.arraycopy(origPacketBytes, originalPacketPos, packetToSend.getByteBuffer(), HEADER_LENGTH, splitSize); + } + + if (this.useCompression) { + System.arraycopy(packetToSend.getByteBuffer(), 0, toCompress.getByteBuffer(), toCompressPosition, HEADER_LENGTH + splitSize); + toCompressPosition += HEADER_LENGTH + splitSize; + } else { + this.mysqlOutput.write(packetToSend.getByteBuffer(), 0, HEADER_LENGTH + splitSize); + this.mysqlOutput.flush(); + } + + originalPacketPos += splitSize; + len -= this.maxThreeBytes; + + } + + // split to compressed packets + if (this.useCompression) { + len = toCompressPosition; + toCompressPosition = 0; + splitSize = this.maxThreeBytes - COMP_HEADER_LENGTH; + while (len >= 0) { + this.compressedPacketSequence++; + + if (len < splitSize) { + splitSize = len; + } + + Buffer compressedPacketToSend = compressPacket(toCompress, toCompressPosition, splitSize); + packetLen = compressedPacketToSend.getPosition(); + this.mysqlOutput.write(compressedPacketToSend.getByteBuffer(), 0, packetLen); + this.mysqlOutput.flush(); + + toCompressPosition += splitSize; + len -= (this.maxThreeBytes - COMP_HEADER_LENGTH); + } + } + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.lastPacketSentTimeMs, this.lastPacketReceivedTimeMs, ioEx, + getExceptionInterceptor()); + } + } + + private void reclaimLargeSharedSendPacket() { + if ((this.sharedSendPacket != null) && (this.sharedSendPacket.getCapacity() > 1048576)) { + this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE); + } + } + + boolean hadWarnings() { + return this.hadWarnings; + } + + void scanForAndThrowDataTruncation() throws SQLException { + if ((this.streamingData == null) && versionMeetsMinimum(4, 1, 0) && this.connection.getJdbcCompliantTruncation() && this.warningCount > 0) { + SQLError.convertShowWarningsToSQLWarnings(this.connection, this.warningCount, true); + } + } + + /** + * Secure authentication for 4.1 and newer servers. + * + * @param packet + * @param packLength + * @param user + * @param password + * @param database + * @param writeClientParams + * + * @throws SQLException + */ + private void secureAuth(Buffer packet, int packLength, String user, String password, String database, boolean writeClientParams) throws SQLException { + // Passwords can be 16 chars long + if (packet == null) { + packet = new Buffer(packLength); + } + + if (writeClientParams) { + if (this.use41Extensions) { + if (versionMeetsMinimum(4, 1, 1)) { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + + // charset, JDBC will connect as 'latin1', and use 'SET NAMES' to change to the desired charset after the connection is established. + packet.writeByte((byte) 8); + + // Set of bytes reserved for future use. + packet.writeBytesNoNull(new byte[23]); + } else { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + } + } else { + packet.writeInt((int) this.clientParam); + packet.writeLongInt(this.maxThreeBytes); + } + } + + // User/Password data + packet.writeString(user, CODE_PAGE_1252, this.connection); + + if (password.length() != 0) { + /* Prepare false scramble */ + packet.writeString(FALSE_SCRAMBLE, CODE_PAGE_1252, this.connection); + } else { + /* For empty password */ + packet.writeString("", CODE_PAGE_1252, this.connection); + } + + if (this.useConnectWithDb) { + packet.writeString(database, CODE_PAGE_1252, this.connection); + } + + send(packet, packet.getPosition()); + + // + // Don't continue stages if password is empty + // + if (password.length() > 0) { + Buffer b = readPacket(); + + b.setPosition(0); + + byte[] replyAsBytes = b.getByteBuffer(); + + if ((replyAsBytes.length == 24) && (replyAsBytes[0] != 0)) { + // Old passwords will have '*' at the first byte of hash */ + if (replyAsBytes[0] != '*') { + try { + /* Build full password hash as it is required to decode scramble */ + byte[] buff = Security.passwordHashStage1(password); + + /* Store copy as we'll need it later */ + byte[] passwordHash = new byte[buff.length]; + System.arraycopy(buff, 0, passwordHash, 0, buff.length); + + /* Finally hash complete password using hash we got from server */ + passwordHash = Security.passwordHashStage2(passwordHash, replyAsBytes); + + byte[] packetDataAfterSalt = new byte[replyAsBytes.length - 4]; + + System.arraycopy(replyAsBytes, 4, packetDataAfterSalt, 0, replyAsBytes.length - 4); + + byte[] mysqlScrambleBuff = new byte[SEED_LENGTH]; + + /* Decypt and store scramble 4 = hash for stage2 */ + Security.xorString(packetDataAfterSalt, mysqlScrambleBuff, passwordHash, SEED_LENGTH); + + /* Encode scramble with password. Recycle buffer */ + Security.xorString(mysqlScrambleBuff, buff, buff, SEED_LENGTH); + + Buffer packet2 = new Buffer(25); + packet2.writeBytesNoNull(buff); + + this.packetSequence++; + + send(packet2, 24); + } catch (NoSuchAlgorithmException nse) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.91") + Messages.getString("MysqlIO.92"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } else { + try { + /* Create password to decode scramble */ + byte[] passwordHash = Security.createKeyFromOldPassword(password); + + /* Decypt and store scramble 4 = hash for stage2 */ + byte[] netReadPos4 = new byte[replyAsBytes.length - 4]; + + System.arraycopy(replyAsBytes, 4, netReadPos4, 0, replyAsBytes.length - 4); + + byte[] mysqlScrambleBuff = new byte[SEED_LENGTH]; + + /* Decypt and store scramble 4 = hash for stage2 */ + Security.xorString(netReadPos4, mysqlScrambleBuff, passwordHash, SEED_LENGTH); + + /* Finally scramble decoded scramble with password */ + String scrambledPassword = Util.scramble(StringUtils.toString(mysqlScrambleBuff), password); + + Buffer packet2 = new Buffer(packLength); + packet2.writeString(scrambledPassword, CODE_PAGE_1252, this.connection); + this.packetSequence++; + + send(packet2, 24); + } catch (NoSuchAlgorithmException nse) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.91") + Messages.getString("MysqlIO.92"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + } + } + } + + /** + * Secure authentication for 4.1.1 and newer servers. + * + * @param packet + * @param packLength + * @param user + * @param password + * @param database + * @param writeClientParams + * + * @throws SQLException + */ + void secureAuth411(Buffer packet, int packLength, String user, String password, String database, boolean writeClientParams) throws SQLException { + String enc = getEncodingForHandshake(); + // SERVER: public_seed=create_random_string() + // send(public_seed) + // + // CLIENT: recv(public_seed) + // hash_stage1=sha1("password") + // hash_stage2=sha1(hash_stage1) + // reply=xor(hash_stage1, sha1(public_seed,hash_stage2) + // + // // this three steps are done in scramble() + // + // send(reply) + // + // + // SERVER: recv(reply) + // hash_stage1=xor(reply, sha1(public_seed,hash_stage2)) + // candidate_hash2=sha1(hash_stage1) + // check(candidate_hash2==hash_stage2) + // Passwords can be 16 chars long + if (packet == null) { + packet = new Buffer(packLength); + } + + if (writeClientParams) { + if (this.use41Extensions) { + if (versionMeetsMinimum(4, 1, 1)) { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + + appendCharsetByteForHandshake(packet, enc); + + // Set of bytes reserved for future use. + packet.writeBytesNoNull(new byte[23]); + } else { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + } + } else { + packet.writeInt((int) this.clientParam); + packet.writeLongInt(this.maxThreeBytes); + } + } + + // User/Password data + if (user != null) { + packet.writeString(user, enc, this.connection); + } + + if (password.length() != 0) { + packet.writeByte((byte) 0x14); + + try { + packet.writeBytesNoNull(Security.scramble411(password, this.seed, this.connection.getPasswordCharacterEncoding())); + } catch (NoSuchAlgorithmException nse) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.91") + Messages.getString("MysqlIO.92"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } catch (UnsupportedEncodingException e) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.91") + Messages.getString("MysqlIO.92"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } else { + /* For empty password */ + packet.writeByte((byte) 0); + } + + if (this.useConnectWithDb) { + packet.writeString(database, enc, this.connection); + } else { + /* For empty database */ + packet.writeByte((byte) 0); + } + + // connection attributes + if ((this.serverCapabilities & CLIENT_CONNECT_ATTRS) != 0) { + sendConnectionAttributes(packet, enc, this.connection); + } + + send(packet, packet.getPosition()); + + byte savePacketSequence = this.packetSequence++; + + Buffer reply = checkErrorPacket(); + + if (reply.isAuthMethodSwitchRequestPacket()) { + /* + * By sending this very specific reply server asks us to send scrambled password in old format. The reply contains scramble_323. + */ + this.packetSequence = ++savePacketSequence; + packet.clear(); + + String seed323 = this.seed.substring(0, 8); + packet.writeString(Util.newCrypt(password, seed323, this.connection.getPasswordCharacterEncoding())); + send(packet, packet.getPosition()); + + /* Read what server thinks about out new auth message report */ + checkErrorPacket(); + } + } + + /** + * Un-packs binary-encoded result set data for one row + * + * @param fields + * @param binaryData + * @param resultSetConcurrency + * + * @return byte[][] + * + * @throws SQLException + */ + private final ResultSetRow unpackBinaryResultSetRow(Field[] fields, Buffer binaryData, int resultSetConcurrency) throws SQLException { + int numFields = fields.length; + + byte[][] unpackedRowData = new byte[numFields][]; + + // + // Unpack the null bitmask, first + // + + int nullCount = (numFields + 9) / 8; + int nullMaskPos = binaryData.getPosition(); + binaryData.setPosition(nullMaskPos + nullCount); + int bit = 4; // first two bits are reserved for future use + + // + // TODO: Benchmark if moving check for updatable result sets out of loop is worthwhile? + // + + for (int i = 0; i < numFields; i++) { + if ((binaryData.readByte(nullMaskPos) & bit) != 0) { + unpackedRowData[i] = null; + } else { + if (resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) { + extractNativeEncodedColumn(binaryData, fields, i, unpackedRowData); + } else { + unpackNativeEncodedColumn(binaryData, fields, i, unpackedRowData); + } + } + + if (((bit <<= 1) & 255) == 0) { + bit = 1; /* To next byte */ + + nullMaskPos++; + } + } + + return new ByteArrayRow(unpackedRowData, getExceptionInterceptor()); + } + + private final void extractNativeEncodedColumn(Buffer binaryData, Field[] fields, int columnIndex, byte[][] unpackedRowData) throws SQLException { + Field curField = fields[columnIndex]; + + switch (curField.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_NULL: + break; // for dummy binds + + case MysqlDefs.FIELD_TYPE_TINY: + + unpackedRowData[columnIndex] = new byte[] { binaryData.readByte() }; + break; + + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + + unpackedRowData[columnIndex] = binaryData.getBytes(2); + break; + case MysqlDefs.FIELD_TYPE_LONG: + case MysqlDefs.FIELD_TYPE_INT24: + + unpackedRowData[columnIndex] = binaryData.getBytes(4); + break; + case MysqlDefs.FIELD_TYPE_LONGLONG: + + unpackedRowData[columnIndex] = binaryData.getBytes(8); + break; + case MysqlDefs.FIELD_TYPE_FLOAT: + + unpackedRowData[columnIndex] = binaryData.getBytes(4); + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + + unpackedRowData[columnIndex] = binaryData.getBytes(8); + break; + case MysqlDefs.FIELD_TYPE_TIME: + + int length = (int) binaryData.readFieldLength(); + + unpackedRowData[columnIndex] = binaryData.getBytes(length); + + break; + case MysqlDefs.FIELD_TYPE_DATE: + + length = (int) binaryData.readFieldLength(); + + unpackedRowData[columnIndex] = binaryData.getBytes(length); + + break; + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + length = (int) binaryData.readFieldLength(); + + unpackedRowData[columnIndex] = binaryData.getBytes(length); + break; + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + case MysqlDefs.FIELD_TYPE_GEOMETRY: + case MysqlDefs.FIELD_TYPE_BIT: + case MysqlDefs.FIELD_TYPE_JSON: + unpackedRowData[columnIndex] = binaryData.readLenByteArray(0); + + break; + default: + throw SQLError.createSQLException( + Messages.getString("MysqlIO.97") + curField.getMysqlType() + Messages.getString("MysqlIO.98") + columnIndex + + Messages.getString("MysqlIO.99") + fields.length + Messages.getString("MysqlIO.100"), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + private final void unpackNativeEncodedColumn(Buffer binaryData, Field[] fields, int columnIndex, byte[][] unpackedRowData) throws SQLException { + Field curField = fields[columnIndex]; + + switch (curField.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_NULL: + break; // for dummy binds + + case MysqlDefs.FIELD_TYPE_TINY: + + byte tinyVal = binaryData.readByte(); + + if (!curField.isUnsigned()) { + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(tinyVal)); + } else { + short unsignedTinyVal = (short) (tinyVal & 0xff); + + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(unsignedTinyVal)); + } + + break; + + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + + short shortVal = (short) binaryData.readInt(); + + if (!curField.isUnsigned()) { + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(shortVal)); + } else { + int unsignedShortVal = shortVal & 0xffff; + + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(unsignedShortVal)); + } + + break; + + case MysqlDefs.FIELD_TYPE_LONG: + case MysqlDefs.FIELD_TYPE_INT24: + + int intVal = (int) binaryData.readLong(); + + if (!curField.isUnsigned()) { + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(intVal)); + } else { + long longVal = intVal & 0xffffffffL; + + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(longVal)); + } + + break; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + + long longVal = binaryData.readLongLong(); + + if (!curField.isUnsigned()) { + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(longVal)); + } else { + BigInteger asBigInteger = ResultSetImpl.convertLongToUlong(longVal); + + unpackedRowData[columnIndex] = StringUtils.getBytes(asBigInteger.toString()); + } + + break; + + case MysqlDefs.FIELD_TYPE_FLOAT: + + float floatVal = Float.intBitsToFloat(binaryData.readIntAsLong()); + + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(floatVal)); + + break; + + case MysqlDefs.FIELD_TYPE_DOUBLE: + + double doubleVal = Double.longBitsToDouble(binaryData.readLongLong()); + + unpackedRowData[columnIndex] = StringUtils.getBytes(String.valueOf(doubleVal)); + + break; + + case MysqlDefs.FIELD_TYPE_TIME: + + int length = (int) binaryData.readFieldLength(); + + int hour = 0; + int minute = 0; + int seconds = 0; + + if (length != 0) { + binaryData.readByte(); // skip tm->neg + binaryData.readLong(); // skip daysPart + hour = binaryData.readByte(); + minute = binaryData.readByte(); + seconds = binaryData.readByte(); + + if (length > 8) { + binaryData.readLong(); // ignore 'secondsPart' + } + } + + byte[] timeAsBytes = new byte[8]; + + timeAsBytes[0] = (byte) Character.forDigit(hour / 10, 10); + timeAsBytes[1] = (byte) Character.forDigit(hour % 10, 10); + + timeAsBytes[2] = (byte) ':'; + + timeAsBytes[3] = (byte) Character.forDigit(minute / 10, 10); + timeAsBytes[4] = (byte) Character.forDigit(minute % 10, 10); + + timeAsBytes[5] = (byte) ':'; + + timeAsBytes[6] = (byte) Character.forDigit(seconds / 10, 10); + timeAsBytes[7] = (byte) Character.forDigit(seconds % 10, 10); + + unpackedRowData[columnIndex] = timeAsBytes; + + break; + + case MysqlDefs.FIELD_TYPE_DATE: + length = (int) binaryData.readFieldLength(); + + int year = 0; + int month = 0; + int day = 0; + + hour = 0; + minute = 0; + seconds = 0; + + if (length != 0) { + year = binaryData.readInt(); + month = binaryData.readByte(); + day = binaryData.readByte(); + } + + if ((year == 0) && (month == 0) && (day == 0)) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(this.connection.getZeroDateTimeBehavior())) { + unpackedRowData[columnIndex] = null; + + break; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(this.connection.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + year = 1; + month = 1; + day = 1; + } + + byte[] dateAsBytes = new byte[10]; + + dateAsBytes[0] = (byte) Character.forDigit(year / 1000, 10); + + int after1000 = year % 1000; + + dateAsBytes[1] = (byte) Character.forDigit(after1000 / 100, 10); + + int after100 = after1000 % 100; + + dateAsBytes[2] = (byte) Character.forDigit(after100 / 10, 10); + dateAsBytes[3] = (byte) Character.forDigit(after100 % 10, 10); + + dateAsBytes[4] = (byte) '-'; + + dateAsBytes[5] = (byte) Character.forDigit(month / 10, 10); + dateAsBytes[6] = (byte) Character.forDigit(month % 10, 10); + + dateAsBytes[7] = (byte) '-'; + + dateAsBytes[8] = (byte) Character.forDigit(day / 10, 10); + dateAsBytes[9] = (byte) Character.forDigit(day % 10, 10); + + unpackedRowData[columnIndex] = dateAsBytes; + + break; + + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + length = (int) binaryData.readFieldLength(); + + year = 0; + month = 0; + day = 0; + + hour = 0; + minute = 0; + seconds = 0; + + int nanos = 0; + + if (length != 0) { + year = binaryData.readInt(); + month = binaryData.readByte(); + day = binaryData.readByte(); + + if (length > 4) { + hour = binaryData.readByte(); + minute = binaryData.readByte(); + seconds = binaryData.readByte(); + } + + //if (length > 7) { + // nanos = (int)binaryData.readLong(); + //} + } + + if ((year == 0) && (month == 0) && (day == 0)) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(this.connection.getZeroDateTimeBehavior())) { + unpackedRowData[columnIndex] = null; + + break; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(this.connection.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Timestamp", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + year = 1; + month = 1; + day = 1; + } + + int stringLength = 19; + + byte[] nanosAsBytes = StringUtils.getBytes(Integer.toString(nanos)); + + stringLength += (1 + nanosAsBytes.length); // '.' + # of digits + + byte[] datetimeAsBytes = new byte[stringLength]; + + datetimeAsBytes[0] = (byte) Character.forDigit(year / 1000, 10); + + after1000 = year % 1000; + + datetimeAsBytes[1] = (byte) Character.forDigit(after1000 / 100, 10); + + after100 = after1000 % 100; + + datetimeAsBytes[2] = (byte) Character.forDigit(after100 / 10, 10); + datetimeAsBytes[3] = (byte) Character.forDigit(after100 % 10, 10); + + datetimeAsBytes[4] = (byte) '-'; + + datetimeAsBytes[5] = (byte) Character.forDigit(month / 10, 10); + datetimeAsBytes[6] = (byte) Character.forDigit(month % 10, 10); + + datetimeAsBytes[7] = (byte) '-'; + + datetimeAsBytes[8] = (byte) Character.forDigit(day / 10, 10); + datetimeAsBytes[9] = (byte) Character.forDigit(day % 10, 10); + + datetimeAsBytes[10] = (byte) ' '; + + datetimeAsBytes[11] = (byte) Character.forDigit(hour / 10, 10); + datetimeAsBytes[12] = (byte) Character.forDigit(hour % 10, 10); + + datetimeAsBytes[13] = (byte) ':'; + + datetimeAsBytes[14] = (byte) Character.forDigit(minute / 10, 10); + datetimeAsBytes[15] = (byte) Character.forDigit(minute % 10, 10); + + datetimeAsBytes[16] = (byte) ':'; + + datetimeAsBytes[17] = (byte) Character.forDigit(seconds / 10, 10); + datetimeAsBytes[18] = (byte) Character.forDigit(seconds % 10, 10); + + datetimeAsBytes[19] = (byte) '.'; + + final int nanosOffset = 20; + + System.arraycopy(nanosAsBytes, 0, datetimeAsBytes, nanosOffset, nanosAsBytes.length); + + unpackedRowData[columnIndex] = datetimeAsBytes; + + break; + + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + case MysqlDefs.FIELD_TYPE_BIT: + case MysqlDefs.FIELD_TYPE_JSON: + unpackedRowData[columnIndex] = binaryData.readLenByteArray(0); + + break; + + default: + throw SQLError.createSQLException( + Messages.getString("MysqlIO.97") + curField.getMysqlType() + Messages.getString("MysqlIO.98") + columnIndex + + Messages.getString("MysqlIO.99") + fields.length + Messages.getString("MysqlIO.100"), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + /** + * Negotiates the SSL communications channel used when connecting + * to a MySQL server that understands SSL. + * + * @param user + * @param password + * @param database + * @param packLength + * @throws SQLException + * @throws CommunicationsException + */ + private void negotiateSSLConnection(String user, String password, String database, int packLength) throws SQLException { + if (!ExportControlled.enabled()) { + throw new ConnectionFeatureNotAvailableException(this.connection, this.lastPacketSentTimeMs, null); + } + + if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { + this.clientParam |= CLIENT_SECURE_CONNECTION; + } + + this.clientParam |= CLIENT_SSL; + + Buffer packet = new Buffer(packLength); + + if (this.use41Extensions) { + packet.writeLong(this.clientParam); + packet.writeLong(this.maxThreeBytes); + appendCharsetByteForHandshake(packet, getEncodingForHandshake()); + packet.writeBytesNoNull(new byte[23]); // Set of bytes reserved for future use. + } else { + packet.writeInt((int) this.clientParam); + } + + send(packet, packet.getPosition()); + + ExportControlled.transformSocketToSSLSocket(this); + } + + public boolean isSSLEstablished() { + return ExportControlled.enabled() && ExportControlled.isSSLEstablished(this); + } + + protected int getServerStatus() { + return this.serverStatus; + } + + protected List fetchRowsViaCursor(List fetchedRows, long statementId, Field[] columnTypes, int fetchSize, + boolean useBufferRowExplicit) throws SQLException { + + if (fetchedRows == null) { + fetchedRows = new ArrayList(fetchSize); + } else { + fetchedRows.clear(); + } + + this.sharedSendPacket.clear(); + + this.sharedSendPacket.writeByte((byte) MysqlDefs.COM_FETCH); + this.sharedSendPacket.writeLong(statementId); + this.sharedSendPacket.writeLong(fetchSize); + + sendCommand(MysqlDefs.COM_FETCH, null, this.sharedSendPacket, true, null, 0); + + ResultSetRow row = null; + + while ((row = nextRow(columnTypes, columnTypes.length, true, ResultSet.CONCUR_READ_ONLY, false, useBufferRowExplicit, false, null)) != null) { + fetchedRows.add(row); + } + + return fetchedRows; + } + + protected long getThreadId() { + return this.threadId; + } + + protected boolean useNanosForElapsedTime() { + return this.useNanosForElapsedTime; + } + + protected long getSlowQueryThreshold() { + return this.slowQueryThreshold; + } + + protected String getQueryTimingUnits() { + return this.queryTimingUnits; + } + + protected int getCommandCount() { + return this.commandCount; + } + + private void checkTransactionState(int oldStatus) throws SQLException { + boolean previouslyInTrans = (oldStatus & SERVER_STATUS_IN_TRANS) != 0; + boolean currentlyInTrans = inTransactionOnServer(); + + if (previouslyInTrans && !currentlyInTrans) { + this.connection.transactionCompleted(); + } else if (!previouslyInTrans && currentlyInTrans) { + this.connection.transactionBegun(); + } + } + + private void preserveOldTransactionState() { + this.serverStatus |= (this.oldServerStatus & SERVER_STATUS_IN_TRANS); + } + + protected void setStatementInterceptors(List statementInterceptors) { + this.statementInterceptors = statementInterceptors.isEmpty() ? null : statementInterceptors; + } + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + protected void setSocketTimeout(int milliseconds) throws SQLException { + try { + this.mysqlConnection.setSoTimeout(milliseconds); + } catch (SocketException e) { + SQLException sqlEx = SQLError.createSQLException("Invalid socket timeout value or state", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + } + + protected void releaseResources() { + if (this.deflater != null) { + this.deflater.end(); + this.deflater = null; + } + } + + /** + * Get the Java encoding to be used for the handshake + * response. Defaults to UTF-8. + */ + String getEncodingForHandshake() { + String enc = this.connection.getEncoding(); + if (enc == null) { + enc = "UTF-8"; + } + return enc; + } + + /** + * Append the MySQL collation index to the handshake packet. A + * single byte will be added to the packet corresponding to the + * collation index found for the requested Java encoding name. + * + * If the index is > 255 which may be valid at some point in + * the future, an exception will be thrown. At the time of this + * implementation the index cannot be > 255 and only the + * COM_CHANGE_USER rpc, not the handshake response, can handle a + * value > 255. + * + * @param packet + * to append to + * @param end + * The Java encoding name used to lookup the collation index + */ + private void appendCharsetByteForHandshake(Buffer packet, String enc) throws SQLException { + int charsetIndex = 0; + if (enc != null) { + charsetIndex = CharsetMapping.getCollationIndexForJavaEncoding(enc, this.connection); + } + if (charsetIndex == 0) { + charsetIndex = CharsetMapping.MYSQL_COLLATION_INDEX_utf8; + } + if (charsetIndex > 255) { + throw SQLError.createSQLException("Invalid character set index for encoding: " + enc, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + packet.writeByte((byte) charsetIndex); + } + + public boolean isEOFDeprecated() { + return (this.clientParam & CLIENT_DEPRECATE_EOF) != 0; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlParameterMetadata.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlParameterMetadata.java new file mode 100644 index 0000000..1f21e30 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlParameterMetadata.java @@ -0,0 +1,183 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.ParameterMetaData; +import java.sql.SQLException; +import java.sql.Types; + +public class MysqlParameterMetadata implements ParameterMetaData { + boolean returnSimpleMetadata = false; + + ResultSetMetaData metadata = null; + + int parameterCount = 0; + + private ExceptionInterceptor exceptionInterceptor; + + MysqlParameterMetadata(Field[] fieldInfo, int parameterCount, ExceptionInterceptor exceptionInterceptor) { + this.metadata = new ResultSetMetaData(fieldInfo, false, true, exceptionInterceptor); + + this.parameterCount = parameterCount; + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Used for "fake" basic metadata for client-side prepared statements when + * we don't know the parameter types. + * + * @param parameterCount + */ + MysqlParameterMetadata(int count) { + this.parameterCount = count; + this.returnSimpleMetadata = true; + } + + public int getParameterCount() throws SQLException { + return this.parameterCount; + } + + public int isNullable(int arg0) throws SQLException { + checkAvailable(); + + return this.metadata.isNullable(arg0); + } + + private void checkAvailable() throws SQLException { + if (this.metadata == null || this.metadata.fields == null) { + throw SQLError.createSQLException("Parameter metadata not available for the given statement", SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, + this.exceptionInterceptor); + } + } + + public boolean isSigned(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return false; + } + + checkAvailable(); + + return (this.metadata.isSigned(arg0)); + } + + public int getPrecision(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getPrecision(arg0)); + } + + public int getScale(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getScale(arg0)); + } + + public int getParameterType(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return Types.VARCHAR; + } + + checkAvailable(); + + return (this.metadata.getColumnType(arg0)); + } + + public String getParameterTypeName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return "VARCHAR"; + } + + checkAvailable(); + + return (this.metadata.getColumnTypeName(arg0)); + } + + public String getParameterClassName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return "java.lang.String"; + } + + checkAvailable(); + + return (this.metadata.getColumnClassName(arg0)); + } + + public int getParameterMode(int arg0) throws SQLException { + return parameterModeIn; + } + + private void checkBounds(int paramNumber) throws SQLException { + if (paramNumber < 1) { + throw SQLError.createSQLException("Parameter index of '" + paramNumber + "' is invalid.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + if (paramNumber > this.parameterCount) { + throw SQLError.createSQLException( + "Parameter index of '" + paramNumber + "' is greater than number of parameters, which is '" + this.parameterCount + "'.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + + } + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlSavepoint.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlSavepoint.java new file mode 100644 index 0000000..9f5b30c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/MysqlSavepoint.java @@ -0,0 +1,104 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.rmi.server.UID; +import java.sql.SQLException; +import java.sql.Savepoint; + +/** + * Represents SQL SAVEPOINTS in MySQL. + */ +public class MysqlSavepoint implements Savepoint { + private static String getUniqueId() { + // no need to re-invent the wheel here... + String uidStr = new UID().toString(); + + int uidLength = uidStr.length(); + + StringBuilder safeString = new StringBuilder(uidLength + 1); + safeString.append('_'); + + for (int i = 0; i < uidLength; i++) { + char c = uidStr.charAt(i); + + if (Character.isLetter(c) || Character.isDigit(c)) { + safeString.append(c); + } else { + safeString.append('_'); + } + } + + return safeString.toString(); + } + + private String savepointName; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates an unnamed savepoint. + * + * @param conn + * + * @throws SQLException + * if an error occurs + */ + MysqlSavepoint(ExceptionInterceptor exceptionInterceptor) throws SQLException { + this(getUniqueId(), exceptionInterceptor); + } + + /** + * Creates a named savepoint + * + * @param name + * the name of the savepoint. + * + * @throws SQLException + * if name == null or is empty. + */ + MysqlSavepoint(String name, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (name == null || name.length() == 0) { + throw SQLError.createSQLException("Savepoint name can not be NULL or empty", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + this.savepointName = name; + + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * @see java.sql.Savepoint#getSavepointId() + */ + public int getSavepointId() throws SQLException { + throw SQLError.createSQLException("Only named savepoints are supported.", SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); + } + + /** + * @see java.sql.Savepoint#getSavepointName() + */ + public String getSavepointName() throws SQLException { + return this.savepointName; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NamedPipeSocketFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NamedPipeSocketFactory.java new file mode 100644 index 0000000..ba577e9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NamedPipeSocketFactory.java @@ -0,0 +1,228 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.Socket; +import java.net.SocketException; +import java.sql.SQLException; +import java.util.Properties; + +/** + * A socket factory for named pipes (on Windows) + */ +public class NamedPipeSocketFactory implements SocketFactory, SocketMetadata { + /** + * A socket that encapsulates named pipes on Windows + */ + class NamedPipeSocket extends Socket { + private boolean isClosed = false; + + private RandomAccessFile namedPipeFile; + + NamedPipeSocket(String filePath) throws IOException { + if ((filePath == null) || (filePath.length() == 0)) { + throw new IOException(Messages.getString("NamedPipeSocketFactory.4")); + } + + this.namedPipeFile = new RandomAccessFile(filePath, "rw"); + } + + /** + * @see java.net.Socket#close() + */ + @Override + public synchronized void close() throws IOException { + this.namedPipeFile.close(); + this.isClosed = true; + } + + /** + * @see java.net.Socket#getInputStream() + */ + @Override + public InputStream getInputStream() throws IOException { + return new RandomAccessFileInputStream(this.namedPipeFile); + } + + /** + * @see java.net.Socket#getOutputStream() + */ + @Override + public OutputStream getOutputStream() throws IOException { + return new RandomAccessFileOutputStream(this.namedPipeFile); + } + + /** + * @see java.net.Socket#isClosed() + */ + @Override + public boolean isClosed() { + return this.isClosed; + } + } + + /** + * Enables OutputStream-type functionality for a RandomAccessFile + */ + class RandomAccessFileInputStream extends InputStream { + RandomAccessFile raFile; + + RandomAccessFileInputStream(RandomAccessFile file) { + this.raFile = file; + } + + /** + * @see java.io.InputStream#available() + */ + @Override + public int available() throws IOException { + return -1; + } + + /** + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + this.raFile.close(); + } + + /** + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException { + return this.raFile.read(); + } + + /** + * @see java.io.InputStream#read(byte[]) + */ + @Override + public int read(byte[] b) throws IOException { + return this.raFile.read(b); + } + + /** + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + return this.raFile.read(b, off, len); + } + } + + /** + * Enables OutputStream-type functionality for a RandomAccessFile + */ + class RandomAccessFileOutputStream extends OutputStream { + RandomAccessFile raFile; + + RandomAccessFileOutputStream(RandomAccessFile file) { + this.raFile = file; + } + + /** + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException { + this.raFile.close(); + } + + /** + * @see java.io.OutputStream#write(byte[]) + */ + @Override + public void write(byte[] b) throws IOException { + this.raFile.write(b); + } + + /** + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + this.raFile.write(b, off, len); + } + + /** + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException { + } + } + + public static final String NAMED_PIPE_PROP_NAME = "namedPipePath"; + + private Socket namedPipeSocket; + + /** + * Constructor for NamedPipeSocketFactory. + */ + public NamedPipeSocketFactory() { + super(); + } + + /** + * @see com.mysql.jdbc.SocketFactory#afterHandshake() + */ + public Socket afterHandshake() throws SocketException, IOException { + return this.namedPipeSocket; + } + + /** + * @see com.mysql.jdbc.SocketFactory#beforeHandshake() + */ + public Socket beforeHandshake() throws SocketException, IOException { + return this.namedPipeSocket; + } + + /** + * @see com.mysql.jdbc.SocketFactory#connect(String, Properties) + */ + public Socket connect(String host, int portNumber /* ignored */, Properties props) throws SocketException, IOException { + String namedPipePath = props.getProperty(NAMED_PIPE_PROP_NAME); + + if (namedPipePath == null) { + namedPipePath = "\\\\.\\pipe\\MySQL"; + } else if (namedPipePath.length() == 0) { + throw new SocketException(Messages.getString("NamedPipeSocketFactory.2") + NAMED_PIPE_PROP_NAME + Messages.getString("NamedPipeSocketFactory.3")); + } + + this.namedPipeSocket = new NamedPipeSocket(namedPipePath); + + return this.namedPipeSocket; + } + + public boolean isLocallyConnected(ConnectionImpl conn) throws SQLException { + // Until I learn otherwise (or learn how to detect it), I assume that we are + return true; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NdbLoadBalanceExceptionChecker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NdbLoadBalanceExceptionChecker.java new file mode 100644 index 0000000..957ea11 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NdbLoadBalanceExceptionChecker.java @@ -0,0 +1,40 @@ +/* + Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public class NdbLoadBalanceExceptionChecker extends StandardLoadBalanceExceptionChecker { + + @Override + public boolean shouldExceptionTriggerFailover(SQLException ex) { + return super.shouldExceptionTriggerFailover(ex) || checkNdbException(ex); + } + + private boolean checkNdbException(SQLException ex) { + // Have to parse the message since most NDB errors are mapped to the same DEMC, sadly. + return (ex.getMessage().startsWith("Lock wait timeout exceeded") + || (ex.getMessage().startsWith("Got temporary error") && ex.getMessage().endsWith("from NDB"))); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NetworkResources.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NetworkResources.java new file mode 100644 index 0000000..27d625b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NetworkResources.java @@ -0,0 +1,90 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +class NetworkResources { + private final Socket mysqlConnection; + private final InputStream mysqlInput; + private final OutputStream mysqlOutput; + + protected NetworkResources(Socket mysqlConnection, InputStream mysqlInput, OutputStream mysqlOutput) { + this.mysqlConnection = mysqlConnection; + this.mysqlInput = mysqlInput; + this.mysqlOutput = mysqlOutput; + } + + /** + * Forcibly closes the underlying socket to MySQL. + */ + protected final void forceClose() { + try { + try { + if (this.mysqlInput != null) { + this.mysqlInput.close(); + } + } finally { + if (this.mysqlConnection != null && !this.mysqlConnection.isClosed() && !this.mysqlConnection.isInputShutdown()) { + try { + this.mysqlConnection.shutdownInput(); + } catch (UnsupportedOperationException ex) { + // ignore, some sockets do not support this method + } + } + } + } catch (IOException ioEx) { + // we can't do anything constructive about this + } + + try { + try { + if (this.mysqlOutput != null) { + this.mysqlOutput.close(); + } + } finally { + if (this.mysqlConnection != null && !this.mysqlConnection.isClosed() && !this.mysqlConnection.isOutputShutdown()) { + try { + this.mysqlConnection.shutdownOutput(); + } catch (UnsupportedOperationException ex) { + // ignore, some sockets do not support this method + } + } + } + } catch (IOException ioEx) { + // we can't do anything constructive about this + } + + try { + if (this.mysqlConnection != null) { + this.mysqlConnection.close(); + } + } catch (IOException ioEx) { + // we can't do anything constructive about this + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NoSubInterceptorWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NoSubInterceptorWrapper.java new file mode 100644 index 0000000..447032f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NoSubInterceptorWrapper.java @@ -0,0 +1,73 @@ +/* + Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +/** + * Wraps statement interceptors during driver startup so that they don't produce different result sets than we expect. + */ +public class NoSubInterceptorWrapper implements StatementInterceptorV2 { + + private final StatementInterceptorV2 underlyingInterceptor; + + public NoSubInterceptorWrapper(StatementInterceptorV2 underlyingInterceptor) { + if (underlyingInterceptor == null) { + throw new RuntimeException("Interceptor to be wrapped can not be NULL"); + } + + this.underlyingInterceptor = underlyingInterceptor; + } + + public void destroy() { + this.underlyingInterceptor.destroy(); + } + + public boolean executeTopLevelOnly() { + return this.underlyingInterceptor.executeTopLevelOnly(); + } + + public void init(Connection conn, Properties props) throws SQLException { + this.underlyingInterceptor.init(conn, props); + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection, + int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException { + this.underlyingInterceptor.postProcess(sql, interceptedStatement, originalResultSet, connection, warningCount, noIndexUsed, noGoodIndexUsed, + statementException); + + return null; // don't allow result set substitution + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + this.underlyingInterceptor.preProcess(sql, interceptedStatement, connection); + + return null; // don't allow result set substitution + } + + public StatementInterceptorV2 getUnderlyingInterceptor() { + return this.underlyingInterceptor; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringDriver.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringDriver.java new file mode 100644 index 0000000..625325d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringDriver.java @@ -0,0 +1,909 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.net.URLDecoder; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface + * + *

    + * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to + * connect to the target URL. + *

    + * + *

    + * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast + * quantities of supporting code. + *

    + * + *

    + * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a + * driver by doing Class.forName("foo.bah.Driver") + *

    + */ +public class NonRegisteringDriver implements java.sql.Driver { + private static final String ALLOWED_QUOTES = "\"'"; + + private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; + + private static final String URL_PREFIX = "jdbc:mysql://"; + + private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; + + public static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; + + protected static final ConcurrentHashMap connectionPhantomRefs = new ConcurrentHashMap(); + + protected static final ReferenceQueue refQueue = new ReferenceQueue(); + + public static final String OS = getOSName(); + public static final String PLATFORM = getPlatform(); + public static final String LICENSE = "@MYSQL_CJ_LICENSE_TYPE@"; + public static final String RUNTIME_VENDOR = System.getProperty("java.vendor"); + public static final String RUNTIME_VERSION = System.getProperty("java.version"); + public static final String VERSION = "@MYSQL_CJ_VERSION@"; + public static final String NAME = "@MYSQL_CJ_DISPLAY_PROD_NAME@"; + + /* + * Standardizes OS name information to align with other drivers/clients + * for MySQL connection attributes + * + * @return the transformed, standardized OS name + */ + public static String getOSName() { + return System.getProperty("os.name"); + } + + /* + * Standardizes platform information to align with other drivers/clients + * for MySQL connection attributes + * + * @return the transformed, standardized platform details + */ + public static String getPlatform() { + return System.getProperty("os.arch"); + } + + static { + AbandonedConnectionCleanupThread referenceThread = new AbandonedConnectionCleanupThread(); + referenceThread.setDaemon(true); + referenceThread.start(); + } + + /** + * Key used to retreive the database value from the properties instance + * passed to the driver. + */ + public static final String DBNAME_PROPERTY_KEY = "DBNAME"; + + /** Should the driver generate debugging output? */ + public static final boolean DEBUG = false; + + /** Index for hostname coming out of parseHostPortPair(). */ + public final static int HOST_NAME_INDEX = 0; + + /** + * Key used to retreive the hostname value from the properties instance + * passed to the driver. + */ + public static final String HOST_PROPERTY_KEY = "HOST"; + + public static final String NUM_HOSTS_PROPERTY_KEY = "NUM_HOSTS"; + + /** + * Key used to retreive the password value from the properties instance + * passed to the driver. + */ + public static final String PASSWORD_PROPERTY_KEY = "password"; + + /** Index for port # coming out of parseHostPortPair(). */ + public final static int PORT_NUMBER_INDEX = 1; + + /** + * Key used to retreive the port number value from the properties instance + * passed to the driver. + */ + public static final String PORT_PROPERTY_KEY = "PORT"; + + public static final String PROPERTIES_TRANSFORM_KEY = "propertiesTransform"; + + /** Should the driver generate method-call traces? */ + public static final boolean TRACE = false; + + public static final String USE_CONFIG_PROPERTY_KEY = "useConfigs"; + + /** + * Key used to retreive the username value from the properties instance + * passed to the driver. + */ + public static final String USER_PROPERTY_KEY = "user"; + + public static final String PROTOCOL_PROPERTY_KEY = "PROTOCOL"; + + public static final String PATH_PROPERTY_KEY = "PATH"; + + /** + * Gets the drivers major version number + * + * @return the drivers major version number + */ + static int getMajorVersionInternal() { + return safeIntParse("@MYSQL_CJ_MAJOR_VERSION@"); + } + + /** + * Get the drivers minor version number + * + * @return the drivers minor version number + */ + static int getMinorVersionInternal() { + return safeIntParse("@MYSQL_CJ_MINOR_VERSION@"); + } + + /** + * Parses hostPortPair in the form of [host][:port] into an array, with the + * element of index HOST_NAME_INDEX being the host (or null if not + * specified), and the element of index PORT_NUMBER_INDEX being the port (or + * null if not specified). + * + * @param hostPortPair + * host and port in form of of [host][:port] + * + * @return array containing host and port as Strings + * + * @throws SQLException + * if a parse error occurs + */ + protected static String[] parseHostPortPair(String hostPortPair) throws SQLException { + + String[] splitValues = new String[2]; + + if (StringUtils.startsWithIgnoreCaseAndWs(hostPortPair, "address=")) { + splitValues[HOST_NAME_INDEX] = hostPortPair.trim(); + splitValues[PORT_NUMBER_INDEX] = null; + + return splitValues; + } + + int portIndex = hostPortPair.indexOf(":"); + + String hostname = null; + + if (portIndex != -1) { + if ((portIndex + 1) < hostPortPair.length()) { + String portAsString = hostPortPair.substring(portIndex + 1); + hostname = hostPortPair.substring(0, portIndex); + + splitValues[HOST_NAME_INDEX] = hostname; + + splitValues[PORT_NUMBER_INDEX] = portAsString; + } else { + throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.37"), SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } + } else { + splitValues[HOST_NAME_INDEX] = hostPortPair; + splitValues[PORT_NUMBER_INDEX] = null; + } + + return splitValues; + } + + private static int safeIntParse(String intAsString) { + try { + return Integer.parseInt(intAsString); + } catch (NumberFormatException nfe) { + return 0; + } + } + + /** + * Construct a new driver and register it with DriverManager + * + * @throws SQLException + * if a database error occurs. + */ + public NonRegisteringDriver() throws SQLException { + // Required for Class.forName().newInstance() + } + + /** + * Typically, drivers will return true if they understand the subprotocol + * specified in the URL and false if they don't. This driver's protocols + * start with jdbc:mysql: + * + * @param url + * the URL of the driver + * + * @return true if this driver accepts the given URL + * + * @exception SQLException + * if a database access error occurs or the url is null + * + * @see java.sql.Driver#acceptsURL + */ + public boolean acceptsURL(String url) throws SQLException { + if (url == null) { + throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); + } + return (parseURL(url, null) != null); + } + + // + // return the database name property + // + + /** + * Try to make a database connection to the given URL. The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given + * URL. This will be common, as when the JDBC driverManager is asked to connect to a given URL, it passes the URL to each loaded driver in turn. + * + *

    + * The driver should raise an SQLException if the URL is null or if it is the right driver to connect to the given URL, but has trouble connecting to the + * database. + *

    + * + *

    + * The java.util.Properties argument can be used to pass arbitrary string tag/value pairs as connection arguments. These properties take precedence over any + * properties sent in the URL. + *

    + * + *

    + * MySQL protocol takes the form: + * + *

    +     * jdbc:mysql://host:port/database
    +     * 
    + * + *

    + * + * @param url + * the URL of the database to connect to + * @param info + * a list of arbitrary tag/value pairs as connection arguments + * + * @return a connection to the URL or null if it isn't us + * + * @exception SQLException + * if a database access error occurs or the url is null + * + * @see java.sql.Driver#connect + */ + public java.sql.Connection connect(String url, Properties info) throws SQLException { + if (url == null) { + throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); + } + + if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) { + return connectLoadBalanced(url, info); + } else if (StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { + return connectReplicationConnection(url, info); + } + + Properties props = null; + + if ((props = parseURL(url, info)) == null) { + return null; + } + + if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) { + return connectFailover(url, info); + } + + try { + Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(host(props), port(props), props, database(props), url); + + return newConn; + } catch (SQLException sqlEx) { + // Don't wrap SQLExceptions, throw + // them un-changed. + throw sqlEx; + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("NonRegisteringDriver.17") + ex.toString() + Messages.getString("NonRegisteringDriver.18"), + SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + protected static void trackConnection(Connection newConn) { + + ConnectionPhantomReference phantomRef = new ConnectionPhantomReference((ConnectionImpl) newConn, refQueue); + connectionPhantomRefs.put(phantomRef, phantomRef); + } + + private java.sql.Connection connectLoadBalanced(String url, Properties info) throws SQLException { + Properties parsedProps = parseURL(url, info); + + if (parsedProps == null) { + return null; + } + + // People tend to drop this in, it doesn't make sense + parsedProps.remove("roundRobinLoadBalance"); + + int numHosts = Integer.parseInt(parsedProps.getProperty(NUM_HOSTS_PROPERTY_KEY)); + + List hostList = new ArrayList(); + + for (int i = 0; i < numHosts; i++) { + int index = i + 1; + + hostList.add(parsedProps.getProperty(HOST_PROPERTY_KEY + "." + index) + ":" + parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index)); + } + + return LoadBalancedConnectionProxy.createProxyInstance(hostList, parsedProps); + } + + private java.sql.Connection connectFailover(String url, Properties info) throws SQLException { + Properties parsedProps = parseURL(url, info); + + if (parsedProps == null) { + return null; + } + + // People tend to drop this in, it doesn't make sense + parsedProps.remove("roundRobinLoadBalance"); + + int numHosts = Integer.parseInt(parsedProps.getProperty(NUM_HOSTS_PROPERTY_KEY)); + + List hostList = new ArrayList(); + + for (int i = 0; i < numHosts; i++) { + int index = i + 1; + + hostList.add(parsedProps.getProperty(HOST_PROPERTY_KEY + "." + index) + ":" + parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index)); + } + + return FailoverConnectionProxy.createProxyInstance(hostList, parsedProps); + } + + protected java.sql.Connection connectReplicationConnection(String url, Properties info) throws SQLException { + Properties parsedProps = parseURL(url, info); + + if (parsedProps == null) { + return null; + } + + Properties masterProps = (Properties) parsedProps.clone(); + Properties slavesProps = (Properties) parsedProps.clone(); + + // Marker used for further testing later on, also when + // debugging + slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave", "true"); + + int numHosts = Integer.parseInt(parsedProps.getProperty(NUM_HOSTS_PROPERTY_KEY)); + + if (numHosts < 2) { + throw SQLError.createSQLException("Must specify at least one slave host to connect to for master/slave replication load-balancing functionality", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } + List slaveHostList = new ArrayList(); + List masterHostList = new ArrayList(); + + String firstHost = masterProps.getProperty(HOST_PROPERTY_KEY + ".1") + ":" + masterProps.getProperty(PORT_PROPERTY_KEY + ".1"); + + boolean usesExplicitServerType = NonRegisteringDriver.isHostPropertiesList(firstHost); + + for (int i = 0; i < numHosts; i++) { + int index = i + 1; + + masterProps.remove(HOST_PROPERTY_KEY + "." + index); + masterProps.remove(PORT_PROPERTY_KEY + "." + index); + slavesProps.remove(HOST_PROPERTY_KEY + "." + index); + slavesProps.remove(PORT_PROPERTY_KEY + "." + index); + + String host = parsedProps.getProperty(HOST_PROPERTY_KEY + "." + index); + String port = parsedProps.getProperty(PORT_PROPERTY_KEY + "." + index); + if (usesExplicitServerType) { + if (isHostMaster(host)) { + masterHostList.add(host); + } else { + slaveHostList.add(host); + } + } else { + if (i == 0) { + masterHostList.add(host + ":" + port); + } else { + slaveHostList.add(host + ":" + port); + } + } + } + + slavesProps.remove(NUM_HOSTS_PROPERTY_KEY); + masterProps.remove(NUM_HOSTS_PROPERTY_KEY); + masterProps.remove(HOST_PROPERTY_KEY); + masterProps.remove(PORT_PROPERTY_KEY); + slavesProps.remove(HOST_PROPERTY_KEY); + slavesProps.remove(PORT_PROPERTY_KEY); + + return ReplicationConnectionProxy.createProxyInstance(masterHostList, masterProps, slaveHostList, slavesProps); + } + + private boolean isHostMaster(String host) { + if (NonRegisteringDriver.isHostPropertiesList(host)) { + Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(host); + if (hostSpecificProps.containsKey("type") && "master".equalsIgnoreCase(hostSpecificProps.get("type").toString())) { + return true; + } + } + return false; + } + + /** + * Returns the database property from props + * + * @param props + * the Properties to look for the database property. + * + * @return the database name. + */ + public String database(Properties props) { + return props.getProperty(DBNAME_PROPERTY_KEY); + } + + /** + * Gets the drivers major version number + * + * @return the drivers major version number + */ + public int getMajorVersion() { + return getMajorVersionInternal(); + } + + /** + * Get the drivers minor version number + * + * @return the drivers minor version number + */ + public int getMinorVersion() { + return getMinorVersionInternal(); + } + + /** + * The getPropertyInfo method is intended to allow a generic GUI tool to + * discover what properties it should prompt a human for in order to get + * enough information to connect to a database. + * + *

    + * Note that depending on the values the human has supplied so far, additional values may become necessary, so it may be necessary to iterate through + * several calls to getPropertyInfo + *

    + * + * @param url + * the Url of the database to connect to + * @param info + * a proposed list of tag/value pairs that will be sent on + * connect open. + * + * @return An array of DriverPropertyInfo objects describing possible + * properties. This array may be an empty array if no properties are + * required + * + * @exception SQLException + * if a database-access error occurs + * + * @see java.sql.Driver#getPropertyInfo + */ + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + if (info == null) { + info = new Properties(); + } + + if ((url != null) && url.startsWith(URL_PREFIX)) { + info = parseURL(url, info); + } + + DriverPropertyInfo hostProp = new DriverPropertyInfo(HOST_PROPERTY_KEY, info.getProperty(HOST_PROPERTY_KEY)); + hostProp.required = true; + hostProp.description = Messages.getString("NonRegisteringDriver.3"); + + DriverPropertyInfo portProp = new DriverPropertyInfo(PORT_PROPERTY_KEY, info.getProperty(PORT_PROPERTY_KEY, "3306")); + portProp.required = false; + portProp.description = Messages.getString("NonRegisteringDriver.7"); + + DriverPropertyInfo dbProp = new DriverPropertyInfo(DBNAME_PROPERTY_KEY, info.getProperty(DBNAME_PROPERTY_KEY)); + dbProp.required = false; + dbProp.description = "Database name"; + + DriverPropertyInfo userProp = new DriverPropertyInfo(USER_PROPERTY_KEY, info.getProperty(USER_PROPERTY_KEY)); + userProp.required = true; + userProp.description = Messages.getString("NonRegisteringDriver.13"); + + DriverPropertyInfo passwordProp = new DriverPropertyInfo(PASSWORD_PROPERTY_KEY, info.getProperty(PASSWORD_PROPERTY_KEY)); + passwordProp.required = true; + passwordProp.description = Messages.getString("NonRegisteringDriver.16"); + + DriverPropertyInfo[] dpi = ConnectionPropertiesImpl.exposeAsDriverPropertyInfo(info, 5); + + dpi[0] = hostProp; + dpi[1] = portProp; + dpi[2] = dbProp; + dpi[3] = userProp; + dpi[4] = passwordProp; + + return dpi; + } + + // + // return the value of any property this driver knows about + // + + /** + * Returns the hostname property + * + * @param props + * the java.util.Properties instance to retrieve the hostname + * from. + * + * @return the hostname + */ + public String host(Properties props) { + return props.getProperty(HOST_PROPERTY_KEY, "localhost"); + } + + /** + * Report whether the driver is a genuine JDBC compliant driver. A driver + * may only report "true" here if it passes the JDBC compliance tests, + * otherwise it is required to return false. JDBC compliance requires full + * support for the JDBC API and full support for SQL 92 Entry Level. + * + *

    + * MySQL is not SQL92 compliant + *

    + * + * @return is this driver JDBC compliant? + */ + public boolean jdbcCompliant() { + return false; + } + + @SuppressWarnings("deprecation") + public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { + Properties urlProps = (defaults != null) ? new Properties(defaults) : new Properties(); + + if (url == null) { + return null; + } + + if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { + + return null; + } + + int beginningOfSlashes = url.indexOf("//"); + + if (StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)) { + + urlProps.setProperty("socketFactory", "com.mysql.management.driverlaunched.ServerLauncherSocketFactory"); + } + + /* + * Parse parameters after the ? in the URL and remove them from the + * original URL. + */ + int index = url.indexOf("?"); + + if (index != -1) { + String paramString = url.substring(index + 1, url.length()); + url = url.substring(0, index); + + StringTokenizer queryParams = new StringTokenizer(paramString, "&"); + + while (queryParams.hasMoreTokens()) { + String parameterValuePair = queryParams.nextToken(); + + int indexOfEquals = StringUtils.indexOfIgnoreCase(0, parameterValuePair, "="); + + String parameter = null; + String value = null; + + if (indexOfEquals != -1) { + parameter = parameterValuePair.substring(0, indexOfEquals); + + if (indexOfEquals + 1 < parameterValuePair.length()) { + value = parameterValuePair.substring(indexOfEquals + 1); + } + } + + if ((value != null && value.length() > 0) && (parameter != null && parameter.length() > 0)) { + try { + urlProps.setProperty(parameter, URLDecoder.decode(value, "UTF-8")); + } catch (UnsupportedEncodingException badEncoding) { + // punt + urlProps.setProperty(parameter, URLDecoder.decode(value)); + } catch (NoSuchMethodError nsme) { + // punt again + urlProps.setProperty(parameter, URLDecoder.decode(value)); + } + } + } + } + + url = url.substring(beginningOfSlashes + 2); + + String hostStuff = null; + + int slashIndex = StringUtils.indexOfIgnoreCase(0, url, "/", ALLOWED_QUOTES, ALLOWED_QUOTES, StringUtils.SEARCH_MODE__ALL); + + if (slashIndex != -1) { + hostStuff = url.substring(0, slashIndex); + + if ((slashIndex + 1) < url.length()) { + urlProps.put(DBNAME_PROPERTY_KEY, url.substring((slashIndex + 1), url.length())); + } + } else { + hostStuff = url; + } + + int numHosts = 0; + + if ((hostStuff != null) && (hostStuff.trim().length() > 0)) { + List hosts = StringUtils.split(hostStuff, ",", ALLOWED_QUOTES, ALLOWED_QUOTES, false); + + for (String hostAndPort : hosts) { + numHosts++; + + String[] hostPortPair = parseHostPortPair(hostAndPort); + + if (hostPortPair[HOST_NAME_INDEX] != null && hostPortPair[HOST_NAME_INDEX].trim().length() > 0) { + urlProps.setProperty(HOST_PROPERTY_KEY + "." + numHosts, hostPortPair[HOST_NAME_INDEX]); + } else { + urlProps.setProperty(HOST_PROPERTY_KEY + "." + numHosts, "localhost"); + } + + if (hostPortPair[PORT_NUMBER_INDEX] != null) { + urlProps.setProperty(PORT_PROPERTY_KEY + "." + numHosts, hostPortPair[PORT_NUMBER_INDEX]); + } else { + urlProps.setProperty(PORT_PROPERTY_KEY + "." + numHosts, "3306"); + } + } + } else { + numHosts = 1; + urlProps.setProperty(HOST_PROPERTY_KEY + ".1", "localhost"); + urlProps.setProperty(PORT_PROPERTY_KEY + ".1", "3306"); + } + + urlProps.setProperty(NUM_HOSTS_PROPERTY_KEY, String.valueOf(numHosts)); + urlProps.setProperty(HOST_PROPERTY_KEY, urlProps.getProperty(HOST_PROPERTY_KEY + ".1")); + urlProps.setProperty(PORT_PROPERTY_KEY, urlProps.getProperty(PORT_PROPERTY_KEY + ".1")); + + String propertiesTransformClassName = urlProps.getProperty(PROPERTIES_TRANSFORM_KEY); + + if (propertiesTransformClassName != null) { + try { + ConnectionPropertiesTransform propTransformer = (ConnectionPropertiesTransform) Class.forName(propertiesTransformClassName).newInstance(); + + urlProps = propTransformer.transformProperties(urlProps); + } catch (InstantiationException e) { + throw SQLError.createSQLException( + "Unable to create properties transform instance '" + propertiesTransformClassName + "' due to underlying exception: " + e.toString(), + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } catch (IllegalAccessException e) { + throw SQLError.createSQLException( + "Unable to create properties transform instance '" + propertiesTransformClassName + "' due to underlying exception: " + e.toString(), + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } catch (ClassNotFoundException e) { + throw SQLError.createSQLException( + "Unable to create properties transform instance '" + propertiesTransformClassName + "' due to underlying exception: " + e.toString(), + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } + } + + if (Util.isColdFusion() && urlProps.getProperty("autoConfigureForColdFusion", "true").equalsIgnoreCase("true")) { + String configs = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY); + + StringBuilder newConfigs = new StringBuilder(); + + if (configs != null) { + newConfigs.append(configs); + newConfigs.append(","); + } + + newConfigs.append("coldFusion"); + + urlProps.setProperty(USE_CONFIG_PROPERTY_KEY, newConfigs.toString()); + } + + // If we use a config, it actually should get overridden by anything in the URL or passed-in properties + + String configNames = null; + + if (defaults != null) { + configNames = defaults.getProperty(USE_CONFIG_PROPERTY_KEY); + } + + if (configNames == null) { + configNames = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY); + } + + if (configNames != null) { + List splitNames = StringUtils.split(configNames, ",", true); + + Properties configProps = new Properties(); + + Iterator namesIter = splitNames.iterator(); + + while (namesIter.hasNext()) { + String configName = namesIter.next(); + + try { + InputStream configAsStream = getClass().getResourceAsStream("configs/" + configName + ".properties"); + + if (configAsStream == null) { + throw SQLError.createSQLException("Can't find configuration template named '" + configName + "'", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + } + configProps.load(configAsStream); + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException( + "Unable to load configuration template '" + configName + "' due to underlying IOException: " + ioEx, + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, null); + sqlEx.initCause(ioEx); + + throw sqlEx; + } + } + + Iterator propsIter = urlProps.keySet().iterator(); + + while (propsIter.hasNext()) { + String key = propsIter.next().toString(); + String property = urlProps.getProperty(key); + configProps.setProperty(key, property); + } + + urlProps = configProps; + } + + // Properties passed in should override ones in URL + + if (defaults != null) { + Iterator propsIter = defaults.keySet().iterator(); + + while (propsIter.hasNext()) { + String key = propsIter.next().toString(); + if (!key.equals(NUM_HOSTS_PROPERTY_KEY)) { + String property = defaults.getProperty(key); + urlProps.setProperty(key, property); + } + } + } + + return urlProps; + } + + /** + * Returns the port number property + * + * @param props + * the properties to get the port number from + * + * @return the port number + */ + public int port(Properties props) { + return Integer.parseInt(props.getProperty(PORT_PROPERTY_KEY, "3306")); + } + + /** + * Returns the given property from props + * + * @param name + * the property name + * @param props + * the property instance to look in + * + * @return the property value, or null if not found. + */ + public String property(String name, Properties props) { + return props.getProperty(name); + } + + /** + * Expands hosts of the form address=(protocol=tcp)(host=localhost)(port=3306) + * into a java.util.Properties. Special characters (in this case () and =) must be quoted. + * Any values that are string-quoted ("" or '') are also stripped of quotes. + */ + public static Properties expandHostKeyValues(String host) { + Properties hostProps = new Properties(); + + if (isHostPropertiesList(host)) { + host = host.substring("address=".length() + 1); + List hostPropsList = StringUtils.split(host, ")", "'\"", "'\"", true); + + for (String propDef : hostPropsList) { + if (propDef.startsWith("(")) { + propDef = propDef.substring(1); + } + + List kvp = StringUtils.split(propDef, "=", "'\"", "'\"", true); + + String key = kvp.get(0); + String value = kvp.size() > 1 ? kvp.get(1) : null; + + if (value != null && ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'")))) { + value = value.substring(1, value.length() - 1); + } + + if (value != null) { + if (HOST_PROPERTY_KEY.equalsIgnoreCase(key) || DBNAME_PROPERTY_KEY.equalsIgnoreCase(key) || PORT_PROPERTY_KEY.equalsIgnoreCase(key) + || PROTOCOL_PROPERTY_KEY.equalsIgnoreCase(key) || PATH_PROPERTY_KEY.equalsIgnoreCase(key)) { + key = key.toUpperCase(Locale.ENGLISH); + } else if (USER_PROPERTY_KEY.equalsIgnoreCase(key) || PASSWORD_PROPERTY_KEY.equalsIgnoreCase(key)) { + key = key.toLowerCase(Locale.ENGLISH); + } + + hostProps.setProperty(key, value); + } + } + } + + return hostProps; + } + + public static boolean isHostPropertiesList(String host) { + return host != null && StringUtils.startsWithIgnoreCase(host, "address="); + } + + static class ConnectionPhantomReference extends PhantomReference { + private NetworkResources io; + + ConnectionPhantomReference(ConnectionImpl connectionImpl, ReferenceQueue q) { + super(connectionImpl, q); + + try { + this.io = connectionImpl.getIO().getNetworkResources(); + } catch (SQLException e) { + // if we somehow got here and there's really no i/o, we deal with it later + } + } + + void cleanup() { + if (this.io != null) { + try { + this.io.forceClose(); + } finally { + this.io = null; + } + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java new file mode 100644 index 0000000..5440a5e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +/** + * Driver that opens two connections, one two a replication master, and another to one or more slaves, and decides to use master when the connection is not + * read-only, and use slave(s) when the connection is read-only. + */ +public class NonRegisteringReplicationDriver extends NonRegisteringDriver { + public NonRegisteringReplicationDriver() throws SQLException { + super(); + } + + /* + * (non-Javadoc) + * + * @see java.sql.Driver#connect(java.lang.String, java.util.Properties) + */ + @Override + public Connection connect(String url, Properties info) throws SQLException { + return connectReplicationConnection(url, info); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotImplemented.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotImplemented.java new file mode 100644 index 0000000..41c1f5d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotImplemented.java @@ -0,0 +1,39 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Thrown from methods not required to be implemented. + */ +public class NotImplemented extends java.sql.SQLException { + + static final long serialVersionUID = 7768433826547599990L; + + /** + * Creates a new NotImplemented object. + */ + public NotImplemented() { + super(Messages.getString("NotImplemented.0"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotUpdatable.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotUpdatable.java new file mode 100644 index 0000000..8486c0b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/NotUpdatable.java @@ -0,0 +1,60 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * Thrown when a result sate is not updatable + */ +public class NotUpdatable extends SQLException { + + private static final long serialVersionUID = 8084742846039782258L; + + /** + * The message to use when result set is not updatable. + * + * The same message is used in the warnings generated by Updatabale result + * set. + */ + public static final String NOT_UPDATEABLE_MESSAGE = Messages.getString("NotUpdatable.0") + Messages.getString("NotUpdatable.1") + + Messages.getString("NotUpdatable.2") + Messages.getString("NotUpdatable.3") + Messages.getString("NotUpdatable.4") + + Messages.getString("NotUpdatable.5"); + + /** + * Creates a new NotUpdatable exception. + */ + public NotUpdatable() { + this(NOT_UPDATEABLE_MESSAGE); + } + + /** + * Append the given reason to the not updatable message if the reason is not + * null. + */ + public NotUpdatable(String reason) { + super(reason + Messages.getString("NotUpdatable.1") + Messages.getString("NotUpdatable.2") + Messages.getString("NotUpdatable.3") + + Messages.getString("NotUpdatable.4") + Messages.getString("NotUpdatable.5"), SQLError.SQL_STATE_GENERAL_ERROR); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OperationNotSupportedException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OperationNotSupportedException.java new file mode 100644 index 0000000..870c305 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OperationNotSupportedException.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +class OperationNotSupportedException extends SQLException { + + static final long serialVersionUID = 474918612056813430L; + + OperationNotSupportedException() { + super(Messages.getString("RowDataDynamic.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OutputStreamWatcher.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OutputStreamWatcher.java new file mode 100644 index 0000000..58a2576 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/OutputStreamWatcher.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Objects that want to be notified of lifecycle events on a WatchableOutputStream should implement this interface, and register themselves with setWatcher() + * on the WatchableOutputStream instance. + */ +interface OutputStreamWatcher { + /** + * Called when the OutputStream being watched has .close() called + */ + void streamClosed(WatchableOutputStream out); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PacketTooBigException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PacketTooBigException.java new file mode 100644 index 0000000..705c2f6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PacketTooBigException.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * Thrown when a packet that is too big for the server is created. + */ +public class PacketTooBigException extends SQLException { + + static final long serialVersionUID = 7248633977685452174L; + + /** + * Creates a new PacketTooBigException object. + * + * @param packetSize + * the size of the packet that was going to be sent + * @param maximumPacketSize + * the maximum size the server will accept + */ + public PacketTooBigException(long packetSize, long maximumPacketSize) { + super(Messages.getString("PacketTooBigException.0") + packetSize + Messages.getString("PacketTooBigException.1") + maximumPacketSize + + Messages.getString("PacketTooBigException.2") + Messages.getString("PacketTooBigException.3") + Messages.getString("PacketTooBigException.4"), + SQLError.SQL_STATE_GENERAL_ERROR); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ParameterBindings.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ParameterBindings.java new file mode 100644 index 0000000..ac4db25 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ParameterBindings.java @@ -0,0 +1,92 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Clob; +import java.sql.Date; +import java.sql.Ref; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; + +/** + * Interface to allow PreparedStatement implementations to expose their parameter bindings to StatementInterceptors. + */ +public interface ParameterBindings { + + public abstract Array getArray(int parameterIndex) throws SQLException; + + public abstract InputStream getAsciiStream(int parameterIndex) throws SQLException; + + public abstract BigDecimal getBigDecimal(int parameterIndex) throws SQLException; + + public abstract InputStream getBinaryStream(int parameterIndex) throws SQLException; + + public abstract java.sql.Blob getBlob(int parameterIndex) throws SQLException; + + public abstract boolean getBoolean(int parameterIndex) throws SQLException; + + public abstract byte getByte(int parameterIndex) throws SQLException; + + public abstract byte[] getBytes(int parameterIndex) throws SQLException; + + public abstract Reader getCharacterStream(int parameterIndex) throws SQLException; + + public abstract Clob getClob(int parameterIndex) throws SQLException; + + public abstract Date getDate(int parameterIndex) throws SQLException; + + public abstract double getDouble(int parameterIndex) throws SQLException; + + public abstract float getFloat(int parameterIndex) throws SQLException; + + public abstract int getInt(int parameterIndex) throws SQLException; + + public abstract long getLong(int parameterIndex) throws SQLException; + + public abstract Reader getNCharacterStream(int parameterIndex) throws SQLException; + + public abstract Reader getNClob(int parameterIndex) throws SQLException; + + public abstract Object getObject(int parameterIndex) throws SQLException; + + public abstract Ref getRef(int parameterIndex) throws SQLException; + + public abstract short getShort(int parameterIndex) throws SQLException; + + public abstract String getString(int parameterIndex) throws SQLException; + + public abstract Time getTime(int parameterIndex) throws SQLException; + + public abstract Timestamp getTimestamp(int parameterIndex) throws SQLException; + + public abstract URL getURL(int parameterIndex) throws SQLException; + + public abstract boolean isNull(int parameterIndex) throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerConnectionLRUFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerConnectionLRUFactory.java new file mode 100644 index 0000000..5885e8c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerConnectionLRUFactory.java @@ -0,0 +1,94 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.Set; + +import com.mysql.jdbc.PreparedStatement.ParseInfo; +import com.mysql.jdbc.util.LRUCache; + +public class PerConnectionLRUFactory implements CacheAdapterFactory { + + public CacheAdapter getInstance(Connection forConnection, String url, int cacheMaxSize, int maxKeySize, Properties connectionProperties) + throws SQLException { + + return new PerConnectionLRU(forConnection, cacheMaxSize, maxKeySize); + } + + class PerConnectionLRU implements CacheAdapter { + private final int cacheSqlLimit; + private final LRUCache cache; + private final Connection conn; + + protected PerConnectionLRU(Connection forConnection, int cacheMaxSize, int maxKeySize) { + final int cacheSize = cacheMaxSize; + this.cacheSqlLimit = maxKeySize; + this.cache = new LRUCache(cacheSize); + this.conn = forConnection; + } + + public ParseInfo get(String key) { + if (key == null || key.length() > this.cacheSqlLimit) { + return null; + } + + synchronized (this.conn.getConnectionMutex()) { + return (ParseInfo) this.cache.get(key); + } + } + + public void put(String key, ParseInfo value) { + if (key == null || key.length() > this.cacheSqlLimit) { + return; + } + + synchronized (this.conn.getConnectionMutex()) { + this.cache.put(key, value); + } + } + + public void invalidate(String key) { + synchronized (this.conn.getConnectionMutex()) { + this.cache.remove(key); + } + } + + public void invalidateAll(Set keys) { + synchronized (this.conn.getConnectionMutex()) { + for (String key : keys) { + this.cache.remove(key); + } + } + + } + + public void invalidateAll() { + synchronized (this.conn.getConnectionMutex()) { + this.cache.clear(); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerVmServerConfigCacheFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerVmServerConfigCacheFactory.java new file mode 100644 index 0000000..f317b98 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PerVmServerConfigCacheFactory.java @@ -0,0 +1,64 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class PerVmServerConfigCacheFactory implements CacheAdapterFactory> { + static final ConcurrentHashMap> serverConfigByUrl = new ConcurrentHashMap>(); + + private static final CacheAdapter> serverConfigCache = new CacheAdapter>() { + + public Map get(String key) { + return serverConfigByUrl.get(key); + } + + public void put(String key, Map value) { + serverConfigByUrl.putIfAbsent(key, value); + } + + public void invalidate(String key) { + serverConfigByUrl.remove(key); + } + + public void invalidateAll(Set keys) { + for (String key : keys) { + serverConfigByUrl.remove(key); + } + } + + public void invalidateAll() { + serverConfigByUrl.clear(); + } + }; + + public CacheAdapter> getInstance(Connection forConn, String url, int cacheMaxSize, int maxKeySize, + Properties connectionProperties) throws SQLException { + return serverConfigCache; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PingTarget.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PingTarget.java new file mode 100644 index 0000000..06cb9c0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PingTarget.java @@ -0,0 +1,30 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface PingTarget { + public void doPing() throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PreparedStatement.java new file mode 100644 index 0000000..1de22fa --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/PreparedStatement.java @@ -0,0 +1,5100 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectOutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.sql.Array; +import java.sql.Clob; +import java.sql.DatabaseMetaData; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.Ref; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * A SQL Statement is pre-compiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times. + * + *

    + * Note: The setXXX methods for setting IN parameter values must specify types that are compatible with the defined SQL type of the input parameter. For + * instance, if the IN parameter has SQL type Integer, then setInt should be used. + *

    + * + *

    + * If arbitrary parameter type conversions are required, then the setObject method should be used with a target SQL type. + *

    + */ +public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements java.sql.PreparedStatement { + private static final Constructor JDBC_4_PSTMT_2_ARG_CTOR; + private static final Constructor JDBC_4_PSTMT_3_ARG_CTOR; + private static final Constructor JDBC_4_PSTMT_4_ARG_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42PreparedStatement" : "com.mysql.jdbc.JDBC4PreparedStatement"; + JDBC_4_PSTMT_2_ARG_CTOR = Class.forName(jdbc4ClassName).getConstructor(new Class[] { MySQLConnection.class, String.class }); + JDBC_4_PSTMT_3_ARG_CTOR = Class.forName(jdbc4ClassName).getConstructor(new Class[] { MySQLConnection.class, String.class, String.class }); + JDBC_4_PSTMT_4_ARG_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { MySQLConnection.class, String.class, String.class, ParseInfo.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_PSTMT_2_ARG_CTOR = null; + JDBC_4_PSTMT_3_ARG_CTOR = null; + JDBC_4_PSTMT_4_ARG_CTOR = null; + } + } + + public class BatchParams { + public boolean[] isNull = null; + + public boolean[] isStream = null; + + public InputStream[] parameterStreams = null; + + public byte[][] parameterStrings = null; + + public int[] streamLengths = null; + + BatchParams(byte[][] strings, InputStream[] streams, boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) { + // + // Make copies + // + this.parameterStrings = new byte[strings.length][]; + this.parameterStreams = new InputStream[streams.length]; + this.isStream = new boolean[isStreamFlags.length]; + this.streamLengths = new int[lengths.length]; + this.isNull = new boolean[isNullFlags.length]; + System.arraycopy(strings, 0, this.parameterStrings, 0, strings.length); + System.arraycopy(streams, 0, this.parameterStreams, 0, streams.length); + System.arraycopy(isStreamFlags, 0, this.isStream, 0, isStreamFlags.length); + System.arraycopy(lengths, 0, this.streamLengths, 0, lengths.length); + System.arraycopy(isNullFlags, 0, this.isNull, 0, isNullFlags.length); + } + } + + class EndPoint { + int begin; + + int end; + + EndPoint(int b, int e) { + this.begin = b; + this.end = e; + } + } + + public static final class ParseInfo { + char firstStmtChar = 0; + + boolean foundLoadData = false; + + long lastUsed = 0; + + int statementLength = 0; + + int statementStartPos = 0; + + boolean canRewriteAsMultiValueInsert = false; + + byte[][] staticSql = null; + + boolean isOnDuplicateKeyUpdate = false; + + int locationOfOnDuplicateKeyUpdate = -1; + + String valuesClause; + + boolean parametersInDuplicateKeyClause = false; + + String charEncoding; + + /** + * Represents the "parsed" state of a client-side prepared statement, with the statement broken up into it's static and dynamic (where parameters are + * bound) parts. + */ + ParseInfo(String sql, MySQLConnection conn, java.sql.DatabaseMetaData dbmd, String encoding, SingleByteCharsetConverter converter) throws SQLException { + this(sql, conn, dbmd, encoding, converter, true); + } + + public ParseInfo(String sql, MySQLConnection conn, java.sql.DatabaseMetaData dbmd, String encoding, SingleByteCharsetConverter converter, + boolean buildRewriteInfo) throws SQLException { + try { + if (sql == null) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.61"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + conn.getExceptionInterceptor()); + } + + this.charEncoding = encoding; + this.lastUsed = System.currentTimeMillis(); + + String quotedIdentifierString = dbmd.getIdentifierQuoteString(); + + char quotedIdentifierChar = 0; + + if ((quotedIdentifierString != null) && !quotedIdentifierString.equals(" ") && (quotedIdentifierString.length() > 0)) { + quotedIdentifierChar = quotedIdentifierString.charAt(0); + } + + this.statementLength = sql.length(); + + ArrayList endpointList = new ArrayList(); + boolean inQuotes = false; + char quoteChar = 0; + boolean inQuotedId = false; + int lastParmEnd = 0; + int i; + + boolean noBackslashEscapes = conn.isNoBackslashEscapesSet(); + + // we're not trying to be real pedantic here, but we'd like to skip comments at the beginning of statements, as frameworks such as Hibernate + // use them to aid in debugging + + this.statementStartPos = findStartOfStatement(sql); + + for (i = this.statementStartPos; i < this.statementLength; ++i) { + char c = sql.charAt(i); + + if ((this.firstStmtChar == 0) && Character.isLetter(c)) { + // Determine what kind of statement we're doing (_S_elect, _I_nsert, etc.) + this.firstStmtChar = Character.toUpperCase(c); + + // no need to search for "ON DUPLICATE KEY UPDATE" if not an INSERT statement + if (this.firstStmtChar == 'I') { + this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql, conn.getDontCheckOnDuplicateKeyUpdateInSQL(), + conn.getRewriteBatchedStatements(), conn.isNoBackslashEscapesSet()); + this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1; + } + } + + if (!noBackslashEscapes && c == '\\' && i < (this.statementLength - 1)) { + i++; + continue; // next character is escaped + } + + // are we in a quoted identifier? (only valid when the id is not inside a 'string') + if (!inQuotes && (quotedIdentifierChar != 0) && (c == quotedIdentifierChar)) { + inQuotedId = !inQuotedId; + } else if (!inQuotedId) { + // only respect quotes when not in a quoted identifier + + if (inQuotes) { + if (((c == '\'') || (c == '"')) && c == quoteChar) { + if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) { + i++; + continue; // inline quote escape + } + + inQuotes = !inQuotes; + quoteChar = 0; + } else if (((c == '\'') || (c == '"')) && c == quoteChar) { + inQuotes = !inQuotes; + quoteChar = 0; + } + } else { + if (c == '#' || (c == '-' && (i + 1) < this.statementLength && sql.charAt(i + 1) == '-')) { + // run out to end of statement, or newline, whichever comes first + int endOfStmt = this.statementLength - 1; + + for (; i < endOfStmt; i++) { + c = sql.charAt(i); + + if (c == '\r' || c == '\n') { + break; + } + } + + continue; + } else if (c == '/' && (i + 1) < this.statementLength) { + // Comment? + char cNext = sql.charAt(i + 1); + + if (cNext == '*') { + i += 2; + + for (int j = i; j < this.statementLength; j++) { + i++; + cNext = sql.charAt(j); + + if (cNext == '*' && (j + 1) < this.statementLength) { + if (sql.charAt(j + 1) == '/') { + i++; + + if (i < this.statementLength) { + c = sql.charAt(i); + } + + break; // comment done + } + } + } + } + } else if ((c == '\'') || (c == '"')) { + inQuotes = true; + quoteChar = c; + } + } + } + + if ((c == '?') && !inQuotes && !inQuotedId) { + endpointList.add(new int[] { lastParmEnd, i }); + lastParmEnd = i + 1; + + if (this.isOnDuplicateKeyUpdate && i > this.locationOfOnDuplicateKeyUpdate) { + this.parametersInDuplicateKeyClause = true; + } + } + } + + if (this.firstStmtChar == 'L') { + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { + this.foundLoadData = true; + } else { + this.foundLoadData = false; + } + } else { + this.foundLoadData = false; + } + + endpointList.add(new int[] { lastParmEnd, this.statementLength }); + this.staticSql = new byte[endpointList.size()][]; + + for (i = 0; i < this.staticSql.length; i++) { + int[] ep = endpointList.get(i); + int end = ep[1]; + int begin = ep[0]; + int len = end - begin; + + if (this.foundLoadData) { + this.staticSql[i] = StringUtils.getBytes(sql, begin, len); + } else if (encoding == null) { + byte[] buf = new byte[len]; + + for (int j = 0; j < len; j++) { + buf[j] = (byte) sql.charAt(begin + j); + } + + this.staticSql[i] = buf; + } else { + if (converter != null) { + this.staticSql[i] = StringUtils.getBytes(sql, converter, encoding, conn.getServerCharset(), begin, len, conn.parserKnowsUnicode(), + conn.getExceptionInterceptor()); + } else { + this.staticSql[i] = StringUtils.getBytes(sql, encoding, conn.getServerCharset(), begin, len, conn.parserKnowsUnicode(), conn, + conn.getExceptionInterceptor()); + } + } + } + } catch (StringIndexOutOfBoundsException oobEx) { + SQLException sqlEx = new SQLException("Parse error for " + sql); + sqlEx.initCause(oobEx); + + throw sqlEx; + } + + if (buildRewriteInfo) { + this.canRewriteAsMultiValueInsert = PreparedStatement.canRewrite(sql, this.isOnDuplicateKeyUpdate, this.locationOfOnDuplicateKeyUpdate, + this.statementStartPos) && !this.parametersInDuplicateKeyClause; + + if (this.canRewriteAsMultiValueInsert && conn.getRewriteBatchedStatements()) { + buildRewriteBatchedParams(sql, conn, dbmd, encoding, converter); + } + } + } + + private ParseInfo batchHead; + + private ParseInfo batchValues; + + private ParseInfo batchODKUClause; + + private void buildRewriteBatchedParams(String sql, MySQLConnection conn, DatabaseMetaData metadata, String encoding, + SingleByteCharsetConverter converter) throws SQLException { + this.valuesClause = extractValuesClause(sql, conn.getMetaData().getIdentifierQuoteString()); + String odkuClause = this.isOnDuplicateKeyUpdate ? sql.substring(this.locationOfOnDuplicateKeyUpdate) : null; + + String headSql = null; + + if (this.isOnDuplicateKeyUpdate) { + headSql = sql.substring(0, this.locationOfOnDuplicateKeyUpdate); + } else { + headSql = sql; + } + + this.batchHead = new ParseInfo(headSql, conn, metadata, encoding, converter, false); + this.batchValues = new ParseInfo("," + this.valuesClause, conn, metadata, encoding, converter, false); + this.batchODKUClause = null; + + if (odkuClause != null && odkuClause.length() > 0) { + this.batchODKUClause = new ParseInfo("," + this.valuesClause + " " + odkuClause, conn, metadata, encoding, converter, false); + } + } + + private String extractValuesClause(String sql, String quoteCharStr) throws SQLException { + int indexOfValues = -1; + int valuesSearchStart = this.statementStartPos; + + while (indexOfValues == -1) { + if (quoteCharStr.length() > 0) { + indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES", quoteCharStr, quoteCharStr, + StringUtils.SEARCH_MODE__MRK_COM_WS); + } else { + indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES"); + } + + if (indexOfValues > 0) { + /* check if the char immediately preceding VALUES may be part of the table name */ + char c = sql.charAt(indexOfValues - 1); + if (!(Character.isWhitespace(c) || c == ')' || c == '`')) { + valuesSearchStart = indexOfValues + 6; + indexOfValues = -1; + } else { + /* check if the char immediately following VALUES may be whitespace or open parenthesis */ + c = sql.charAt(indexOfValues + 6); + if (!(Character.isWhitespace(c) || c == '(')) { + valuesSearchStart = indexOfValues + 6; + indexOfValues = -1; + } + } + } else { + break; + } + } + + if (indexOfValues == -1) { + return null; + } + + int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6); + + if (indexOfFirstParen == -1) { + return null; + } + + int endOfValuesClause = sql.lastIndexOf(')'); + + if (endOfValuesClause == -1) { + return null; + } + + if (this.isOnDuplicateKeyUpdate) { + endOfValuesClause = this.locationOfOnDuplicateKeyUpdate - 1; + } + + return sql.substring(indexOfFirstParen, endOfValuesClause + 1); + } + + /** + * Returns a ParseInfo for a multi-value INSERT for a batch of size numBatch (without parsing!). + */ + synchronized ParseInfo getParseInfoForBatch(int numBatch) { + AppendingBatchVisitor apv = new AppendingBatchVisitor(); + buildInfoForBatch(numBatch, apv); + + ParseInfo batchParseInfo = new ParseInfo(apv.getStaticSqlStrings(), this.firstStmtChar, this.foundLoadData, this.isOnDuplicateKeyUpdate, + this.locationOfOnDuplicateKeyUpdate, this.statementLength, this.statementStartPos); + + return batchParseInfo; + } + + /** + * Returns a preparable SQL string for the number of batched parameters, used by server-side prepared statements + * when re-writing batch INSERTs. + */ + + String getSqlForBatch(int numBatch) throws UnsupportedEncodingException { + ParseInfo batchInfo = getParseInfoForBatch(numBatch); + + return getSqlForBatch(batchInfo); + } + + /** + * Used for filling in the SQL for getPreparedSql() - for debugging + */ + String getSqlForBatch(ParseInfo batchInfo) throws UnsupportedEncodingException { + int size = 0; + final byte[][] sqlStrings = batchInfo.staticSql; + final int sqlStringsLength = sqlStrings.length; + + for (int i = 0; i < sqlStringsLength; i++) { + size += sqlStrings[i].length; + size++; // for the '?' + } + + StringBuilder buf = new StringBuilder(size); + + for (int i = 0; i < sqlStringsLength - 1; i++) { + buf.append(StringUtils.toString(sqlStrings[i], this.charEncoding)); + buf.append("?"); + } + + buf.append(StringUtils.toString(sqlStrings[sqlStringsLength - 1])); + + return buf.toString(); + } + + /** + * Builds a ParseInfo for the given batch size, without parsing. We use + * a visitor pattern here, because the if {}s make computing a size for the + * resultant byte[][] make this too complex, and we don't necessarily want to + * use a List for this, because the size can be dynamic, and thus we'll not be + * able to guess a good initial size for an array-based list, and it's not + * efficient to convert a LinkedList to an array. + */ + private void buildInfoForBatch(int numBatch, BatchVisitor visitor) { + final byte[][] headStaticSql = this.batchHead.staticSql; + final int headStaticSqlLength = headStaticSql.length; + + if (headStaticSqlLength > 1) { + for (int i = 0; i < headStaticSqlLength - 1; i++) { + visitor.append(headStaticSql[i]).increment(); + } + } + + // merge end of head, with beginning of a value clause + byte[] endOfHead = headStaticSql[headStaticSqlLength - 1]; + final byte[][] valuesStaticSql = this.batchValues.staticSql; + byte[] beginOfValues = valuesStaticSql[0]; + + visitor.merge(endOfHead, beginOfValues).increment(); + + int numValueRepeats = numBatch - 1; // first one is in the "head" + + if (this.batchODKUClause != null) { + numValueRepeats--; // Last one is in the ODKU clause + } + + final int valuesStaticSqlLength = valuesStaticSql.length; + byte[] endOfValues = valuesStaticSql[valuesStaticSqlLength - 1]; + + for (int i = 0; i < numValueRepeats; i++) { + for (int j = 1; j < valuesStaticSqlLength - 1; j++) { + visitor.append(valuesStaticSql[j]).increment(); + } + visitor.merge(endOfValues, beginOfValues).increment(); + } + + if (this.batchODKUClause != null) { + final byte[][] batchOdkuStaticSql = this.batchODKUClause.staticSql; + byte[] beginOfOdku = batchOdkuStaticSql[0]; + visitor.decrement().merge(endOfValues, beginOfOdku).increment(); + + final int batchOdkuStaticSqlLength = batchOdkuStaticSql.length; + + if (numBatch > 1) { + for (int i = 1; i < batchOdkuStaticSqlLength; i++) { + visitor.append(batchOdkuStaticSql[i]).increment(); + } + } else { + visitor.decrement().append(batchOdkuStaticSql[(batchOdkuStaticSqlLength - 1)]); + } + } else { + // Everything after the values clause, but not ODKU, which today is nothing but a syntax error, but we should still not mangle the SQL! + visitor.decrement().append(this.staticSql[this.staticSql.length - 1]); + } + } + + private ParseInfo(byte[][] staticSql, char firstStmtChar, boolean foundLoadData, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, + int statementLength, int statementStartPos) { + this.firstStmtChar = firstStmtChar; + this.foundLoadData = foundLoadData; + this.isOnDuplicateKeyUpdate = isOnDuplicateKeyUpdate; + this.locationOfOnDuplicateKeyUpdate = locationOfOnDuplicateKeyUpdate; + this.statementLength = statementLength; + this.statementStartPos = statementStartPos; + this.staticSql = staticSql; + } + } + + interface BatchVisitor { + abstract BatchVisitor increment(); + + abstract BatchVisitor decrement(); + + abstract BatchVisitor append(byte[] values); + + abstract BatchVisitor merge(byte[] begin, byte[] end); + } + + static class AppendingBatchVisitor implements BatchVisitor { + LinkedList statementComponents = new LinkedList(); + + public BatchVisitor append(byte[] values) { + this.statementComponents.addLast(values); + + return this; + } + + public BatchVisitor increment() { + // no-op + return this; + } + + public BatchVisitor decrement() { + this.statementComponents.removeLast(); + + return this; + } + + public BatchVisitor merge(byte[] front, byte[] back) { + int mergedLength = front.length + back.length; + byte[] merged = new byte[mergedLength]; + System.arraycopy(front, 0, merged, 0, front.length); + System.arraycopy(back, 0, merged, front.length, back.length); + this.statementComponents.addLast(merged); + return this; + } + + public byte[][] getStaticSqlStrings() { + byte[][] asBytes = new byte[this.statementComponents.size()][]; + this.statementComponents.toArray(asBytes); + + return asBytes; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + Iterator iter = this.statementComponents.iterator(); + while (iter.hasNext()) { + buf.append(StringUtils.toString(iter.next())); + } + + return buf.toString(); + } + + } + + private final static byte[] HEX_DIGITS = new byte[] { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' }; + + /** + * Reads length bytes from reader into buf. Blocks until enough input is + * available + * + * @param reader + * @param buf + * @param length + * + * @throws IOException + */ + protected static int readFully(Reader reader, char[] buf, int length) throws IOException { + int numCharsRead = 0; + + while (numCharsRead < length) { + int count = reader.read(buf, numCharsRead, length - numCharsRead); + + if (count < 0) { + break; + } + + numCharsRead += count; + } + + return numCharsRead; + } + + /** + * Does the batch (if any) contain "plain" statements added by + * Statement.addBatch(String)? + * + * If so, we can't re-write it to use multi-value or multi-queries. + */ + protected boolean batchHasPlainStatements = false; + + private java.sql.DatabaseMetaData dbmd = null; + + /** + * What is the first character of the prepared statement (used to check for + * SELECT vs. INSERT/UPDATE/DELETE) + */ + protected char firstCharOfStmt = 0; + + /** Is this query a LOAD DATA query? */ + protected boolean isLoadDataQuery = false; + + protected boolean[] isNull = null; + + private boolean[] isStream = null; + + protected int numberOfExecutions = 0; + + /** The SQL that was passed in to 'prepare' */ + protected String originalSql = null; + + /** The number of parameters in this PreparedStatement */ + protected int parameterCount; + + protected MysqlParameterMetadata parameterMetaData; + + private InputStream[] parameterStreams = null; + + private byte[][] parameterValues = null; + + /** + * Only used by statement interceptors at the moment to + * provide introspection of bound values + */ + protected int[] parameterTypes = null; + + protected ParseInfo parseInfo; + + private java.sql.ResultSetMetaData pstmtResultMetaData; + + private byte[][] staticSqlStrings = null; + + private byte[] streamConvertBuf = null; + + private int[] streamLengths = null; + + private SimpleDateFormat tsdf = null; + + private SimpleDateFormat ddf; + + private SimpleDateFormat tdf; + + /** + * Are we using a version of MySQL where we can use 'true' boolean values? + */ + protected boolean useTrueBoolean = false; + + protected boolean usingAnsiMode; + + protected String batchedValuesClause; + + private boolean doPingInstead; + + private boolean compensateForOnDuplicateKeyUpdate = false; + + /** Charset encoder used to escape if needed, such as Yen sign in SJIS */ + private CharsetEncoder charsetEncoder; + + /** Command index of currently executing batch command. */ + protected int batchCommandIndex = -1; + + protected boolean serverSupportsFracSecs; + + /** + * Creates a prepared statement instance -- We need to provide factory-style + * methods so we can support both JDBC3 (and older) and JDBC4 runtimes, + * otherwise the class verifier complains when it tries to load JDBC4-only + * interface classes that are present in JDBC4 method signatures. + */ + + protected static PreparedStatement getInstance(MySQLConnection conn, String catalog) throws SQLException { + if (!Util.isJdbc4()) { + return new PreparedStatement(conn, catalog); + } + + return (PreparedStatement) Util.handleNewInstance(JDBC_4_PSTMT_2_ARG_CTOR, new Object[] { conn, catalog }, conn.getExceptionInterceptor()); + } + + /** + * Creates a prepared statement instance -- We need to provide factory-style + * methods so we can support both JDBC3 (and older) and JDBC4 runtimes, + * otherwise the class verifier complains when it tries to load JDBC4-only + * interface classes that are present in JDBC4 method signatures. + */ + + protected static PreparedStatement getInstance(MySQLConnection conn, String sql, String catalog) throws SQLException { + if (!Util.isJdbc4()) { + return new PreparedStatement(conn, sql, catalog); + } + + return (PreparedStatement) Util.handleNewInstance(JDBC_4_PSTMT_3_ARG_CTOR, new Object[] { conn, sql, catalog }, conn.getExceptionInterceptor()); + } + + /** + * Creates a prepared statement instance -- We need to provide factory-style + * methods so we can support both JDBC3 (and older) and JDBC4 runtimes, + * otherwise the class verifier complains when it tries to load JDBC4-only + * interface classes that are present in JDBC4 method signatures. + */ + + protected static PreparedStatement getInstance(MySQLConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + if (!Util.isJdbc4()) { + return new PreparedStatement(conn, sql, catalog, cachedParseInfo); + } + + return (PreparedStatement) Util.handleNewInstance(JDBC_4_PSTMT_4_ARG_CTOR, new Object[] { conn, sql, catalog, cachedParseInfo }, + conn.getExceptionInterceptor()); + } + + /** + * Constructor used by server-side prepared statements + * + * @param conn + * the connection that created us + * @param catalog + * the catalog in use when we were created + * + * @throws SQLException + * if an error occurs + */ + public PreparedStatement(MySQLConnection conn, String catalog) throws SQLException { + super(conn, catalog); + + detectFractionalSecondsSupport(); + this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts(); + } + + protected void detectFractionalSecondsSupport() throws SQLException { + this.serverSupportsFracSecs = this.connection != null && this.connection.versionMeetsMinimum(5, 6, 4); + } + + /** + * Constructor for the PreparedStatement class. + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL for this statement + * @param catalog + * the catalog/database this statement should be issued against + * + * @throws SQLException + * if a database error occurs. + */ + public PreparedStatement(MySQLConnection conn, String sql, String catalog) throws SQLException { + super(conn, catalog); + + if (sql == null) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.0"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + detectFractionalSecondsSupport(); + this.originalSql = sql; + + if (this.originalSql.startsWith(PING_MARKER)) { + this.doPingInstead = true; + } else { + this.doPingInstead = false; + } + + this.dbmd = this.connection.getMetaData(); + + this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23); + + this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd, this.charEncoding, this.charConverter); + + initializeFromParseInfo(); + + this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts(); + + if (conn.getRequiresEscapingEncoder()) { + this.charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder(); + } + } + + /** + * Creates a new PreparedStatement object. + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL for this statement + * @param catalog + * the catalog/database this statement should be issued against + * @param cachedParseInfo + * already created parseInfo. + * + * @throws SQLException + */ + public PreparedStatement(MySQLConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + super(conn, catalog); + + if (sql == null) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.1"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + detectFractionalSecondsSupport(); + this.originalSql = sql; + + this.dbmd = this.connection.getMetaData(); + + this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23); + + this.parseInfo = cachedParseInfo; + + this.usingAnsiMode = !this.connection.useAnsiQuotedIdentifiers(); + + initializeFromParseInfo(); + + this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts(); + + if (conn.getRequiresEscapingEncoder()) { + this.charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder(); + } + } + + /** + * JDBC 2.0 Add a set of parameters to the batch. + * + * @exception SQLException + * if a database-access error occurs. + * + * @see StatementImpl#addBatch + */ + public void addBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.batchedArgs == null) { + this.batchedArgs = new ArrayList(); + } + + for (int i = 0; i < this.parameterValues.length; i++) { + checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i); + } + + this.batchedArgs.add(new BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull)); + } + } + + @Override + public void addBatch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.batchHasPlainStatements = true; + + super.addBatch(sql); + } + } + + public String asSql() throws SQLException { + return asSql(false); + } + + public String asSql(boolean quoteStreamsAndUnknowns) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + StringBuilder buf = new StringBuilder(); + + try { + int realParameterCount = this.parameterCount + getParameterIndexOffset(); + Object batchArg = null; + if (this.batchCommandIndex != -1) { + batchArg = this.batchedArgs.get(this.batchCommandIndex); + } + + for (int i = 0; i < realParameterCount; ++i) { + if (this.charEncoding != null) { + buf.append(StringUtils.toString(this.staticSqlStrings[i], this.charEncoding)); + } else { + buf.append(StringUtils.toString(this.staticSqlStrings[i])); + } + + byte val[] = null; + if (batchArg != null && batchArg instanceof String) { + buf.append((String) batchArg); + continue; + } + if (this.batchCommandIndex == -1) { + val = this.parameterValues[i]; + } else { + val = ((BatchParams) batchArg).parameterStrings[i]; + } + + boolean isStreamParam = false; + if (this.batchCommandIndex == -1) { + isStreamParam = this.isStream[i]; + } else { + isStreamParam = ((BatchParams) batchArg).isStream[i]; + } + + if ((val == null) && !isStreamParam) { + if (quoteStreamsAndUnknowns) { + buf.append("'"); + } + + buf.append("** NOT SPECIFIED **"); + + if (quoteStreamsAndUnknowns) { + buf.append("'"); + } + } else if (isStreamParam) { + if (quoteStreamsAndUnknowns) { + buf.append("'"); + } + + buf.append("** STREAM DATA **"); + + if (quoteStreamsAndUnknowns) { + buf.append("'"); + } + } else { + if (this.charConverter != null) { + buf.append(this.charConverter.toString(val)); + } else { + if (this.charEncoding != null) { + buf.append(new String(val, this.charEncoding)); + } else { + buf.append(StringUtils.toAsciiString(val)); + } + } + } + } + + if (this.charEncoding != null) { + buf.append(StringUtils.toString(this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()], this.charEncoding)); + } else { + buf.append(StringUtils.toAsciiString(this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()])); + } + } catch (UnsupportedEncodingException uue) { + throw new RuntimeException(Messages.getString("PreparedStatement.32") + this.charEncoding + Messages.getString("PreparedStatement.33")); + } + + return buf.toString(); + } + } + + @Override + public void clearBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.batchHasPlainStatements = false; + + super.clearBatch(); + } + } + + /** + * In general, parameter values remain in force for repeated used of a + * Statement. Setting a parameter value automatically clears its previous + * value. However, in some cases, it is useful to immediately release the + * resources used by the current parameter values; this can be done by + * calling clearParameters + * + * @exception SQLException + * if a database access error occurs + */ + public void clearParameters() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + for (int i = 0; i < this.parameterValues.length; i++) { + this.parameterValues[i] = null; + this.parameterStreams[i] = null; + this.isStream[i] = false; + this.isNull[i] = false; + this.parameterTypes[i] = Types.NULL; + } + } + } + + private final void escapeblockFast(byte[] buf, Buffer packet, int size) throws SQLException { + int lastwritten = 0; + + for (int i = 0; i < size; i++) { + byte b = buf[i]; + + if (b == '\0') { + // write stuff not yet written + if (i > lastwritten) { + packet.writeBytesNoNull(buf, lastwritten, i - lastwritten); + } + + // write escape + packet.writeByte((byte) '\\'); + packet.writeByte((byte) '0'); + lastwritten = i + 1; + } else { + if ((b == '\\') || (b == '\'') || (!this.usingAnsiMode && b == '"')) { + // write stuff not yet written + if (i > lastwritten) { + packet.writeBytesNoNull(buf, lastwritten, i - lastwritten); + } + + // write escape + packet.writeByte((byte) '\\'); + lastwritten = i; // not i+1 as b wasn't written. + } + } + } + + // write out remaining stuff from buffer + if (lastwritten < size) { + packet.writeBytesNoNull(buf, lastwritten, size - lastwritten); + } + } + + private final void escapeblockFast(byte[] buf, ByteArrayOutputStream bytesOut, int size) { + int lastwritten = 0; + + for (int i = 0; i < size; i++) { + byte b = buf[i]; + + if (b == '\0') { + // write stuff not yet written + if (i > lastwritten) { + bytesOut.write(buf, lastwritten, i - lastwritten); + } + + // write escape + bytesOut.write('\\'); + bytesOut.write('0'); + lastwritten = i + 1; + } else { + if ((b == '\\') || (b == '\'') || (!this.usingAnsiMode && b == '"')) { + // write stuff not yet written + if (i > lastwritten) { + bytesOut.write(buf, lastwritten, i - lastwritten); + } + + // write escape + bytesOut.write('\\'); + lastwritten = i; // not i+1 as b wasn't written. + } + } + } + + // write out remaining stuff from buffer + if (lastwritten < size) { + bytesOut.write(buf, lastwritten, size - lastwritten); + } + } + + /** + * Check to see if the statement is safe for read-only slaves after failover. + * + * @return true if safe for read-only. + * @throws SQLException + */ + protected boolean checkReadOnlySafeStatement() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.firstCharOfStmt == 'S' || !this.connection.isReadOnly(); + } + } + + /** + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate + * + * @return true if the next result is a ResultSet; false if it is an update + * count or there are no more results + * + * @exception SQLException + * if a database access error occurs + */ + public boolean execute() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + MySQLConnection locallyScopedConn = this.connection; + + if (!checkReadOnlySafeStatement()) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + ResultSetInternalMethods rs = null; + + CachedResultSetMetaData cachedMetadata = null; + + this.lastQueryIsOnDupKeyUpdate = false; + + if (this.retrieveGeneratedKeys) { + this.lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL(); + } + + clearWarnings(); + + setupStreamingTimeout(locallyScopedConn); + + this.batchedGeneratedKeys = null; + + Buffer sendPacket = fillSendPacket(); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql); + } + + Field[] metadataFromCache = null; + + if (cachedMetadata != null) { + metadataFromCache = cachedMetadata.fields; + } + + boolean oldInfoMsgState = false; + + if (this.retrieveGeneratedKeys) { + oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); + locallyScopedConn.setReadInfoMsgEnabled(true); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(this.firstCharOfStmt == 'S' ? this.maxRows : -1); + + rs = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), (this.firstCharOfStmt == 'S'), metadataFromCache, false); + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, rs); + } else { + if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, null /* will be created */, rs); + } + } + + if (this.retrieveGeneratedKeys) { + locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState); + rs.setFirstCharOfQuery(this.firstCharOfStmt); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (rs != null) { + this.lastInsertId = rs.getUpdateID(); + + this.results = rs; + } + + return ((rs != null) && rs.reallyResult()); + } + } + + @Override + protected long[] executeBatchInternal() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.connection.isReadOnly()) { + throw new SQLException(Messages.getString("PreparedStatement.25") + Messages.getString("PreparedStatement.26"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + if (this.batchedArgs == null || this.batchedArgs.size() == 0) { + return new long[0]; + } + + // we timeout the entire batch, not individual statements + int batchTimeout = this.timeoutInMillis; + this.timeoutInMillis = 0; + + resetCancelledState(); + + try { + statementBegins(); + + clearWarnings(); + + if (!this.batchHasPlainStatements && this.connection.getRewriteBatchedStatements()) { + + if (canRewriteAsMultiValueInsertAtSqlLevel()) { + return executeBatchedInserts(batchTimeout); + } + + if (this.connection.versionMeetsMinimum(4, 1, 0) && !this.batchHasPlainStatements && this.batchedArgs != null + && this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) { + return executePreparedBatchAsMultiStatement(batchTimeout); + } + } + + return executeBatchSerially(batchTimeout); + } finally { + this.statementExecuting.set(false); + + clearBatch(); + } + } + } + + public boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException { + return this.parseInfo.canRewriteAsMultiValueInsert; + } + + protected int getLocationOfOnDuplicateKeyUpdate() throws SQLException { + return this.parseInfo.locationOfOnDuplicateKeyUpdate; + } + + /** + * Rewrites the already prepared statement into a multi-statement + * query of 'statementsPerBatch' values and executes the entire batch + * using this new statement. + * + * @return update counts in the same fashion as executeBatch() + * + * @throws SQLException + */ + + protected long[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + // This is kind of an abuse, but it gets the job done + if (this.batchedValuesClause == null) { + this.batchedValuesClause = this.originalSql + ";"; + } + + MySQLConnection locallyScopedConn = this.connection; + + boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries(); + CancelTask timeoutTask = null; + + try { + clearWarnings(); + + int numBatchedArgs = this.batchedArgs.size(); + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList(numBatchedArgs); + } + + int numValuesPerBatch = computeBatchSize(numBatchedArgs); + + if (numBatchedArgs < numValuesPerBatch) { + numValuesPerBatch = numBatchedArgs; + } + + java.sql.PreparedStatement batchedStatement = null; + + int batchedParamIndex = 1; + int numberToExecuteAsMultiValue = 0; + int batchCounter = 0; + int updateCountCounter = 0; + long[] updateCounts = new long[numBatchedArgs]; + SQLException sqlEx = null; + + try { + if (!multiQueriesEnabled) { + locallyScopedConn.getIO().enableMultiQueries(); + } + + if (this.retrieveGeneratedKeys) { + batchedStatement = ((Wrapper) locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch), + RETURN_GENERATED_KEYS)).unwrap(java.sql.PreparedStatement.class); + } else { + batchedStatement = ((Wrapper) locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch))) + .unwrap(java.sql.PreparedStatement.class); + } + + if (locallyScopedConn.getEnableQueryTimeouts() && batchTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask((StatementImpl) batchedStatement); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, batchTimeout); + } + + if (numBatchedArgs < numValuesPerBatch) { + numberToExecuteAsMultiValue = numBatchedArgs; + } else { + numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch; + } + + int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch; + + for (int i = 0; i < numberArgsToExecute; i++) { + if (i != 0 && i % numValuesPerBatch == 0) { + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + batchedParamIndex = 1; + } + + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.batchedArgs.get(batchCounter++)); + } + + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + + numValuesPerBatch = numBatchedArgs - batchCounter; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + batchedStatement = null; + } + } + + try { + if (numValuesPerBatch > 0) { + + if (this.retrieveGeneratedKeys) { + batchedStatement = locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch), RETURN_GENERATED_KEYS); + } else { + batchedStatement = locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch)); + } + + if (timeoutTask != null) { + timeoutTask.toCancel = (StatementImpl) batchedStatement; + } + + batchedParamIndex = 1; + + while (batchCounter < numBatchedArgs) { + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.batchedArgs.get(batchCounter++)); + } + + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + } + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + + timeoutTask = null; + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + + return updateCounts; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + + if (!multiQueriesEnabled) { + locallyScopedConn.getIO().disableMultiQueries(); + } + + clearBatch(); + } + } + } + + private String generateMultiStatementForBatch(int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + StringBuilder newStatementSql = new StringBuilder((this.originalSql.length() + 1) * numBatches); + + newStatementSql.append(this.originalSql); + + for (int i = 0; i < numBatches - 1; i++) { + newStatementSql.append(';'); + newStatementSql.append(this.originalSql); + } + + return newStatementSql.toString(); + } + } + + /** + * Rewrites the already prepared statement into a multi-value insert + * statement of 'statementsPerBatch' values and executes the entire batch + * using this new statement. + * + * @return update counts in the same fashion as executeBatch() + * + * @throws SQLException + */ + protected long[] executeBatchedInserts(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String valuesClause = getValuesClause(); + + MySQLConnection locallyScopedConn = this.connection; + + if (valuesClause == null) { + return executeBatchSerially(batchTimeout); + } + + int numBatchedArgs = this.batchedArgs.size(); + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList(numBatchedArgs); + } + + int numValuesPerBatch = computeBatchSize(numBatchedArgs); + + if (numBatchedArgs < numValuesPerBatch) { + numValuesPerBatch = numBatchedArgs; + } + + PreparedStatement batchedStatement = null; + + int batchedParamIndex = 1; + long updateCountRunningTotal = 0; + int numberToExecuteAsMultiValue = 0; + int batchCounter = 0; + CancelTask timeoutTask = null; + SQLException sqlEx = null; + + long[] updateCounts = new long[numBatchedArgs]; + + try { + try { + batchedStatement = /* FIXME -if we ever care about folks proxying our MySQLConnection */ + prepareBatchedInsertSQL(locallyScopedConn, numValuesPerBatch); + + if (locallyScopedConn.getEnableQueryTimeouts() && batchTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(batchedStatement); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, batchTimeout); + } + + if (numBatchedArgs < numValuesPerBatch) { + numberToExecuteAsMultiValue = numBatchedArgs; + } else { + numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch; + } + + int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch; + + for (int i = 0; i < numberArgsToExecute; i++) { + if (i != 0 && i % numValuesPerBatch == 0) { + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + batchedStatement.clearParameters(); + batchedParamIndex = 1; + + } + + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.batchedArgs.get(batchCounter++)); + } + + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + + numValuesPerBatch = numBatchedArgs - batchCounter; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + batchedStatement = null; + } + } + + try { + if (numValuesPerBatch > 0) { + batchedStatement = prepareBatchedInsertSQL(locallyScopedConn, numValuesPerBatch); + + if (timeoutTask != null) { + timeoutTask.toCancel = batchedStatement; + } + + batchedParamIndex = 1; + + while (batchCounter < numBatchedArgs) { + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.batchedArgs.get(batchCounter++)); + } + + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + + if (numBatchedArgs > 1) { + long updCount = updateCountRunningTotal > 0 ? java.sql.Statement.SUCCESS_NO_INFO : 0; + for (int j = 0; j < numBatchedArgs; j++) { + updateCounts[j] = updCount; + } + } else { + updateCounts[0] = updateCountRunningTotal; + } + return updateCounts; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + } + } + } + + protected String getValuesClause() throws SQLException { + return this.parseInfo.valuesClause; + } + + /** + * Computes the optimum number of batched parameter lists to send + * without overflowing max_allowed_packet. + * + * @param numBatchedArgs + * @throws SQLException + */ + protected int computeBatchSize(int numBatchedArgs) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + long[] combinedValues = computeMaxParameterSetSizeAndBatchSize(numBatchedArgs); + + long maxSizeOfParameterSet = combinedValues[0]; + long sizeOfEntireBatch = combinedValues[1]; + + int maxAllowedPacket = this.connection.getMaxAllowedPacket(); + + if (sizeOfEntireBatch < maxAllowedPacket - this.originalSql.length()) { + return numBatchedArgs; + } + + return (int) Math.max(1, (maxAllowedPacket - this.originalSql.length()) / maxSizeOfParameterSet); + } + } + + /** + * Computes the maximum parameter set size, and entire batch size given + * the number of arguments in the batch. + * + * @throws SQLException + */ + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + long sizeOfEntireBatch = 0; + long maxSizeOfParameterSet = 0; + + for (int i = 0; i < numBatchedArgs; i++) { + BatchParams paramArg = (BatchParams) this.batchedArgs.get(i); + + boolean[] isNullBatch = paramArg.isNull; + boolean[] isStreamBatch = paramArg.isStream; + + long sizeOfParameterSet = 0; + + for (int j = 0; j < isNullBatch.length; j++) { + if (!isNullBatch[j]) { + + if (isStreamBatch[j]) { + int streamLength = paramArg.streamLengths[j]; + + if (streamLength != -1) { + sizeOfParameterSet += streamLength * 2; // for safety in escaping + } else { + int paramLength = paramArg.parameterStrings[j].length; + sizeOfParameterSet += paramLength; + } + } else { + sizeOfParameterSet += paramArg.parameterStrings[j].length; + } + } else { + sizeOfParameterSet += 4; // for NULL literal in SQL + } + } + + // + // Account for static part of values clause + // This is a little naive, because the ?s will be replaced but it gives us some padding, and is less housekeeping to ignore them. We're looking + // for a "fuzzy" value here anyway + // + + if (getValuesClause() != null) { + sizeOfParameterSet += getValuesClause().length() + 1; + } else { + sizeOfParameterSet += this.originalSql.length() + 1; + } + + sizeOfEntireBatch += sizeOfParameterSet; + + if (sizeOfParameterSet > maxSizeOfParameterSet) { + maxSizeOfParameterSet = sizeOfParameterSet; + } + } + + return new long[] { maxSizeOfParameterSet, sizeOfEntireBatch }; + } + } + + /** + * Executes the current batch of statements by executing them one-by-one. + * + * @return a list of update counts + * @throws SQLException + * if an error occurs + */ + protected long[] executeBatchSerially(int batchTimeout) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + checkClosed(); + } + + long[] updateCounts = null; + + if (this.batchedArgs != null) { + int nbrCommands = this.batchedArgs.size(); + updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + CancelTask timeoutTask = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && batchTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, batchTimeout); + } + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList(nbrCommands); + } + + for (this.batchCommandIndex = 0; this.batchCommandIndex < nbrCommands; this.batchCommandIndex++) { + Object arg = this.batchedArgs.get(this.batchCommandIndex); + + try { + if (arg instanceof String) { + updateCounts[this.batchCommandIndex] = executeUpdateInternal((String) arg, true, this.retrieveGeneratedKeys); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString((String) arg) ? 1 : 0); + } else { + BatchParams paramArg = (BatchParams) arg; + updateCounts[this.batchCommandIndex] = executeUpdateInternal(paramArg.parameterStrings, paramArg.parameterStreams, + paramArg.isStream, paramArg.streamLengths, paramArg.isNull, true); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(containsOnDuplicateKeyUpdateInSQL() ? 1 : 0); + } + } catch (SQLException ex) { + updateCounts[this.batchCommandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[this.batchCommandIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, this.batchCommandIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + } + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + } catch (NullPointerException npe) { + try { + checkClosed(); + } catch (SQLException connectionClosedEx) { + updateCounts[this.batchCommandIndex] = EXECUTE_FAILED; + + long[] newUpdateCounts = new long[this.batchCommandIndex]; + + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, this.batchCommandIndex); + + throw SQLError.createBatchUpdateException(connectionClosedEx, newUpdateCounts, getExceptionInterceptor()); + } + + throw npe; // we don't know why this happened, punt + } finally { + this.batchCommandIndex = -1; + + if (timeoutTask != null) { + timeoutTask.cancel(); + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + } + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } + + } + + public String getDateTime(String pattern) { + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + return sdf.format(new java.util.Date()); + } + + /** + * Actually execute the prepared statement. This is here so server-side + * PreparedStatements can re-use most of the code from this class. + * + * @param maxRowsToRetrieve + * the max number of rows to return + * @param sendPacket + * the packet to send + * @param createStreamingResultSet + * should a 'streaming' result set be created? + * @param queryIsSelectOnly + * is this query doing a SELECT? + * @param unpackFields + * + * @return the results as a ResultSet + * + * @throws SQLException + * if an error occurs. + */ + protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, Buffer sendPacket, boolean createStreamingResultSet, boolean queryIsSelectOnly, + Field[] metadataFromCache, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + + resetCancelledState(); + + MySQLConnection locallyScopedConnection = this.connection; + + this.numberOfExecutions++; + + if (this.doPingInstead) { + doPingInstead(); + + return this.results; + } + + ResultSetInternalMethods rs; + + CancelTask timeoutTask = null; + + try { + if (locallyScopedConnection.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && locallyScopedConnection.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConnection.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis); + } + + if (!isBatch) { + statementBegins(); + } + + rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket, this.resultSetType, this.resultSetConcurrency, + createStreamingResultSet, this.currentCatalog, metadataFromCache, isBatch); + + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConnection.getCancelTimer().purge(); + + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + SQLException cause = null; + + if (this.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + resetCancelledState(); + + throw cause; + } + } + } finally { + if (!isBatch) { + this.statementExecuting.set(false); + } + + if (timeoutTask != null) { + timeoutTask.cancel(); + locallyScopedConnection.getCancelTimer().purge(); + } + } + + return rs; + } catch (NullPointerException npe) { + checkClosed(); // we can't synchronize ourselves against async connection-close due to deadlock issues, so this is the next best thing for + // this particular corner case. + + throw npe; + } + } + } + + /** + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the query - never + * null + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.ResultSet executeQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + MySQLConnection locallyScopedConn = this.connection; + + checkForDml(this.originalSql, this.firstCharOfStmt); + + CachedResultSetMetaData cachedMetadata = null; + + clearWarnings(); + + this.batchedGeneratedKeys = null; + + setupStreamingTimeout(locallyScopedConn); + + Buffer sendPacket = fillSendPacket(); + + implicitlyCloseAllOpenResults(); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql); + } + + Field[] metadataFromCache = null; + + if (cachedMetadata != null) { + metadataFromCache = cachedMetadata.fields; + } + + locallyScopedConn.setSessionMaxRows(this.maxRows); + + this.results = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), true, metadataFromCache, false); + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, this.results); + } else { + if (locallyScopedConn.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, null /* will be created */, this.results); + } + } + + this.lastInsertId = this.results.getUpdateID(); + + return this.results; + } + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, SQL + * statements that return nothing such as SQL DDL statements can be + * executed. + * + * @return either the row count for INSERT, UPDATE or DELETE; or 0 for SQL + * statements that return nothing. + * + * @exception SQLException + * if a database access error occurs + */ + public int executeUpdate() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate()); + } + + /* + * We need this variant, because ServerPreparedStatement calls this for + * batched updates, which will end up clobbering the warnings and generated + * keys we need to gather for the batch. + */ + protected long executeUpdateInternal(boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (clearBatchedGeneratedKeysAndWarnings) { + clearWarnings(); + this.batchedGeneratedKeys = null; + } + + return executeUpdateInternal(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull, isBatch); + } + } + + /** + * Added to allow batch-updates + * + * @param batchedParameterStrings + * string values used in single statement + * @param batchedParameterStreams + * stream values used in single statement + * @param batchedIsStream + * flags for streams used in single statement + * @param batchedStreamLengths + * lengths of streams to be read. + * @param batchedIsNull + * flags for parameters that are null + * + * @return the update count + * + * @throws SQLException + * if a database error occurs + */ + protected long executeUpdateInternal(byte[][] batchedParameterStrings, InputStream[] batchedParameterStreams, boolean[] batchedIsStream, + int[] batchedStreamLengths, boolean[] batchedIsNull, boolean isReallyBatch) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn.isReadOnly(false)) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") + Messages.getString("PreparedStatement.35"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if ((this.firstCharOfStmt == 'S') && isSelectQuery()) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), "01S03", getExceptionInterceptor()); + } + + implicitlyCloseAllOpenResults(); + + ResultSetInternalMethods rs = null; + + Buffer sendPacket = fillSendPacket(batchedParameterStrings, batchedParameterStreams, batchedIsStream, batchedStreamLengths); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(-1); + + boolean oldInfoMsgState = false; + + if (this.retrieveGeneratedKeys) { + oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); + locallyScopedConn.setReadInfoMsgEnabled(true); + } + + rs = executeInternal(-1, sendPacket, false, false, null, isReallyBatch); + + if (this.retrieveGeneratedKeys) { + locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState); + rs.setFirstCharOfQuery(this.firstCharOfStmt); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + this.results = rs; + + this.updateCount = rs.getUpdateCount(); + + if (containsOnDuplicateKeyUpdateInSQL() && this.compensateForOnDuplicateKeyUpdate) { + if (this.updateCount == 2 || this.updateCount == 0) { + this.updateCount = 1; + } + } + + this.lastInsertId = rs.getUpdateID(); + + return this.updateCount; + } + } + + protected boolean containsOnDuplicateKeyUpdateInSQL() { + return this.parseInfo.isOnDuplicateKeyUpdate; + } + + /** + * Creates the packet that contains the query to be sent to the server. + * + * @return A Buffer filled with the query representing the + * PreparedStatement. + * + * @throws SQLException + * if an error occurs. + */ + protected Buffer fillSendPacket() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return fillSendPacket(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths); + } + } + + /** + * Creates the packet that contains the query to be sent to the server. + * + * @param batchedParameterStrings + * the parameters as strings + * @param batchedParameterStreams + * the parameters as streams + * @param batchedIsStream + * is the given parameter a stream? + * @param batchedStreamLengths + * the lengths of the streams (if appropriate) + * + * @return a Buffer filled with the query that represents this statement + * + * @throws SQLException + * if an error occurs. + */ + protected Buffer fillSendPacket(byte[][] batchedParameterStrings, InputStream[] batchedParameterStreams, boolean[] batchedIsStream, + int[] batchedStreamLengths) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Buffer sendPacket = this.connection.getIO().getSharedSendPacket(); + + sendPacket.clear(); + + sendPacket.writeByte((byte) MysqlDefs.QUERY); + + boolean useStreamLengths = this.connection.getUseStreamLengthsInPrepStmts(); + + // + // Try and get this allocation as close as possible for BLOBs + // + int ensurePacketSize = 0; + + String statementComment = this.connection.getStatementComment(); + + byte[] commentAsBytes = null; + + if (statementComment != null) { + if (this.charConverter != null) { + commentAsBytes = this.charConverter.toBytes(statementComment); + } else { + commentAsBytes = StringUtils.getBytes(statementComment, this.charConverter, this.charEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } + + ensurePacketSize += commentAsBytes.length; + ensurePacketSize += 6; // for /*[space] [space]*/ + } + + for (int i = 0; i < batchedParameterStrings.length; i++) { + if (batchedIsStream[i] && useStreamLengths) { + ensurePacketSize += batchedStreamLengths[i]; + } + } + + if (ensurePacketSize != 0) { + sendPacket.ensureCapacity(ensurePacketSize); + } + + if (commentAsBytes != null) { + sendPacket.writeBytesNoNull(Constants.SLASH_STAR_SPACE_AS_BYTES); + sendPacket.writeBytesNoNull(commentAsBytes); + sendPacket.writeBytesNoNull(Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES); + } + + for (int i = 0; i < batchedParameterStrings.length; i++) { + checkAllParametersSet(batchedParameterStrings[i], batchedParameterStreams[i], i); + + sendPacket.writeBytesNoNull(this.staticSqlStrings[i]); + + if (batchedIsStream[i]) { + streamToBytes(sendPacket, batchedParameterStreams[i], true, batchedStreamLengths[i], useStreamLengths); + } else { + sendPacket.writeBytesNoNull(batchedParameterStrings[i]); + } + } + + sendPacket.writeBytesNoNull(this.staticSqlStrings[batchedParameterStrings.length]); + + return sendPacket; + } + } + + private void checkAllParametersSet(byte[] parameterString, InputStream parameterStream, int columnIndex) throws SQLException { + if ((parameterString == null) && parameterStream == null) { + + throw SQLError.createSQLException(Messages.getString("PreparedStatement.40") + (columnIndex + 1), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS, + getExceptionInterceptor()); + } + } + + /** + * Returns a prepared statement for the number of batched parameters, used when re-writing batch INSERTs. + */ + protected PreparedStatement prepareBatchedInsertSQL(MySQLConnection localConn, int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + PreparedStatement pstmt = new PreparedStatement(localConn, "Rewritten batch of: " + this.originalSql, this.currentCatalog, + this.parseInfo.getParseInfoForBatch(numBatches)); + pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys); + pstmt.rewrittenBatchSize = numBatches; + + return pstmt; + } + } + + protected void setRetrieveGeneratedKeys(boolean flag) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.retrieveGeneratedKeys = flag; + } + } + + protected int rewrittenBatchSize = 0; + + public int getRewrittenBatchSize() { + return this.rewrittenBatchSize; + } + + public String getNonRewrittenSql() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + int indexOfBatch = this.originalSql.indexOf(" of: "); + + if (indexOfBatch != -1) { + return this.originalSql.substring(indexOfBatch + 5); + } + + return this.originalSql; + } + } + + /** + * @param parameterIndex + * + * @throws SQLException + */ + public byte[] getBytesRepresentation(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.isStream[parameterIndex]) { + return streamToBytes(this.parameterStreams[parameterIndex], false, this.streamLengths[parameterIndex], + this.connection.getUseStreamLengthsInPrepStmts()); + } + + byte[] parameterVal = this.parameterValues[parameterIndex]; + + if (parameterVal == null) { + return null; + } + + if ((parameterVal[0] == '\'') && (parameterVal[parameterVal.length - 1] == '\'')) { + byte[] valNoQuotes = new byte[parameterVal.length - 2]; + System.arraycopy(parameterVal, 1, valNoQuotes, 0, parameterVal.length - 2); + + return valNoQuotes; + } + + return parameterVal; + } + } + + /** + * Get bytes representation for a parameter in a statement batch. + * + * @param parameterIndex + * @param commandIndex + * @throws SQLException + */ + protected byte[] getBytesRepresentationForBatch(int parameterIndex, int commandIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Object batchedArg = this.batchedArgs.get(commandIndex); + if (batchedArg instanceof String) { + try { + return (StringUtils.getBytes((String) batchedArg, this.charEncoding)); + + } catch (UnsupportedEncodingException uue) { + throw new RuntimeException(Messages.getString("PreparedStatement.32") + this.charEncoding + Messages.getString("PreparedStatement.33")); + } + } + + BatchParams params = (BatchParams) batchedArg; + if (params.isStream[parameterIndex]) { + return streamToBytes(params.parameterStreams[parameterIndex], false, params.streamLengths[parameterIndex], + this.connection.getUseStreamLengthsInPrepStmts()); + } + byte parameterVal[] = params.parameterStrings[parameterIndex]; + if (parameterVal == null) { + return null; + } + + if ((parameterVal[0] == '\'') && (parameterVal[parameterVal.length - 1] == '\'')) { + byte[] valNoQuotes = new byte[parameterVal.length - 2]; + System.arraycopy(parameterVal, 1, valNoQuotes, 0, parameterVal.length - 2); + + return valNoQuotes; + } + + return parameterVal; + } + } + + // --------------------------JDBC 2.0----------------------------- + + private final String getDateTimePattern(String dt, boolean toTime) throws Exception { + // + // Special case + // + int dtLength = (dt != null) ? dt.length() : 0; + + if ((dtLength >= 8) && (dtLength <= 10)) { + int dashCount = 0; + boolean isDateOnly = true; + + for (int i = 0; i < dtLength; i++) { + char c = dt.charAt(i); + + if (!Character.isDigit(c) && (c != '-')) { + isDateOnly = false; + + break; + } + + if (c == '-') { + dashCount++; + } + } + + if (isDateOnly && (dashCount == 2)) { + return "yyyy-MM-dd"; + } + } + + // + // Special case - time-only + // + boolean colonsOnly = true; + + for (int i = 0; i < dtLength; i++) { + char c = dt.charAt(i); + + if (!Character.isDigit(c) && (c != ':')) { + colonsOnly = false; + + break; + } + } + + if (colonsOnly) { + return "HH:mm:ss"; + } + + int n; + int z; + int count; + int maxvecs; + char c; + char separator; + StringReader reader = new StringReader(dt + " "); + ArrayList vec = new ArrayList(); + ArrayList vecRemovelist = new ArrayList(); + Object[] nv = new Object[3]; + Object[] v; + nv[0] = Character.valueOf('y'); + nv[1] = new StringBuilder(); + nv[2] = Integer.valueOf(0); + vec.add(nv); + + if (toTime) { + nv = new Object[3]; + nv[0] = Character.valueOf('h'); + nv[1] = new StringBuilder(); + nv[2] = Integer.valueOf(0); + vec.add(nv); + } + + while ((z = reader.read()) != -1) { + separator = (char) z; + maxvecs = vec.size(); + + for (count = 0; count < maxvecs; count++) { + v = vec.get(count); + n = ((Integer) v[2]).intValue(); + c = getSuccessor(((Character) v[0]).charValue(), n); + + if (!Character.isLetterOrDigit(separator)) { + if ((c == ((Character) v[0]).charValue()) && (c != 'S')) { + vecRemovelist.add(v); + } else { + ((StringBuilder) v[1]).append(separator); + + if ((c == 'X') || (c == 'Y')) { + v[2] = Integer.valueOf(4); + } + } + } else { + if (c == 'X') { + c = 'y'; + nv = new Object[3]; + nv[1] = (new StringBuilder(((StringBuilder) v[1]).toString())).append('M'); + nv[0] = Character.valueOf('M'); + nv[2] = Integer.valueOf(1); + vec.add(nv); + } else if (c == 'Y') { + c = 'M'; + nv = new Object[3]; + nv[1] = (new StringBuilder(((StringBuilder) v[1]).toString())).append('d'); + nv[0] = Character.valueOf('d'); + nv[2] = Integer.valueOf(1); + vec.add(nv); + } + + ((StringBuilder) v[1]).append(c); + + if (c == ((Character) v[0]).charValue()) { + v[2] = Integer.valueOf(n + 1); + } else { + v[0] = Character.valueOf(c); + v[2] = Integer.valueOf(1); + } + } + } + + int size = vecRemovelist.size(); + + for (int i = 0; i < size; i++) { + v = vecRemovelist.get(i); + vec.remove(v); + } + + vecRemovelist.clear(); + } + + int size = vec.size(); + + for (int i = 0; i < size; i++) { + v = vec.get(i); + c = ((Character) v[0]).charValue(); + n = ((Integer) v[2]).intValue(); + + boolean bk = getSuccessor(c, n) != c; + boolean atEnd = (((c == 's') || (c == 'm') || ((c == 'h') && toTime)) && bk); + boolean finishesAtDate = (bk && (c == 'd') && !toTime); + boolean containsEnd = (((StringBuilder) v[1]).toString().indexOf('W') != -1); + + if ((!atEnd && !finishesAtDate) || (containsEnd)) { + vecRemovelist.add(v); + } + } + + size = vecRemovelist.size(); + + for (int i = 0; i < size; i++) { + vec.remove(vecRemovelist.get(i)); + } + + vecRemovelist.clear(); + v = vec.get(0); // might throw exception + + StringBuilder format = (StringBuilder) v[1]; + format.setLength(format.length() - 1); + + return format.toString(); + } + + /** + * The number, types and properties of a ResultSet's columns are provided by + * the getMetaData method. + * + * @return the description of a ResultSet's columns + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + // + // We could just tack on a LIMIT 0 here no matter what the statement, and check if a result set was returned or not, but I'm not comfortable with + // that, myself, so we take the "safer" road, and only allow metadata for _actual_ SELECTS (but not SHOWs). + // + // CALL's are trapped further up and you end up with a CallableStatement anyway. + // + + if (!isSelectQuery()) { + return null; + } + + PreparedStatement mdStmt = null; + java.sql.ResultSet mdRs = null; + + if (this.pstmtResultMetaData == null) { + try { + mdStmt = new PreparedStatement(this.connection, this.originalSql, this.currentCatalog, this.parseInfo); + + mdStmt.setMaxRows(1); + + int paramCount = this.parameterValues.length; + + for (int i = 1; i <= paramCount; i++) { + mdStmt.setString(i, ""); + } + + boolean hadResults = mdStmt.execute(); + + if (hadResults) { + mdRs = mdStmt.getResultSet(); + + this.pstmtResultMetaData = mdRs.getMetaData(); + } else { + this.pstmtResultMetaData = new ResultSetMetaData(new Field[0], this.connection.getUseOldAliasMetadataBehavior(), + this.connection.getYearIsDateType(), getExceptionInterceptor()); + } + } finally { + SQLException sqlExRethrow = null; + + if (mdRs != null) { + try { + mdRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + mdRs = null; + } + + if (mdStmt != null) { + try { + mdStmt.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + mdStmt = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + } + + return this.pstmtResultMetaData; + } + } + + protected boolean isSelectQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return StringUtils.startsWithIgnoreCaseAndWs(StringUtils.stripComments(this.originalSql, "'\"", "'\"", true, false, true, true), "SELECT"); + } + } + + /** + * @see PreparedStatement#getParameterMetaData() + */ + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.parameterMetaData == null) { + if (this.connection.getGenerateSimpleParameterMetadata()) { + this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount); + } else { + this.parameterMetaData = new MysqlParameterMetadata(null, this.parameterCount, getExceptionInterceptor()); + } + } + + return this.parameterMetaData; + } + } + + ParseInfo getParseInfo() { + return this.parseInfo; + } + + private final char getSuccessor(char c, int n) { + return ((c == 'y') && (n == 2)) ? 'X' + : (((c == 'y') && (n < 4)) ? 'y' : ((c == 'y') ? 'M' : (((c == 'M') && (n == 2)) ? 'Y' + : (((c == 'M') && (n < 3)) ? 'M' : ((c == 'M') ? 'd' : (((c == 'd') && (n < 2)) ? 'd' : ((c == 'd') ? 'H' : (((c == 'H') && (n < 2)) + ? 'H' + : ((c == 'H') ? 'm' : (((c == 'm') && (n < 2)) ? 'm' : ((c == 'm') ? 's' : (((c == 's') && (n < 2)) ? 's' : 'W')))))))))))); + } + + /** + * Used to escape binary data with hex for mb charsets + * + * @param buf + * @param packet + * @param size + * @throws SQLException + */ + private final void hexEscapeBlock(byte[] buf, Buffer packet, int size) throws SQLException { + for (int i = 0; i < size; i++) { + byte b = buf[i]; + int lowBits = (b & 0xff) / 16; + int highBits = (b & 0xff) % 16; + + packet.writeByte(HEX_DIGITS[lowBits]); + packet.writeByte(HEX_DIGITS[highBits]); + } + } + + private void initializeFromParseInfo() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.staticSqlStrings = this.parseInfo.staticSql; + this.isLoadDataQuery = this.parseInfo.foundLoadData; + this.firstCharOfStmt = this.parseInfo.firstStmtChar; + + this.parameterCount = this.staticSqlStrings.length - 1; + + this.parameterValues = new byte[this.parameterCount][]; + this.parameterStreams = new InputStream[this.parameterCount]; + this.isStream = new boolean[this.parameterCount]; + this.streamLengths = new int[this.parameterCount]; + this.isNull = new boolean[this.parameterCount]; + this.parameterTypes = new int[this.parameterCount]; + + clearParameters(); + + for (int j = 0; j < this.parameterCount; j++) { + this.isStream[j] = false; + } + } + } + + boolean isNull(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.isNull[paramIndex]; + } + } + + private final int readblock(InputStream i, byte[] b) throws SQLException { + try { + return i.read(b); + } catch (Throwable ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.56") + ex.getClass().getName(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + private final int readblock(InputStream i, byte[] b, int length) throws SQLException { + try { + int lengthToRead = length; + + if (lengthToRead > b.length) { + lengthToRead = b.length; + } + + return i.read(b, 0, lengthToRead); + } catch (Throwable ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.56") + ex.getClass().getName(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + /** + * Closes this statement, releasing all resources + * + * @param calledExplicitly + * was this called by close()? + * + * @throws SQLException + * if an error occurs + */ + @Override + protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + // additional check in case Statement was closed + // while current thread was waiting for lock + if (this.isClosed) { + return; + } + + if (this.useUsageAdvisor) { + if (this.numberOfExecutions <= 1) { + String message = Messages.getString("PreparedStatement.43"); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", this.currentCatalog, this.connectionId, this.getId(), -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + super.realClose(calledExplicitly, closeOpenResults); + + this.dbmd = null; + this.originalSql = null; + this.staticSqlStrings = null; + this.parameterValues = null; + this.parameterStreams = null; + this.isStream = null; + this.streamLengths = null; + this.isNull = null; + this.streamConvertBuf = null; + this.parameterTypes = null; + } + } + + /** + * JDBC 2.0 Set an Array parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing an SQL array + * + * @throws SQLException + * because this method is not implemented. + * @throws NotImplemented + */ + public void setArray(int i, Array x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * When a very large ASCII value is input to a LONGVARCHAR parameter, it may + * be more practical to send it via a java.io.InputStream. JDBC will read + * the data from the stream as needed, until it reaches end-of-file. The + * JDBC driver will do any necessary conversion from ASCII to the database + * char format. + * + *

    + * Note: This stream object can either be a standard Java stream object or your own subclass that implements the standard interface. + *

    + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * @param length + * the number of bytes in the stream + * + * @exception SQLException + * if a database access error occurs + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.VARCHAR); + } else { + setBinaryStream(parameterIndex, x, length); + } + } + + /** + * Set a parameter to a java.math.BigDecimal value. The driver converts this + * to a SQL NUMERIC value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.DECIMAL); + } else { + setInternal(parameterIndex, StringUtils.fixDecimalExponent(StringUtils.consistentToString(x))); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DECIMAL; + } + } + + /** + * When a very large binary value is input to a LONGVARBINARY parameter, it + * may be more practical to send it via a java.io.InputStream. JDBC will + * read the data from the stream as needed, until it reaches end-of-file. + * + *

    + * Note: This stream object can either be a standard Java stream object or your own subclass that implements the standard interface. + *

    + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * @param length + * the number of bytes to read from the stream (ignored) + * + * @throws SQLException + * if a database access error occurs + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + int parameterIndexOffset = getParameterIndexOffset(); + + if ((parameterIndex < 1) || (parameterIndex > this.staticSqlStrings.length)) { + throw SQLError.createSQLException( + Messages.getString("PreparedStatement.2") + parameterIndex + Messages.getString("PreparedStatement.3") + + this.staticSqlStrings.length + Messages.getString("PreparedStatement.4"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } else if (parameterIndexOffset == -1 && parameterIndex == 1) { + throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + this.parameterStreams[parameterIndex - 1 + parameterIndexOffset] = x; + this.isStream[parameterIndex - 1 + parameterIndexOffset] = true; + this.streamLengths[parameterIndex - 1 + parameterIndexOffset] = length; + this.isNull[parameterIndex - 1 + parameterIndexOffset] = false; + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BLOB; + } + } + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + setBinaryStream(parameterIndex, inputStream, (int) length); + } + + /** + * JDBC 2.0 Set a BLOB parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing a BLOB + * + * @throws SQLException + * if a database error occurs + */ + public void setBlob(int i, java.sql.Blob x) throws SQLException { + if (x == null) { + setNull(i, Types.BLOB); + } else { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + + bytesOut.write('\''); + escapeblockFast(x.getBytes(1, (int) x.length()), bytesOut, (int) x.length()); + bytesOut.write('\''); + + setInternal(i, bytesOut.toByteArray()); + + this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.BLOB; + } + } + + /** + * Set a parameter to a Java boolean value. The driver converts this to a + * SQL BIT value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @throws SQLException + * if a database access error occurs + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + if (this.useTrueBoolean) { + setInternal(parameterIndex, x ? "1" : "0"); + } else { + setInternal(parameterIndex, x ? "'t'" : "'f'"); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BOOLEAN; + } + } + + /** + * Set a parameter to a Java byte value. The driver converts this to a SQL + * TINYINT value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setByte(int parameterIndex, byte x) throws SQLException { + setInternal(parameterIndex, String.valueOf(x)); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TINYINT; + } + + /** + * Set a parameter to a Java array of bytes. The driver converts this to a + * SQL VARBINARY or LONGVARBINARY (depending on the argument's size relative + * to the driver's limits on VARBINARYs) when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + setBytes(parameterIndex, x, true, true); + + if (x != null) { + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BINARY; + } + } + + protected void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + String connectionEncoding = this.connection.getEncoding(); + + try { + if (this.connection.isNoBackslashEscapesSet() || (escapeForMBChars && this.connection.getUseUnicode() && connectionEncoding != null + && CharsetMapping.isMultibyteCharset(connectionEncoding))) { + + // Send as hex + + ByteArrayOutputStream bOut = new ByteArrayOutputStream((x.length * 2) + 3); + bOut.write('x'); + bOut.write('\''); + + for (int i = 0; i < x.length; i++) { + int lowBits = (x[i] & 0xff) / 16; + int highBits = (x[i] & 0xff) % 16; + + bOut.write(HEX_DIGITS[lowBits]); + bOut.write(HEX_DIGITS[highBits]); + } + + bOut.write('\''); + + setInternal(parameterIndex, bOut.toByteArray()); + + return; + } + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + // escape them + int numBytes = x.length; + + int pad = 2; + + boolean needsIntroducer = checkForIntroducer && this.connection.versionMeetsMinimum(4, 1, 0); + + if (needsIntroducer) { + pad += 7; + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(numBytes + pad); + + if (needsIntroducer) { + bOut.write('_'); + bOut.write('b'); + bOut.write('i'); + bOut.write('n'); + bOut.write('a'); + bOut.write('r'); + bOut.write('y'); + } + bOut.write('\''); + + for (int i = 0; i < numBytes; ++i) { + byte b = x[i]; + + switch (b) { + case 0: /* Must be escaped for 'mysql' */ + bOut.write('\\'); + bOut.write('0'); + + break; + + case '\n': /* Must be escaped for logs */ + bOut.write('\\'); + bOut.write('n'); + + break; + + case '\r': + bOut.write('\\'); + bOut.write('r'); + + break; + + case '\\': + bOut.write('\\'); + bOut.write('\\'); + + break; + + case '\'': + bOut.write('\\'); + bOut.write('\''); + + break; + + case '"': /* Better safe than sorry */ + bOut.write('\\'); + bOut.write('"'); + + break; + + case '\032': /* This gives problems on Win32 */ + bOut.write('\\'); + bOut.write('Z'); + + break; + + default: + bOut.write(b); + } + } + + bOut.write('\''); + + setInternal(parameterIndex, bOut.toByteArray()); + } + } + } + + /** + * Used by updatable result sets for refreshRow() because the parameter has + * already been escaped for updater or inserter prepared statements. + * + * @param parameterIndex + * the parameter to set. + * @param parameterAsBytes + * the parameter as a string. + * + * @throws SQLException + * if an error occurs + */ + protected void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes) throws SQLException { + byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2]; + parameterWithQuotes[0] = '\''; + System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1, parameterAsBytes.length); + parameterWithQuotes[parameterAsBytes.length + 1] = '\''; + + setInternal(parameterIndex, parameterWithQuotes); + } + + protected void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes) throws SQLException { + setInternal(parameterIndex, parameterAsBytes); + } + + /** + * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR + * parameter, it may be more practical to send it via a java.io.Reader. JDBC + * will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * UNICODE to the database char format. + * + *

    + * Note: This stream object can either be a standard Java stream object or your own subclass that implements the standard interface. + *

    + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param reader + * the java reader which contains the UNICODE data + * @param length + * the number of characters in the stream + * + * @exception SQLException + * if a database-access error occurs. + */ + public void setCharacterStream(int parameterIndex, java.io.Reader reader, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + if (reader == null) { + setNull(parameterIndex, Types.LONGVARCHAR); + } else { + char[] c = null; + int len = 0; + + boolean useLength = this.connection.getUseStreamLengthsInPrepStmts(); + + String forcedEncoding = this.connection.getClobCharacterEncoding(); + + if (useLength && (length != -1)) { + c = new char[length]; + + int numCharsRead = readFully(reader, c, length); // blocks until all read + + if (forcedEncoding == null) { + setString(parameterIndex, new String(c, 0, numCharsRead)); + } else { + try { + setBytes(parameterIndex, StringUtils.getBytes(new String(c, 0, numCharsRead), forcedEncoding)); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + } else { + c = new char[4096]; + + StringBuilder buf = new StringBuilder(); + + while ((len = reader.read(c)) != -1) { + buf.append(c, 0, len); + } + + if (forcedEncoding == null) { + setString(parameterIndex, buf.toString()); + } else { + try { + setBytes(parameterIndex, StringUtils.getBytes(buf.toString(), forcedEncoding)); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + } + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB; + } + } catch (java.io.IOException ioEx) { + throw SQLError.createSQLException(ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + } + + /** + * JDBC 2.0 Set a CLOB parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing a CLOB + * + * @throws SQLException + * if a database error occurs + */ + public void setClob(int i, Clob x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (x == null) { + setNull(i, Types.CLOB); + } else { + + String forcedEncoding = this.connection.getClobCharacterEncoding(); + + if (forcedEncoding == null) { + setString(i, x.getSubString(1L, (int) x.length())); + } else { + try { + setBytes(i, StringUtils.getBytes(x.getSubString(1L, (int) x.length()), forcedEncoding)); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.CLOB; + } + } + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this to a + * SQL DATE value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + public void setDate(int parameterIndex, java.sql.Date x) throws java.sql.SQLException { + setDate(parameterIndex, x, null); + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this to a + * SQL DATE value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the calendar to interpret the date with + * + * @exception SQLException + * if a database-access error occurs. + */ + public void setDate(int parameterIndex, java.sql.Date x, Calendar cal) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.DATE); + } else { + if (!this.useLegacyDatetimeCode) { + newSetDateInternal(parameterIndex, x, cal); + } else { + synchronized (checkClosed().getConnectionMutex()) { + if (this.ddf == null) { + this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US); + } + if (cal != null) { + this.ddf.setTimeZone(cal.getTimeZone()); + } + + setInternal(parameterIndex, this.ddf.format(x)); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATE; + } + } + } + } + + /** + * Set a parameter to a Java double value. The driver converts this to a SQL + * DOUBLE value when it sends it to the database + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setDouble(int parameterIndex, double x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.connection.getAllowNanAndInf() && (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) { + throw SQLError.createSQLException("'" + x + "' is not a valid numeric or approximate numeric value", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + } + + setInternal(parameterIndex, StringUtils.fixDecimalExponent(String.valueOf(x))); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DOUBLE; + } + } + + /** + * Set a parameter to a Java float value. The driver converts this to a SQL + * FLOAT value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setFloat(int parameterIndex, float x) throws SQLException { + setInternal(parameterIndex, StringUtils.fixDecimalExponent(String.valueOf(x))); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.FLOAT; + } + + /** + * Set a parameter to a Java int value. The driver converts this to a SQL + * INTEGER value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setInt(int parameterIndex, int x) throws SQLException { + setInternal(parameterIndex, String.valueOf(x)); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.INTEGER; + } + + protected final void setInternal(int paramIndex, byte[] val) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + int parameterIndexOffset = getParameterIndexOffset(); + + checkBounds(paramIndex, parameterIndexOffset); + + this.isStream[paramIndex - 1 + parameterIndexOffset] = false; + this.isNull[paramIndex - 1 + parameterIndexOffset] = false; + this.parameterStreams[paramIndex - 1 + parameterIndexOffset] = null; + this.parameterValues[paramIndex - 1 + parameterIndexOffset] = val; + } + } + + protected void checkBounds(int paramIndex, int parameterIndexOffset) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((paramIndex < 1)) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.49") + paramIndex + Messages.getString("PreparedStatement.50"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } else if (paramIndex > this.parameterCount) { + throw SQLError + .createSQLException( + Messages.getString("PreparedStatement.51") + paramIndex + Messages.getString("PreparedStatement.52") + + (this.parameterValues.length) + Messages.getString("PreparedStatement.53"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } else if (parameterIndexOffset == -1 && paramIndex == 1) { + throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + } + + protected final void setInternal(int paramIndex, String val) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + byte[] parameterAsBytes = null; + + if (this.charConverter != null) { + parameterAsBytes = this.charConverter.toBytes(val); + } else { + parameterAsBytes = StringUtils.getBytes(val, this.charConverter, this.charEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } + + setInternal(paramIndex, parameterAsBytes); + } + } + + /** + * Set a parameter to a Java long value. The driver converts this to a SQL + * BIGINT value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setLong(int parameterIndex, long x) throws SQLException { + setInternal(parameterIndex, String.valueOf(x)); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BIGINT; + } + + /** + * Set a parameter to SQL NULL + * + *

    + * Note: You must specify the parameters SQL type (although MySQL ignores it) + *

    + * + * @param parameterIndex + * the first parameter is 1, etc... + * @param sqlType + * the SQL type code defined in java.sql.Types + * + * @exception SQLException + * if a database access error occurs + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setInternal(parameterIndex, "null"); + this.isNull[parameterIndex - 1 + getParameterIndexOffset()] = true; + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL; + } + } + + /** + * Set a parameter to SQL NULL. + * + *

    + * Note: You must specify the parameter's SQL type. + *

    + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param sqlType + * SQL type code defined by java.sql.Types + * @param arg + * argument parameters for null + * + * @exception SQLException + * if a database-access error occurs. + */ + public void setNull(int parameterIndex, int sqlType, String arg) throws SQLException { + setNull(parameterIndex, sqlType); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL; + } + + private void setNumericObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException { + Number parameterAsNum; + + if (parameterObj instanceof Boolean) { + parameterAsNum = ((Boolean) parameterObj).booleanValue() ? Integer.valueOf(1) : Integer.valueOf(0); + } else if (parameterObj instanceof String) { + switch (targetSqlType) { + case Types.BIT: + if ("1".equals(parameterObj) || "0".equals(parameterObj)) { + parameterAsNum = Integer.valueOf((String) parameterObj); + } else { + boolean parameterAsBoolean = "true".equalsIgnoreCase((String) parameterObj); + + parameterAsNum = parameterAsBoolean ? Integer.valueOf(1) : Integer.valueOf(0); + } + + break; + + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + parameterAsNum = Integer.valueOf((String) parameterObj); + + break; + + case Types.BIGINT: + parameterAsNum = Long.valueOf((String) parameterObj); + + break; + + case Types.REAL: + parameterAsNum = Float.valueOf((String) parameterObj); + + break; + + case Types.FLOAT: + case Types.DOUBLE: + parameterAsNum = Double.valueOf((String) parameterObj); + + break; + + case Types.DECIMAL: + case Types.NUMERIC: + default: + parameterAsNum = new java.math.BigDecimal((String) parameterObj); + } + } else { + parameterAsNum = (Number) parameterObj; + } + + switch (targetSqlType) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + setInt(parameterIndex, parameterAsNum.intValue()); + + break; + + case Types.BIGINT: + setLong(parameterIndex, parameterAsNum.longValue()); + + break; + + case Types.REAL: + setFloat(parameterIndex, parameterAsNum.floatValue()); + + break; + + case Types.FLOAT: + case Types.DOUBLE: + setDouble(parameterIndex, parameterAsNum.doubleValue()); + + break; + + case Types.DECIMAL: + case Types.NUMERIC: + + if (parameterAsNum instanceof java.math.BigDecimal) { + BigDecimal scaledBigDecimal = null; + + try { + scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale); + } catch (ArithmeticException ex) { + try { + scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx) { + throw SQLError.createSQLException("Can't set scale of '" + scale + "' for DECIMAL argument '" + parameterAsNum + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + setBigDecimal(parameterIndex, scaledBigDecimal); + } else if (parameterAsNum instanceof java.math.BigInteger) { + setBigDecimal(parameterIndex, new java.math.BigDecimal((java.math.BigInteger) parameterAsNum, scale)); + } else { + setBigDecimal(parameterIndex, new java.math.BigDecimal(parameterAsNum.doubleValue())); + } + + break; + } + } + + public void setObject(int parameterIndex, Object parameterObj) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (parameterObj == null) { + setNull(parameterIndex, java.sql.Types.OTHER); + } else { + if (parameterObj instanceof Byte) { + setInt(parameterIndex, ((Byte) parameterObj).intValue()); + } else if (parameterObj instanceof String) { + setString(parameterIndex, (String) parameterObj); + } else if (parameterObj instanceof BigDecimal) { + setBigDecimal(parameterIndex, (BigDecimal) parameterObj); + } else if (parameterObj instanceof Short) { + setShort(parameterIndex, ((Short) parameterObj).shortValue()); + } else if (parameterObj instanceof Integer) { + setInt(parameterIndex, ((Integer) parameterObj).intValue()); + } else if (parameterObj instanceof Long) { + setLong(parameterIndex, ((Long) parameterObj).longValue()); + } else if (parameterObj instanceof Float) { + setFloat(parameterIndex, ((Float) parameterObj).floatValue()); + } else if (parameterObj instanceof Double) { + setDouble(parameterIndex, ((Double) parameterObj).doubleValue()); + } else if (parameterObj instanceof byte[]) { + setBytes(parameterIndex, (byte[]) parameterObj); + } else if (parameterObj instanceof java.sql.Date) { + setDate(parameterIndex, (java.sql.Date) parameterObj); + } else if (parameterObj instanceof Time) { + setTime(parameterIndex, (Time) parameterObj); + } else if (parameterObj instanceof Timestamp) { + setTimestamp(parameterIndex, (Timestamp) parameterObj); + } else if (parameterObj instanceof Boolean) { + setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue()); + } else if (parameterObj instanceof InputStream) { + setBinaryStream(parameterIndex, (InputStream) parameterObj, -1); + } else if (parameterObj instanceof java.sql.Blob) { + setBlob(parameterIndex, (java.sql.Blob) parameterObj); + } else if (parameterObj instanceof java.sql.Clob) { + setClob(parameterIndex, (java.sql.Clob) parameterObj); + } else if (this.connection.getTreatUtilDateAsTimestamp() && parameterObj instanceof java.util.Date) { + setTimestamp(parameterIndex, new Timestamp(((java.util.Date) parameterObj).getTime())); + } else if (parameterObj instanceof BigInteger) { + setString(parameterIndex, parameterObj.toString()); + } else { + setSerializableObject(parameterIndex, parameterObj); + } + } + } + } + + /** + * @param parameterIndex + * @param parameterObj + * @param targetSqlType + * + * @throws SQLException + */ + public void setObject(int parameterIndex, Object parameterObj, int targetSqlType) throws SQLException { + if (!(parameterObj instanceof BigDecimal)) { + setObject(parameterIndex, parameterObj, targetSqlType, 0); + } else { + setObject(parameterIndex, parameterObj, targetSqlType, ((BigDecimal) parameterObj).scale()); + } + } + + /** + * Set the value of a parameter using an object; use the java.lang + * equivalent objects for integral values. + * + *

    + * The given Java object will be converted to the targetSqlType before being sent to the database. + *

    + * + *

    + * note that this method may be used to pass database-specific abstract data types. This is done by using a Driver-specific Java type and using a + * targetSqlType of java.sql.Types.OTHER + *

    + * + * @param parameterIndex + * the first parameter is 1... + * @param parameterObj + * the object containing the input parameter value + * @param targetSqlType + * The SQL type to be send to the database + * @param scale + * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + * + * @throws SQLException + * if a database access error occurs + */ + public void setObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (parameterObj == null) { + setNull(parameterIndex, java.sql.Types.OTHER); + } else { + try { + /* + * From Table-B5 in the JDBC Spec + */ + switch (targetSqlType) { + case Types.BOOLEAN: + + if (parameterObj instanceof Boolean) { + setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue()); + + break; + } else if (parameterObj instanceof String) { + setBoolean(parameterIndex, "true".equalsIgnoreCase((String) parameterObj) || !"0".equalsIgnoreCase((String) parameterObj)); + + break; + } else if (parameterObj instanceof Number) { + int intValue = ((Number) parameterObj).intValue(); + + setBoolean(parameterIndex, intValue != 0); + + break; + } else { + throw SQLError.createSQLException("No conversion from " + parameterObj.getClass().getName() + " to Types.BOOLEAN possible.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + + setNumericObject(parameterIndex, parameterObj, targetSqlType, scale); + + break; + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + if (parameterObj instanceof BigDecimal) { + setString(parameterIndex, (StringUtils.fixDecimalExponent(StringUtils.consistentToString((BigDecimal) parameterObj)))); + } else { + setString(parameterIndex, parameterObj.toString()); + } + + break; + + case Types.CLOB: + + if (parameterObj instanceof java.sql.Clob) { + setClob(parameterIndex, (java.sql.Clob) parameterObj); + } else { + setString(parameterIndex, parameterObj.toString()); + } + + break; + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.BLOB: + + if (parameterObj instanceof byte[]) { + setBytes(parameterIndex, (byte[]) parameterObj); + } else if (parameterObj instanceof java.sql.Blob) { + setBlob(parameterIndex, (java.sql.Blob) parameterObj); + } else { + setBytes(parameterIndex, StringUtils.getBytes(parameterObj.toString(), this.charConverter, this.charEncoding, + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor())); + } + + break; + + case Types.DATE: + case Types.TIMESTAMP: + + java.util.Date parameterAsDate; + + if (parameterObj instanceof String) { + ParsePosition pp = new ParsePosition(0); + java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern((String) parameterObj, false), Locale.US); + parameterAsDate = sdf.parse((String) parameterObj, pp); + } else { + parameterAsDate = (java.util.Date) parameterObj; + } + + switch (targetSqlType) { + case Types.DATE: + + if (parameterAsDate instanceof java.sql.Date) { + setDate(parameterIndex, (java.sql.Date) parameterAsDate); + } else { + setDate(parameterIndex, new java.sql.Date(parameterAsDate.getTime())); + } + + break; + + case Types.TIMESTAMP: + + if (parameterAsDate instanceof java.sql.Timestamp) { + setTimestamp(parameterIndex, (java.sql.Timestamp) parameterAsDate); + } else { + setTimestamp(parameterIndex, new java.sql.Timestamp(parameterAsDate.getTime())); + } + + break; + } + + break; + + case Types.TIME: + + if (parameterObj instanceof String) { + java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern((String) parameterObj, true), Locale.US); + setTime(parameterIndex, new java.sql.Time(sdf.parse((String) parameterObj).getTime())); + } else if (parameterObj instanceof Timestamp) { + Timestamp xT = (Timestamp) parameterObj; + setTime(parameterIndex, new java.sql.Time(xT.getTime())); + } else { + setTime(parameterIndex, (java.sql.Time) parameterObj); + } + + break; + + case Types.OTHER: + setSerializableObject(parameterIndex, parameterObj); + + break; + + default: + throw SQLError.createSQLException(Messages.getString("PreparedStatement.16"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } catch (Exception ex) { + if (ex instanceof SQLException) { + throw (SQLException) ex; + } + + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("PreparedStatement.17") + parameterObj.getClass().toString() + Messages.getString("PreparedStatement.18") + + ex.getClass().getName() + Messages.getString("PreparedStatement.19") + ex.getMessage(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + } + } + + protected int setOneBatchedParameterSet(java.sql.PreparedStatement batchedStatement, int batchedParamIndex, Object paramSet) throws SQLException { + BatchParams paramArg = (BatchParams) paramSet; + + boolean[] isNullBatch = paramArg.isNull; + boolean[] isStreamBatch = paramArg.isStream; + + for (int j = 0; j < isNullBatch.length; j++) { + if (isNullBatch[j]) { + batchedStatement.setNull(batchedParamIndex++, Types.NULL); + } else { + if (isStreamBatch[j]) { + batchedStatement.setBinaryStream(batchedParamIndex++, paramArg.parameterStreams[j], paramArg.streamLengths[j]); + } else { + ((com.mysql.jdbc.PreparedStatement) batchedStatement).setBytesNoEscapeNoQuotes(batchedParamIndex++, paramArg.parameterStrings[j]); + } + } + } + + return batchedParamIndex; + } + + /** + * JDBC 2.0 Set a REF(<structured-type>) parameter. + * + * @param i + * the first parameter is 1, the second is 2, ... + * @param x + * an object representing data of an SQL REF Type + * + * @throws SQLException + * if a database error occurs + * @throws NotImplemented + */ + public void setRef(int i, Ref x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Sets the value for the placeholder as a serialized Java object (used by + * various forms of setObject() + * + * @param parameterIndex + * @param parameterObj + * + * @throws SQLException + */ + private final void setSerializableObject(int parameterIndex, Object parameterObj) throws SQLException { + try { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut); + objectOut.writeObject(parameterObj); + objectOut.flush(); + objectOut.close(); + bytesOut.flush(); + bytesOut.close(); + + byte[] buf = bytesOut.toByteArray(); + ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf); + setBinaryStream(parameterIndex, bytesIn, buf.length); + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BINARY; + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.54") + ex.getClass().getName(), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + /** + * Set a parameter to a Java short value. The driver converts this to a SQL + * SMALLINT value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setShort(int parameterIndex, short x) throws SQLException { + setInternal(parameterIndex, String.valueOf(x)); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.SMALLINT; + } + + /** + * Set a parameter to a Java String value. The driver converts this to a SQL + * VARCHAR or LONGVARCHAR value (depending on the arguments size relative to + * the driver's limits on VARCHARs) when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setString(int parameterIndex, String x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + // if the passed string is null, then set this column to null + if (x == null) { + setNull(parameterIndex, Types.CHAR); + } else { + checkClosed(); + + int stringLength = x.length(); + + if (this.connection.isNoBackslashEscapesSet()) { + // Scan for any nasty chars + + boolean needsHexEscape = isEscapeNeededForString(x, stringLength); + + if (!needsHexEscape) { + byte[] parameterAsBytes = null; + + StringBuilder quotedString = new StringBuilder(x.length() + 2); + quotedString.append('\''); + quotedString.append(x); + quotedString.append('\''); + + if (!this.isLoadDataQuery) { + parameterAsBytes = StringUtils.getBytes(quotedString.toString(), this.charConverter, this.charEncoding, + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } else { + // Send with platform character encoding + parameterAsBytes = StringUtils.getBytes(quotedString.toString()); + } + + setInternal(parameterIndex, parameterAsBytes); + } else { + byte[] parameterAsBytes = null; + + if (!this.isLoadDataQuery) { + parameterAsBytes = StringUtils.getBytes(x, this.charConverter, this.charEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } else { + // Send with platform character encoding + parameterAsBytes = StringUtils.getBytes(x); + } + + setBytes(parameterIndex, parameterAsBytes); + } + + return; + } + + String parameterAsString = x; + boolean needsQuoted = true; + + if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) { + needsQuoted = false; // saves an allocation later + + StringBuilder buf = new StringBuilder((int) (x.length() * 1.1)); + + buf.append('\''); + + // + // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure... + // + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + buf.append('\\'); + buf.append('0'); + + break; + + case '\n': /* Must be escaped for logs */ + buf.append('\\'); + buf.append('n'); + + break; + + case '\r': + buf.append('\\'); + buf.append('r'); + + break; + + case '\\': + buf.append('\\'); + buf.append('\\'); + + break; + + case '\'': + buf.append('\\'); + buf.append('\''); + + break; + + case '"': /* Better safe than sorry */ + if (this.usingAnsiMode) { + buf.append('\\'); + } + + buf.append('"'); + + break; + + case '\032': /* This gives problems on Win32 */ + buf.append('\\'); + buf.append('Z'); + + break; + + case '\u00a5': + case '\u20a9': + // escape characters interpreted as backslash by mysql + if (this.charsetEncoder != null) { + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + cbuf.put(c); + cbuf.position(0); + this.charsetEncoder.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + buf.append('\\'); + } + } + buf.append(c); + break; + + default: + buf.append(c); + } + } + + buf.append('\''); + + parameterAsString = buf.toString(); + } + + byte[] parameterAsBytes = null; + + if (!this.isLoadDataQuery) { + if (needsQuoted) { + parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charConverter, this.charEncoding, + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } else { + parameterAsBytes = StringUtils.getBytes(parameterAsString, this.charConverter, this.charEncoding, this.connection.getServerCharset(), + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } + } else { + // Send with platform character encoding + parameterAsBytes = StringUtils.getBytes(parameterAsString); + } + + setInternal(parameterIndex, parameterAsBytes); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.VARCHAR; + } + } + } + + private boolean isEscapeNeededForString(String x, int stringLength) { + boolean needsHexEscape = false; + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + + needsHexEscape = true; + break; + + case '\n': /* Must be escaped for logs */ + needsHexEscape = true; + + break; + + case '\r': + needsHexEscape = true; + break; + + case '\\': + needsHexEscape = true; + + break; + + case '\'': + needsHexEscape = true; + + break; + + case '"': /* Better safe than sorry */ + needsHexEscape = true; + + break; + + case '\032': /* This gives problems on Win32 */ + needsHexEscape = true; + break; + } + + if (needsHexEscape) { + break; // no need to scan more + } + } + return needsHexEscape; + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts this to a + * SQL TIME value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the cal specifying the timezone + * + * @throws SQLException + * if a database-access error occurs. + */ + public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true); + } + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts this to a + * SQL TIME value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1...)); + * @param x + * the parameter value + * + * @throws java.sql.SQLException + * if a database access error occurs + */ + public void setTime(int parameterIndex, Time x) throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimeInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false); + } + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts this to a + * SQL TIME value when it sends it to the database, using the given + * timezone. + * + * @param parameterIndex + * the first parameter is 1...)); + * @param x + * the parameter value + * @param tz + * the timezone to use + * + * @throws java.sql.SQLException + * if a database access error occurs + */ + private void setTimeInternal(int parameterIndex, Time x, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws java.sql.SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.TIME); + } else { + checkClosed(); + + if (!this.useLegacyDatetimeCode) { + newSetTimeInternal(parameterIndex, x, targetCalendar); + } else { + Calendar sessionCalendar = getCalendarInstanceForSessionOrNew(); + + x = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), rollForward); + + setInternal(parameterIndex, "'" + x.toString() + "'"); + } + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIME; + } + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts this + * to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the calendar specifying the timezone to use + * + * @throws SQLException + * if a database-access error occurs. + */ + public void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true); + } + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts this + * to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @throws java.sql.SQLException + * if a database access error occurs + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimestampInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false); + } + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts this + * to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param tz + * the timezone to use + * + * @throws SQLException + * if a database-access error occurs. + */ + private void setTimestampInternal(int parameterIndex, Timestamp x, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.TIMESTAMP); + } else { + checkClosed(); + + if (!this.sendFractionalSeconds) { + x = TimeUtil.truncateFractionalSeconds(x); + } + + if (!this.useLegacyDatetimeCode) { + newSetTimestampInternal(parameterIndex, x, targetCalendar); + } else { + Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + + x = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), rollForward); + + if (this.connection.getUseSSPSCompatibleTimezoneShift()) { + doSSPSCompatibleTimezoneShift(parameterIndex, x); + } else { + synchronized (this) { + if (this.tsdf == null) { + this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); + } + + StringBuffer buf = new StringBuffer(); + buf.append(this.tsdf.format(x)); + + if (this.serverSupportsFracSecs) { + int nanos = x.getNanos(); + + if (nanos != 0) { + buf.append('.'); + buf.append(TimeUtil.formatNanos(nanos, this.serverSupportsFracSecs, true)); + } + } + + buf.append('\''); + + setInternal(parameterIndex, buf.toString()); // SimpleDateFormat is not + // thread-safe + } + } + } + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIMESTAMP; + } + } + + private void newSetTimestampInternal(int parameterIndex, Timestamp x, Calendar targetCalendar) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.tsdf == null) { + this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); + } + + if (targetCalendar != null) { + this.tsdf.setTimeZone(targetCalendar.getTimeZone()); + } else { + this.tsdf.setTimeZone(this.connection.getServerTimezoneTZ()); + } + + StringBuffer buf = new StringBuffer(); + buf.append(this.tsdf.format(x)); + buf.append('.'); + buf.append(TimeUtil.formatNanos(x.getNanos(), this.serverSupportsFracSecs, true)); + buf.append('\''); + + setInternal(parameterIndex, buf.toString()); + } + } + + private void newSetTimeInternal(int parameterIndex, Time x, Calendar targetCalendar) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.tdf == null) { + this.tdf = new SimpleDateFormat("''HH:mm:ss''", Locale.US); + } + + if (targetCalendar != null) { + this.tdf.setTimeZone(targetCalendar.getTimeZone()); + } else { + this.tdf.setTimeZone(this.connection.getServerTimezoneTZ()); + } + + setInternal(parameterIndex, this.tdf.format(x)); + } + } + + private void newSetDateInternal(int parameterIndex, Date x, Calendar targetCalendar) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.ddf == null) { + this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US); + } + + if (targetCalendar != null) { + this.ddf.setTimeZone(targetCalendar.getTimeZone()); + } else if (this.connection.getNoTimezoneConversionForDateType()) { + this.ddf.setTimeZone(this.connection.getDefaultTimeZone()); + } else { + this.ddf.setTimeZone(this.connection.getServerTimezoneTZ()); + } + + setInternal(parameterIndex, this.ddf.format(x)); + } + } + + private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Calendar sessionCalendar2 = (this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar2) { + java.util.Date oldTime = sessionCalendar2.getTime(); + + try { + sessionCalendar2.setTime(x); + + int year = sessionCalendar2.get(Calendar.YEAR); + int month = sessionCalendar2.get(Calendar.MONTH) + 1; + int date = sessionCalendar2.get(Calendar.DAY_OF_MONTH); + + int hour = sessionCalendar2.get(Calendar.HOUR_OF_DAY); + int minute = sessionCalendar2.get(Calendar.MINUTE); + int seconds = sessionCalendar2.get(Calendar.SECOND); + + StringBuilder tsBuf = new StringBuilder(); + + tsBuf.append('\''); + tsBuf.append(year); + + tsBuf.append("-"); + + if (month < 10) { + tsBuf.append('0'); + } + + tsBuf.append(month); + + tsBuf.append('-'); + + if (date < 10) { + tsBuf.append('0'); + } + + tsBuf.append(date); + + tsBuf.append(' '); + + if (hour < 10) { + tsBuf.append('0'); + } + + tsBuf.append(hour); + + tsBuf.append(':'); + + if (minute < 10) { + tsBuf.append('0'); + } + + tsBuf.append(minute); + + tsBuf.append(':'); + + if (seconds < 10) { + tsBuf.append('0'); + } + + tsBuf.append(seconds); + + tsBuf.append('.'); + tsBuf.append(TimeUtil.formatNanos(x.getNanos(), this.serverSupportsFracSecs, true)); + tsBuf.append('\''); + + setInternal(parameterIndex, tsBuf.toString()); + + } finally { + sessionCalendar2.setTime(oldTime); + } + } + } + } + + /** + * When a very large Unicode value is input to a LONGVARCHAR parameter, it + * may be more practical to send it via a java.io.InputStream. JDBC will + * read the data from the stream as needed, until it reaches end-of-file. + * The JDBC driver will do any necessary conversion from UNICODE to the + * database char format. + * + *

    + * Note: This stream object can either be a standard Java stream object or your own subclass that implements the standard interface. + *

    + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * @param length + * the number of bytes to read from the stream + * + * @throws SQLException + * if a database access error occurs + * + * @deprecated + */ + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.VARCHAR); + } else { + setBinaryStream(parameterIndex, x, length); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB; + } + } + + /** + * @see PreparedStatement#setURL(int, URL) + */ + public void setURL(int parameterIndex, URL arg) throws SQLException { + if (arg != null) { + setString(parameterIndex, arg.toString()); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATALINK; + } else { + setNull(parameterIndex, Types.CHAR); + } + } + + private final void streamToBytes(Buffer packet, InputStream in, boolean escape, int streamLength, boolean useLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + if (this.streamConvertBuf == null) { + this.streamConvertBuf = new byte[4096]; + } + + String connectionEncoding = this.connection.getEncoding(); + + boolean hexEscape = false; + + try { + if (this.connection.isNoBackslashEscapesSet() || (this.connection.getUseUnicode() && connectionEncoding != null + && CharsetMapping.isMultibyteCharset(connectionEncoding) && !this.connection.parserKnowsUnicode())) { + hexEscape = true; + } + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + if (streamLength == -1) { + useLength = false; + } + + int bc = -1; + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, streamLength); + } else { + bc = readblock(in, this.streamConvertBuf); + } + + int lengthLeftToRead = streamLength - bc; + + if (hexEscape) { + packet.writeStringNoNull("x"); + } else if (this.connection.getIO().versionMeetsMinimum(4, 1, 0)) { + packet.writeStringNoNull("_binary"); + } + + if (escape) { + packet.writeByte((byte) '\''); + } + + while (bc > 0) { + if (hexEscape) { + hexEscapeBlock(this.streamConvertBuf, packet, bc); + } else if (escape) { + escapeblockFast(this.streamConvertBuf, packet, bc); + } else { + packet.writeBytesNoNull(this.streamConvertBuf, 0, bc); + } + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, lengthLeftToRead); + + if (bc > 0) { + lengthLeftToRead -= bc; + } + } else { + bc = readblock(in, this.streamConvertBuf); + } + } + + if (escape) { + packet.writeByte((byte) '\''); + } + } finally { + if (this.connection.getAutoClosePStmtStreams()) { + try { + in.close(); + } catch (IOException ioEx) { + } + + in = null; + } + } + } + } + + private final byte[] streamToBytes(InputStream in, boolean escape, int streamLength, boolean useLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + in.mark(Integer.MAX_VALUE); // we may need to read this same stream several times, so we need to reset it at the end. + try { + if (this.streamConvertBuf == null) { + this.streamConvertBuf = new byte[4096]; + } + if (streamLength == -1) { + useLength = false; + } + + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + + int bc = -1; + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, streamLength); + } else { + bc = readblock(in, this.streamConvertBuf); + } + + int lengthLeftToRead = streamLength - bc; + + if (escape) { + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + bytesOut.write('_'); + bytesOut.write('b'); + bytesOut.write('i'); + bytesOut.write('n'); + bytesOut.write('a'); + bytesOut.write('r'); + bytesOut.write('y'); + } + + bytesOut.write('\''); + } + + while (bc > 0) { + if (escape) { + escapeblockFast(this.streamConvertBuf, bytesOut, bc); + } else { + bytesOut.write(this.streamConvertBuf, 0, bc); + } + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, lengthLeftToRead); + + if (bc > 0) { + lengthLeftToRead -= bc; + } + } else { + bc = readblock(in, this.streamConvertBuf); + } + } + + if (escape) { + bytesOut.write('\''); + } + + return bytesOut.toByteArray(); + } finally { + try { + in.reset(); + } catch (IOException e) { + } + if (this.connection.getAutoClosePStmtStreams()) { + try { + in.close(); + } catch (IOException ioEx) { + } + + in = null; + } + } + } + } + + /** + * Returns this PreparedStatement represented as a string. + * + * @return this PreparedStatement represented as a string. + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(super.toString()); + buf.append(": "); + + try { + buf.append(asSql()); + } catch (SQLException sqlEx) { + buf.append("EXCEPTION: " + sqlEx.toString()); + } + + return buf.toString(); + } + + /** + * For calling stored functions, this will be -1 as we don't really count + * the first '?' parameter marker, it's only syntax, but JDBC counts it + * as #1, otherwise it will return 0 + */ + + protected int getParameterIndexOffset() { + return 0; + } + + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + setAsciiStream(parameterIndex, x, -1); + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + setAsciiStream(parameterIndex, x, (int) length); + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB; + } + + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + setBinaryStream(parameterIndex, x, -1); + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + setBinaryStream(parameterIndex, x, (int) length); + } + + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + setBinaryStream(parameterIndex, inputStream); + } + + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + setCharacterStream(parameterIndex, reader, -1); + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + setCharacterStream(parameterIndex, reader, (int) length); + + } + + public void setClob(int parameterIndex, Reader reader) throws SQLException { + setCharacterStream(parameterIndex, reader); + + } + + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + setCharacterStream(parameterIndex, reader, length); + } + + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + setNCharacterStream(parameterIndex, value, -1); + } + + /** + * Set a parameter to a Java String value. The driver converts this to a SQL + * VARCHAR or LONGVARCHAR value with introducer _utf8 (depending on the + * arguments size relative to the driver's limits on VARCHARs) when it sends + * it to the database. If charset is set as utf8, this method just call setString. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setNString(int parameterIndex, String x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.charEncoding.equalsIgnoreCase("UTF-8") || this.charEncoding.equalsIgnoreCase("utf8")) { + setString(parameterIndex, x); + return; + } + + // if the passed string is null, then set this column to null + if (x == null) { + setNull(parameterIndex, java.sql.Types.CHAR); + } else { + int stringLength = x.length(); + // Ignore sql_mode=NO_BACKSLASH_ESCAPES in current implementation. + + // Add introducer _utf8 for NATIONAL CHARACTER + StringBuilder buf = new StringBuilder((int) (x.length() * 1.1 + 4)); + buf.append("_utf8"); + buf.append('\''); + + // + // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure... + // + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + buf.append('\\'); + buf.append('0'); + + break; + + case '\n': /* Must be escaped for logs */ + buf.append('\\'); + buf.append('n'); + + break; + + case '\r': + buf.append('\\'); + buf.append('r'); + + break; + + case '\\': + buf.append('\\'); + buf.append('\\'); + + break; + + case '\'': + buf.append('\\'); + buf.append('\''); + + break; + + case '"': /* Better safe than sorry */ + if (this.usingAnsiMode) { + buf.append('\\'); + } + + buf.append('"'); + + break; + + case '\032': /* This gives problems on Win32 */ + buf.append('\\'); + buf.append('Z'); + + break; + + default: + buf.append(c); + } + } + + buf.append('\''); + + String parameterAsString = buf.toString(); + + byte[] parameterAsBytes = null; + + if (!this.isLoadDataQuery) { + parameterAsBytes = StringUtils.getBytes(parameterAsString, this.connection.getCharsetConverter("UTF-8"), "UTF-8", + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + } else { + // Send with platform character encoding + parameterAsBytes = StringUtils.getBytes(parameterAsString); + } + + setInternal(parameterIndex, parameterAsBytes); + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = -9; /* Types.NVARCHAR */ + } + } + } + + /** + * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR + * parameter, it may be more practical to send it via a java.io.Reader. JDBC + * will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * UNICODE to the database char format. + * + *

    + * Note: This stream object can either be a standard Java stream object or your own subclass that implements the standard interface. + *

    + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param reader + * the java reader which contains the UNICODE data + * @param length + * the number of characters in the stream + * + * @exception SQLException + * if a database-access error occurs. + */ + public void setNCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + if (reader == null) { + setNull(parameterIndex, java.sql.Types.LONGVARCHAR); + + } else { + char[] c = null; + int len = 0; + + boolean useLength = this.connection.getUseStreamLengthsInPrepStmts(); + + // Ignore "clobCharacterEncoding" because utf8 should be used this time. + + if (useLength && (length != -1)) { + c = new char[(int) length]; // can't take more than Integer.MAX_VALUE + + int numCharsRead = readFully(reader, c, (int) length); // blocks until all read + setNString(parameterIndex, new String(c, 0, numCharsRead)); + + } else { + c = new char[4096]; + + StringBuilder buf = new StringBuilder(); + + while ((len = reader.read(c)) != -1) { + buf.append(c, 0, len); + } + + setNString(parameterIndex, buf.toString()); + } + + this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = 2011; /* Types.NCLOB */ + } + } catch (java.io.IOException ioEx) { + throw SQLError.createSQLException(ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + } + + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + setNCharacterStream(parameterIndex, reader); + } + + /** + * JDBC 4.0 Set a NCLOB parameter. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param reader + * the java reader which contains the UNICODE data + * @param length + * the number of characters in the stream + * + * @throws SQLException + * if a database error occurs + */ + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + if (reader == null) { + setNull(parameterIndex, java.sql.Types.LONGVARCHAR); + } else { + setNCharacterStream(parameterIndex, reader, length); + } + } + + public ParameterBindings getParameterBindings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return new EmulatedPreparedStatementBindings(); + } + } + + class EmulatedPreparedStatementBindings implements ParameterBindings { + + private ResultSetImpl bindingsAsRs; + private boolean[] parameterIsNull; + + EmulatedPreparedStatementBindings() throws SQLException { + List rows = new ArrayList(); + this.parameterIsNull = new boolean[PreparedStatement.this.parameterCount]; + System.arraycopy(PreparedStatement.this.isNull, 0, this.parameterIsNull, 0, PreparedStatement.this.parameterCount); + byte[][] rowData = new byte[PreparedStatement.this.parameterCount][]; + Field[] typeMetadata = new Field[PreparedStatement.this.parameterCount]; + + for (int i = 0; i < PreparedStatement.this.parameterCount; i++) { + if (PreparedStatement.this.batchCommandIndex == -1) { + rowData[i] = getBytesRepresentation(i); + } else { + rowData[i] = getBytesRepresentationForBatch(i, PreparedStatement.this.batchCommandIndex); + } + + int charsetIndex = 0; + + if (PreparedStatement.this.parameterTypes[i] == Types.BINARY || PreparedStatement.this.parameterTypes[i] == Types.BLOB) { + charsetIndex = CharsetMapping.MYSQL_COLLATION_INDEX_binary; + } else { + try { + charsetIndex = CharsetMapping.getCollationIndexForJavaEncoding(PreparedStatement.this.connection.getEncoding(), + PreparedStatement.this.connection); + } catch (SQLException ex) { + throw ex; + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + } + + Field parameterMetadata = new Field(null, "parameter_" + (i + 1), charsetIndex, PreparedStatement.this.parameterTypes[i], rowData[i].length); + parameterMetadata.setConnection(PreparedStatement.this.connection); + typeMetadata[i] = parameterMetadata; + } + + rows.add(new ByteArrayRow(rowData, getExceptionInterceptor())); + + this.bindingsAsRs = new ResultSetImpl(PreparedStatement.this.connection.getCatalog(), typeMetadata, new RowDataStatic(rows), + PreparedStatement.this.connection, null); + this.bindingsAsRs.next(); + } + + public Array getArray(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getArray(parameterIndex); + } + + public InputStream getAsciiStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getAsciiStream(parameterIndex); + } + + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBigDecimal(parameterIndex); + } + + public InputStream getBinaryStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBinaryStream(parameterIndex); + } + + public java.sql.Blob getBlob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBlob(parameterIndex); + } + + public boolean getBoolean(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBoolean(parameterIndex); + } + + public byte getByte(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getByte(parameterIndex); + } + + public byte[] getBytes(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBytes(parameterIndex); + } + + public Reader getCharacterStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public java.sql.Clob getClob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getClob(parameterIndex); + } + + public Date getDate(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getDate(parameterIndex); + } + + public double getDouble(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getDouble(parameterIndex); + } + + public float getFloat(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getFloat(parameterIndex); + } + + public int getInt(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getInt(parameterIndex); + } + + public long getLong(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getLong(parameterIndex); + } + + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public Reader getNClob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public Object getObject(int parameterIndex) throws SQLException { + checkBounds(parameterIndex, 0); + + if (this.parameterIsNull[parameterIndex - 1]) { + return null; + } + + // we can't rely on the default mapping for JDBC's ResultSet.getObject() for numerics, they're not one-to-one with PreparedStatement.setObject + + switch (PreparedStatement.this.parameterTypes[parameterIndex - 1]) { + case Types.TINYINT: + return Byte.valueOf(getByte(parameterIndex)); + case Types.SMALLINT: + return Short.valueOf(getShort(parameterIndex)); + case Types.INTEGER: + return Integer.valueOf(getInt(parameterIndex)); + case Types.BIGINT: + return Long.valueOf(getLong(parameterIndex)); + case Types.FLOAT: + return Float.valueOf(getFloat(parameterIndex)); + case Types.DOUBLE: + return Double.valueOf(getDouble(parameterIndex)); + default: + return this.bindingsAsRs.getObject(parameterIndex); + } + } + + public Ref getRef(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getRef(parameterIndex); + } + + public short getShort(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getShort(parameterIndex); + } + + public String getString(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getString(parameterIndex); + } + + public Time getTime(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getTime(parameterIndex); + } + + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getTimestamp(parameterIndex); + } + + public URL getURL(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getURL(parameterIndex); + } + + public boolean isNull(int parameterIndex) throws SQLException { + checkBounds(parameterIndex, 0); + + return this.parameterIsNull[parameterIndex - 1]; + } + } + + public String getPreparedSql() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.rewrittenBatchSize == 0) { + return this.originalSql; + } + + try { + return this.parseInfo.getSqlForBatch(this.parseInfo); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: evolve public interface + } + } + + @Override + public int getUpdateCount() throws SQLException { + int count = super.getUpdateCount(); + + if (containsOnDuplicateKeyUpdateInSQL() && this.compensateForOnDuplicateKeyUpdate) { + if (count == 2 || count == 0) { + count = 1; + } + } + + return count; + } + + protected static boolean canRewrite(String sql, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, int statementStartPos) { + // Needs to be INSERT or REPLACE. + // Can't have INSERT ... SELECT or INSERT ... ON DUPLICATE KEY UPDATE with an id=LAST_INSERT_ID(...). + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT", statementStartPos)) { + if (StringUtils.indexOfIgnoreCase(statementStartPos, sql, "SELECT", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) != -1) { + return false; + } + if (isOnDuplicateKeyUpdate) { + int updateClausePos = StringUtils.indexOfIgnoreCase(locationOfOnDuplicateKeyUpdate, sql, " UPDATE "); + if (updateClausePos != -1) { + return StringUtils.indexOfIgnoreCase(updateClausePos, sql, "LAST_INSERT_ID", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1; + } + } + return true; + } + + return StringUtils.startsWithIgnoreCaseAndWs(sql, "REPLACE", statementStartPos) + && StringUtils.indexOfIgnoreCase(statementStartPos, sql, "SELECT", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1; + } + + /** + * JDBC 4.2 + * Same as PreparedStatement.executeUpdate() but returns long instead of int. + */ + public long executeLargeUpdate() throws SQLException { + return executeUpdateInternal(true, false); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ProfilerEventHandlerFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ProfilerEventHandlerFactory.java new file mode 100644 index 0000000..f70f10b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ProfilerEventHandlerFactory.java @@ -0,0 +1,76 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +import com.mysql.jdbc.log.Log; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +public class ProfilerEventHandlerFactory { + + private Connection ownerConnection = null; + + protected Log log = null; + + /** + * Returns the ProfilerEventHandlerFactory that handles profiler events for the given + * connection. + * + * @param conn + * the connection to handle events for + * @return the ProfilerEventHandlerFactory that handles profiler events + */ + public static synchronized ProfilerEventHandler getInstance(MySQLConnection conn) throws SQLException { + ProfilerEventHandler handler = conn.getProfilerEventHandlerInstance(); + + if (handler == null) { + handler = (ProfilerEventHandler) Util.getInstance(conn.getProfilerEventHandler(), new Class[0], new Object[0], conn.getExceptionInterceptor()); + + // we do it this way to not require exposing the connection properties for all who utilize it + conn.initializeExtension(handler); + conn.setProfilerEventHandlerInstance(handler); + } + + return handler; + } + + public static synchronized void removeInstance(MySQLConnection conn) { + ProfilerEventHandler handler = conn.getProfilerEventHandlerInstance(); + + if (handler != null) { + handler.destroy(); + } + } + + private ProfilerEventHandlerFactory(Connection conn) { + this.ownerConnection = conn; + + try { + this.log = this.ownerConnection.getLog(); + } catch (SQLException sqlEx) { + throw new RuntimeException("Unable to get logger from connection"); + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RandomBalanceStrategy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RandomBalanceStrategy.java new file mode 100644 index 0000000..ecf2360 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RandomBalanceStrategy.java @@ -0,0 +1,130 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class RandomBalanceStrategy implements BalanceStrategy { + + public RandomBalanceStrategy() { + } + + public void destroy() { + // we don't have anything to clean up + } + + public void init(Connection conn, Properties props) throws SQLException { + // we don't have anything to initialize + } + + public ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + int numHosts = configuredHosts.size(); + + SQLException ex = null; + + List whiteList = new ArrayList(numHosts); + whiteList.addAll(configuredHosts); + + Map blackList = proxy.getGlobalBlacklist(); + + whiteList.removeAll(blackList.keySet()); + + Map whiteListMap = this.getArrayIndexMap(whiteList); + + for (int attempts = 0; attempts < numRetries;) { + int random = (int) Math.floor((Math.random() * whiteList.size())); + if (whiteList.size() == 0) { + throw SQLError.createSQLException("No hosts configured", null); + } + + String hostPortSpec = whiteList.get(random); + + ConnectionImpl conn = liveConnections.get(hostPortSpec); + + if (conn == null) { + try { + conn = proxy.createConnectionForHost(hostPortSpec); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (proxy.shouldExceptionTriggerConnectionSwitch(sqlEx)) { + + Integer whiteListIndex = whiteListMap.get(hostPortSpec); + + // exclude this host from being picked again + if (whiteListIndex != null) { + whiteList.remove(whiteListIndex.intValue()); + whiteListMap = this.getArrayIndexMap(whiteList); + } + proxy.addToGlobalBlacklist(hostPortSpec); + + if (whiteList.size() == 0) { + attempts++; + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + // start fresh + whiteListMap = new HashMap(numHosts); + whiteList.addAll(configuredHosts); + blackList = proxy.getGlobalBlacklist(); + + whiteList.removeAll(blackList.keySet()); + whiteListMap = this.getArrayIndexMap(whiteList); + } + + continue; + } + + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + + private Map getArrayIndexMap(List l) { + Map m = new HashMap(l.size()); + for (int i = 0; i < l.size(); i++) { + m.put(l.get(i), Integer.valueOf(i)); + } + return m; + + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReflectiveStatementInterceptorAdapter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReflectiveStatementInterceptorAdapter.java new file mode 100644 index 0000000..22c4b1f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReflectiveStatementInterceptorAdapter.java @@ -0,0 +1,94 @@ +/* + Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.Properties; + +public class ReflectiveStatementInterceptorAdapter implements StatementInterceptorV2 { + + private final StatementInterceptor toProxy; + + final Method v2PostProcessMethod; + + public ReflectiveStatementInterceptorAdapter(StatementInterceptor toProxy) { + this.toProxy = toProxy; + this.v2PostProcessMethod = getV2PostProcessMethod(toProxy.getClass()); + } + + public void destroy() { + this.toProxy.destroy(); + } + + public boolean executeTopLevelOnly() { + return this.toProxy.executeTopLevelOnly(); + } + + public void init(Connection conn, Properties props) throws SQLException { + this.toProxy.init(conn, props); + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection, + int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException { + try { + return (ResultSetInternalMethods) this.v2PostProcessMethod.invoke(this.toProxy, + new Object[] { sql, interceptedStatement, originalResultSet, connection, Integer.valueOf(warningCount), + noIndexUsed ? Boolean.TRUE : Boolean.FALSE, noGoodIndexUsed ? Boolean.TRUE : Boolean.FALSE, statementException }); + } catch (IllegalArgumentException e) { + SQLException sqlEx = new SQLException("Unable to reflectively invoke interceptor"); + sqlEx.initCause(e); + + throw sqlEx; + } catch (IllegalAccessException e) { + SQLException sqlEx = new SQLException("Unable to reflectively invoke interceptor"); + sqlEx.initCause(e); + + throw sqlEx; + } catch (InvocationTargetException e) { + SQLException sqlEx = new SQLException("Unable to reflectively invoke interceptor"); + sqlEx.initCause(e); + + throw sqlEx; + } + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + return this.toProxy.preProcess(sql, interceptedStatement, connection); + } + + public static final Method getV2PostProcessMethod(Class toProxyClass) { + try { + Method postProcessMethod = toProxyClass.getMethod("postProcess", new Class[] { String.class, Statement.class, ResultSetInternalMethods.class, + Connection.class, Integer.TYPE, Boolean.TYPE, Boolean.TYPE, SQLException.class }); + + return postProcessMethod; + } catch (SecurityException e) { + return null; + } catch (NoSuchMethodException e) { + return null; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnection.java new file mode 100644 index 0000000..ee1bc1a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnection.java @@ -0,0 +1,52 @@ +/* + Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +public interface ReplicationConnection extends MySQLConnection { + public long getConnectionGroupId(); + + public Connection getCurrentConnection(); + + public Connection getMasterConnection(); + + public void promoteSlaveToMaster(String host) throws SQLException; + + public void removeMasterHost(String host) throws SQLException; + + public void removeMasterHost(String host, boolean waitUntilNotInUse) throws SQLException; + + public boolean isHostMaster(String host); + + public Connection getSlavesConnection(); + + public void addSlaveHost(String host) throws SQLException; + + public void removeSlave(String host) throws SQLException; + + public void removeSlave(String host, boolean closeGently) throws SQLException; + + public boolean isHostSlave(String host); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroup.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroup.java new file mode 100644 index 0000000..83f44cb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroup.java @@ -0,0 +1,248 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Group of connection objects which can be configured as a group. This is used for promotion/demotion of slaves and masters in a replication configuration, + * and for exposing metrics around replication-aware connections. + */ +public class ReplicationConnectionGroup { + private String groupName; + private long connections = 0; + private long slavesAdded = 0; + private long slavesRemoved = 0; + private long slavesPromoted = 0; + private long activeConnections = 0; + private HashMap replicationConnections = new HashMap(); + private Set slaveHostList = new CopyOnWriteArraySet(); + private boolean isInitialized = false; + private Set masterHostList = new CopyOnWriteArraySet(); + + ReplicationConnectionGroup(String groupName) { + this.groupName = groupName; + } + + public long getConnectionCount() { + return this.connections; + } + + public long registerReplicationConnection(ReplicationConnection conn, List localMasterList, List localSlaveList) { + long currentConnectionId; + + synchronized (this) { + if (!this.isInitialized) { + if (localMasterList != null) { + this.masterHostList.addAll(localMasterList); + } + if (localSlaveList != null) { + this.slaveHostList.addAll(localSlaveList); + } + this.isInitialized = true; + } + currentConnectionId = ++this.connections; + this.replicationConnections.put(Long.valueOf(currentConnectionId), conn); + } + this.activeConnections++; + + return currentConnectionId; + } + + public String getGroupName() { + return this.groupName; + } + + public Collection getMasterHosts() { + return this.masterHostList; + } + + public Collection getSlaveHosts() { + return this.slaveHostList; + } + + /** + * Adds a host to the slaves hosts list. + * + * We can safely assume that if this host was added to the slaves list, then it must be added to each one of the replication connections from this group as + * well. + * Unnecessary calls to {@link ReplicationConnection#addSlaveHost(String)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group already has this host in a slave role. + * + * @param hostPortPair + * @throws SQLException + */ + public void addSlaveHost(String hostPortPair) throws SQLException { + // only add if it's not already a slave host + if (this.slaveHostList.add(hostPortPair)) { + this.slavesAdded++; + + // add the slave to all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.addSlaveHost(hostPortPair); + } + } + } + + public void handleCloseConnection(ReplicationConnection conn) { + this.replicationConnections.remove(conn.getConnectionGroupId()); + this.activeConnections--; + } + + /** + * Removes a host from the slaves hosts list. + * + * We can safely assume that if this host was removed from the slaves list, then it must be removed from each one of the replication connections from this + * group as well. + * Unnecessary calls to {@link ReplicationConnection#removeSlave(String, boolean)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group doesn't have this host in a slave role. + * + * @param hostPortPair + * @param closeGently + * @throws SQLException + */ + public void removeSlaveHost(String hostPortPair, boolean closeGently) throws SQLException { + if (this.slaveHostList.remove(hostPortPair)) { + this.slavesRemoved++; + + // remove the slave from all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.removeSlave(hostPortPair, closeGently); + } + } + } + + /** + * Promotes a slave host to master. + * + * We can safely assume that if this host was removed from the slaves list or added to the masters list, then the same host promotion must happen in each + * one of the replication connections from this group as well. + * Unnecessary calls to {@link ReplicationConnection#promoteSlaveToMaster(String)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group already has this host in a master role and not in slave role. + * + * @param hostPortPair + * @throws SQLException + */ + public void promoteSlaveToMaster(String hostPortPair) throws SQLException { + // remove host from slaves AND add host to masters, note that both operands need to be evaluated. + if (this.slaveHostList.remove(hostPortPair) | this.masterHostList.add(hostPortPair)) { + this.slavesPromoted++; + + for (ReplicationConnection c : this.replicationConnections.values()) { + c.promoteSlaveToMaster(hostPortPair); + } + } + } + + /** + * Removes a host from the masters hosts list. + * + * @see #removeMasterHost(String, boolean) + */ + public void removeMasterHost(String hostPortPair) throws SQLException { + this.removeMasterHost(hostPortPair, true); + } + + /** + * Removes a host from the masters hosts list. + * + * We can safely assume that if this host was removed from the masters list, then it must be removed from each one of the replication connections from this + * group as well. + * Unnecessary calls to {@link ReplicationConnection#removeMasterHost(String, boolean)} could result in undesirable locking issues, assuming that this + * method is synchronized by nature. + * + * This is a no-op if the group doesn't have this host in a master role. + * + * @param hostPortPair + * @param closeGently + * @throws SQLException + */ + public void removeMasterHost(String hostPortPair, boolean closeGently) throws SQLException { + if (this.masterHostList.remove(hostPortPair)) { + // remove the master from all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.removeMasterHost(hostPortPair, closeGently); + } + } + } + + public int getConnectionCountWithHostAsSlave(String hostPortPair) { + int matched = 0; + + for (ReplicationConnection c : this.replicationConnections.values()) { + if (c.isHostSlave(hostPortPair)) { + matched++; + } + } + return matched; + } + + public int getConnectionCountWithHostAsMaster(String hostPortPair) { + int matched = 0; + + for (ReplicationConnection c : this.replicationConnections.values()) { + if (c.isHostMaster(hostPortPair)) { + matched++; + } + } + return matched; + } + + public long getNumberOfSlavesAdded() { + return this.slavesAdded; + } + + public long getNumberOfSlavesRemoved() { + return this.slavesRemoved; + } + + public long getNumberOfSlavePromotions() { + return this.slavesPromoted; + } + + public long getTotalConnectionCount() { + return this.connections; + } + + public long getActiveConnectionCount() { + return this.activeConnections; + } + + @Override + public String toString() { + return "ReplicationConnectionGroup[groupName=" + this.groupName + ",masterHostList=" + this.masterHostList + ",slaveHostList=" + this.slaveHostList + + "]"; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroupManager.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroupManager.java new file mode 100644 index 0000000..8ebe9ca --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionGroupManager.java @@ -0,0 +1,200 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class ReplicationConnectionGroupManager { + private static HashMap GROUP_MAP = new HashMap(); + + private static com.mysql.jdbc.jmx.ReplicationGroupManager mbean = new com.mysql.jdbc.jmx.ReplicationGroupManager(); + + private static boolean hasRegisteredJmx = false; + + public static synchronized ReplicationConnectionGroup getConnectionGroupInstance(String groupName) { + if (GROUP_MAP.containsKey(groupName)) { + return GROUP_MAP.get(groupName); + } + ReplicationConnectionGroup group = new ReplicationConnectionGroup(groupName); + GROUP_MAP.put(groupName, group); + return group; + } + + public static void registerJmx() throws SQLException { + if (hasRegisteredJmx) { + return; + } + + mbean.registerJmx(); + hasRegisteredJmx = true; + } + + public static ReplicationConnectionGroup getConnectionGroup(String groupName) { + return GROUP_MAP.get(groupName); + } + + public static Collection getGroupsMatching(String group) { + if (group == null || group.equals("")) { + Set s = new HashSet(); + + s.addAll(GROUP_MAP.values()); + return s; + } + Set s = new HashSet(); + ReplicationConnectionGroup o = GROUP_MAP.get(group); + if (o != null) { + s.add(o); + } + return s; + } + + public static void addSlaveHost(String group, String hostPortPair) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.addSlaveHost(hostPortPair); + } + } + + public static void removeSlaveHost(String group, String hostPortPair) throws SQLException { + removeSlaveHost(group, hostPortPair, true); + } + + public static void removeSlaveHost(String group, String hostPortPair, boolean closeGently) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.removeSlaveHost(hostPortPair, closeGently); + } + } + + public static void promoteSlaveToMaster(String group, String hostPortPair) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.promoteSlaveToMaster(hostPortPair); + } + } + + public static long getSlavePromotionCount(String group) throws SQLException { + Collection s = getGroupsMatching(group); + long promoted = 0; + for (ReplicationConnectionGroup cg : s) { + long tmp = cg.getNumberOfSlavePromotions(); + if (tmp > promoted) { + promoted = tmp; + } + } + return promoted; + } + + public static void removeMasterHost(String group, String hostPortPair) throws SQLException { + removeMasterHost(group, hostPortPair, true); + } + + public static void removeMasterHost(String group, String hostPortPair, boolean closeGently) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.removeMasterHost(hostPortPair, closeGently); + } + } + + public static String getRegisteredReplicationConnectionGroups() { + Collection s = getGroupsMatching(null); + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (ReplicationConnectionGroup cg : s) { + String group = cg.getGroupName(); + sb.append(sep); + sb.append(group); + sep = ","; + } + return sb.toString(); + } + + public static int getNumberOfMasterPromotion(String groupFilter) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getNumberOfSlavePromotions(); + } + return total; + } + + public static int getConnectionCountWithHostAsSlave(String groupFilter, String hostPortPair) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getConnectionCountWithHostAsSlave(hostPortPair); + } + return total; + } + + public static int getConnectionCountWithHostAsMaster(String groupFilter, String hostPortPair) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getConnectionCountWithHostAsMaster(hostPortPair); + } + return total; + } + + public static Collection getSlaveHosts(String groupFilter) { + Collection s = getGroupsMatching(groupFilter); + Collection hosts = new ArrayList(); + for (ReplicationConnectionGroup cg : s) { + hosts.addAll(cg.getSlaveHosts()); + } + return hosts; + } + + public static Collection getMasterHosts(String groupFilter) { + Collection s = getGroupsMatching(groupFilter); + Collection hosts = new ArrayList(); + for (ReplicationConnectionGroup cg : s) { + hosts.addAll(cg.getMasterHosts()); + } + return hosts; + } + + public static long getTotalConnectionCount(String group) { + long connections = 0; + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + connections += cg.getTotalConnectionCount(); + } + return connections; + } + + public static long getActiveConnectionCount(String group) { + long connections = 0; + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + connections += cg.getActiveConnectionCount(); + } + return connections; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionProxy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionProxy.java new file mode 100644 index 0000000..8218c21 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationConnectionProxy.java @@ -0,0 +1,699 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; + +/** + * Connection that opens two connections, one two a replication master, and another to one or more slaves, and decides to use master when the connection is not + * read-only, and use slave(s) when the connection is read-only. + */ +public class ReplicationConnectionProxy extends MultiHostConnectionProxy implements PingTarget { + private ReplicationConnection thisAsReplicationConnection; + + private NonRegisteringDriver driver; + + protected boolean enableJMX = false; + protected boolean allowMasterDownConnections = false; + protected boolean allowSlaveDownConnections = false; + protected boolean readFromMasterWhenNoSlaves = false; + protected boolean readFromMasterWhenNoSlavesOriginal = false; + protected boolean readOnly = false; + + ReplicationConnectionGroup connectionGroup; + private long connectionGroupID = -1; + + private List masterHosts; + private Properties masterProperties; + protected LoadBalancedConnection masterConnection; + + private List slaveHosts; + private Properties slaveProperties; + protected LoadBalancedConnection slavesConnection; + + private static Constructor JDBC_4_REPL_CONNECTION_CTOR; + private static Class[] INTERFACES_TO_PROXY; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_REPL_CONNECTION_CTOR = Class.forName("com.mysql.jdbc.JDBC4ReplicationMySQLConnection") + .getConstructor(new Class[] { ReplicationConnectionProxy.class }); + INTERFACES_TO_PROXY = new Class[] { ReplicationConnection.class, Class.forName("com.mysql.jdbc.JDBC4MySQLConnection") }; + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + INTERFACES_TO_PROXY = new Class[] { ReplicationConnection.class }; + } + } + + public static ReplicationConnection createProxyInstance(List masterHostList, Properties masterProperties, List slaveHostList, + Properties slaveProperties) throws SQLException { + ReplicationConnectionProxy connProxy = new ReplicationConnectionProxy(masterHostList, masterProperties, slaveHostList, slaveProperties); + + return (ReplicationConnection) java.lang.reflect.Proxy.newProxyInstance(ReplicationConnection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy); + } + + /** + * Creates a proxy for java.sql.Connection that routes requests to a load-balanced connection of master servers or a load-balanced connection of slave + * servers. Each sub-connection is created with its own set of independent properties. + * + * @param masterHostList + * The list of hosts to use in the masters connection. + * @param masterProperties + * The properties for the masters connection. + * @param slaveHostList + * The list of hosts to use in the slaves connection. + * @param slaveProperties + * The properties for the slaves connection. + * @throws SQLException + */ + private ReplicationConnectionProxy(List masterHostList, Properties masterProperties, List slaveHostList, Properties slaveProperties) + throws SQLException { + super(); + + this.thisAsReplicationConnection = (ReplicationConnection) this.thisAsConnection; + + String enableJMXAsString = masterProperties.getProperty("replicationEnableJMX", "false"); + try { + this.enableJMX = Boolean.parseBoolean(enableJMXAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForReplicationEnableJMX", new Object[] { enableJMXAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String allowMasterDownConnectionsAsString = masterProperties.getProperty("allowMasterDownConnections", "false"); + try { + this.allowMasterDownConnections = Boolean.parseBoolean(allowMasterDownConnectionsAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForAllowMasterDownConnections", new Object[] { allowMasterDownConnectionsAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String allowSlaveDownConnectionsAsString = masterProperties.getProperty("allowSlaveDownConnections", "false"); + try { + this.allowSlaveDownConnections = Boolean.parseBoolean(allowSlaveDownConnectionsAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForAllowSlaveDownConnections", new Object[] { allowSlaveDownConnectionsAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String readFromMasterWhenNoSlavesAsString = masterProperties.getProperty("readFromMasterWhenNoSlaves"); + try { + this.readFromMasterWhenNoSlavesOriginal = Boolean.parseBoolean(readFromMasterWhenNoSlavesAsString); + + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForReadFromMasterWhenNoSlaves", new Object[] { readFromMasterWhenNoSlavesAsString }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String group = masterProperties.getProperty("replicationConnectionGroup", null); + if (group != null) { + this.connectionGroup = ReplicationConnectionGroupManager.getConnectionGroupInstance(group); + if (this.enableJMX) { + ReplicationConnectionGroupManager.registerJmx(); + } + this.connectionGroupID = this.connectionGroup.registerReplicationConnection(this.thisAsReplicationConnection, masterHostList, slaveHostList); + + this.slaveHosts = new ArrayList(this.connectionGroup.getSlaveHosts()); + this.masterHosts = new ArrayList(this.connectionGroup.getMasterHosts()); + } else { + this.slaveHosts = new ArrayList(slaveHostList); + this.masterHosts = new ArrayList(masterHostList); + } + + this.driver = new NonRegisteringDriver(); + this.slaveProperties = slaveProperties; + this.masterProperties = masterProperties; + + resetReadFromMasterWhenNoSlaves(); + + // Initialize slaves connection first so that it is ready to be used in case the masters connection fails and 'allowMasterDownConnections=true'. + try { + initializeSlavesConnection(); + } catch (SQLException e) { + if (!this.allowSlaveDownConnections) { + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + throw e; + } // Else swallow this exception. + } + + SQLException exCaught = null; + try { + this.currentConnection = initializeMasterConnection(); + } catch (SQLException e) { + exCaught = e; + } + + if (this.currentConnection == null) { + if (this.allowMasterDownConnections && this.slavesConnection != null) { + // Set read-only and fail over to the slaves connection. + this.readOnly = true; + this.currentConnection = this.slavesConnection; + } else { + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + if (exCaught != null) { + throw exCaught; + } + throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.initializationWithEmptyHostsLists"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + } + } + + /** + * Wraps this object with a new replication Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + @Override + MySQLConnection getNewWrapperForThisAsConnection() throws SQLException { + if (Util.isJdbc4() || JDBC_4_REPL_CONNECTION_CTOR != null) { + return (MySQLConnection) Util.handleNewInstance(JDBC_4_REPL_CONNECTION_CTOR, new Object[] { this }, null); + } + return new ReplicationMySQLConnection(this); + } + + /** + * Propagates the connection proxy down through all live connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + @Override + protected void propagateProxyDown(MySQLConnection proxyConn) { + if (this.masterConnection != null) { + this.masterConnection.setProxy(proxyConn); + } + if (this.slavesConnection != null) { + this.slavesConnection.setProxy(proxyConn); + } + } + + /** + * Has no use in replication connections. Always return false. + * + * @param ex + * The Exception instance to check. + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + return false; + } + + /** + * Checks if current connection is the masters l/b connection. + */ + @Override + public boolean isMasterConnection() { + return this.currentConnection != null && this.currentConnection == this.masterConnection; + } + + /** + * Checks if current connection is the slaves l/b connection. + */ + public boolean isSlavesConnection() { + return this.currentConnection != null && this.currentConnection == this.slavesConnection; + } + + @Override + void pickNewConnection() throws SQLException { + // no-op + } + + @Override + void doClose() throws SQLException { + if (this.masterConnection != null) { + this.masterConnection.close(); + } + if (this.slavesConnection != null) { + this.slavesConnection.close(); + } + + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + @Override + void doAbortInternal() throws SQLException { + this.masterConnection.abortInternal(); + this.slavesConnection.abortInternal(); + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + @Override + void doAbort(Executor executor) throws SQLException { + this.masterConnection.abort(executor); + this.slavesConnection.abort(executor); + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + /** + * Proxies method invocation on the java.sql.Connection interface. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + checkConnectionCapabilityForMethod(method); + + boolean invokeAgain = false; + while (true) { + try { + Object result = method.invoke(this.thisAsConnection, args); + if (result != null && result instanceof Statement) { + ((Statement) result).setPingTarget(this); + } + return result; + } catch (InvocationTargetException e) { + if (invokeAgain) { + invokeAgain = false; + } else if (e.getCause() != null && e.getCause() instanceof SQLException + && ((SQLException) e.getCause()).getSQLState() == SQLError.SQL_STATE_INVALID_TRANSACTION_STATE + && ((SQLException) e.getCause()).getErrorCode() == MysqlErrorNumbers.ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION) { + try { + // Try to re-establish the connection with the last known read-only state. + setReadOnly(this.readOnly); + invokeAgain = true; + } catch (SQLException sqlEx) { + // Still not good. Swallow this exception. + } + } + if (!invokeAgain) { + throw e; + } + } + } + } + + /** + * Checks if this connection is in a state capable to invoke the provided method. If the connection is in an inconsistent state, i.e. it has no hosts for + * both sub-connections, then throw an invalid transaction state exception. Nevertheless, the methods defined in the ReplicationConnection interface will be + * allowed as they are the only way to leave from an empty hosts lists situation. + */ + private void checkConnectionCapabilityForMethod(Method method) throws Throwable { + if (this.masterHosts.isEmpty() && this.slaveHosts.isEmpty() && !ReplicationConnection.class.isAssignableFrom(method.getDeclaringClass())) { + throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.noHostsInconsistentState"), + SQLError.SQL_STATE_INVALID_TRANSACTION_STATE, MysqlErrorNumbers.ERROR_CODE_REPLICATION_CONNECTION_WITH_NO_HOSTS, true, null); + } + } + + /** + * Pings both l/b connections. Switch to another connection in case of failure. + */ + public void doPing() throws SQLException { + boolean isMasterConn = isMasterConnection(); + + SQLException mastersPingException = null; + SQLException slavesPingException = null; + + if (this.masterConnection != null) { + try { + this.masterConnection.ping(); + } catch (SQLException e) { + mastersPingException = e; + } + } else { + initializeMasterConnection(); + } + + if (this.slavesConnection != null) { + try { + this.slavesConnection.ping(); + } catch (SQLException e) { + slavesPingException = e; + } + } else { + try { + initializeSlavesConnection(); + if (switchToSlavesConnectionIfNecessary()) { + isMasterConn = false; + } + } catch (SQLException e) { + if (this.masterConnection == null || !this.readFromMasterWhenNoSlaves) { + throw e; + } // Else swallow this exception. + } + } + + if (isMasterConn && mastersPingException != null) { + // Switch to slaves connection. + if (this.slavesConnection != null && slavesPingException == null) { + this.masterConnection = null; + this.currentConnection = this.slavesConnection; + this.readOnly = true; + } + throw mastersPingException; + + } else if (!isMasterConn && (slavesPingException != null || this.slavesConnection == null)) { + // Switch to masters connection, setting read-only state, if 'readFromMasterWhenNoSlaves=true'. + if (this.masterConnection != null && this.readFromMasterWhenNoSlaves && mastersPingException == null) { + this.slavesConnection = null; + this.currentConnection = this.masterConnection; + this.readOnly = true; + this.currentConnection.setReadOnly(true); + } + if (slavesPingException != null) { + throw slavesPingException; + } + } + } + + private MySQLConnection initializeMasterConnection() throws SQLException { + this.masterConnection = null; + + if (this.masterHosts.size() == 0) { + return null; + } + + LoadBalancedConnection newMasterConn = (LoadBalancedConnection) this.driver.connect(buildURL(this.masterHosts, this.masterProperties), + this.masterProperties); + newMasterConn.setProxy(getProxy()); + + this.masterConnection = newMasterConn; + return this.masterConnection; + } + + private MySQLConnection initializeSlavesConnection() throws SQLException { + this.slavesConnection = null; + + if (this.slaveHosts.size() == 0) { + return null; + } + + LoadBalancedConnection newSlavesConn = (LoadBalancedConnection) this.driver.connect(buildURL(this.slaveHosts, this.slaveProperties), + this.slaveProperties); + newSlavesConn.setProxy(getProxy()); + newSlavesConn.setReadOnly(true); + + this.slavesConnection = newSlavesConn; + return this.slavesConnection; + } + + private String buildURL(List hosts, Properties props) { + StringBuilder url = new StringBuilder(NonRegisteringDriver.LOADBALANCE_URL_PREFIX); + + boolean firstHost = true; + for (String host : hosts) { + if (!firstHost) { + url.append(','); + } + url.append(host); + firstHost = false; + } + url.append("/"); + String masterDb = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (masterDb != null) { + url.append(masterDb); + } + + return url.toString(); + } + + private synchronized boolean switchToMasterConnection() throws SQLException { + if (this.masterConnection == null || this.masterConnection.isClosed()) { + try { + if (initializeMasterConnection() == null) { + return false; + } + } catch (SQLException e) { + this.currentConnection = null; + throw e; + } + } + if (!isMasterConnection() && this.masterConnection != null) { + syncSessionState(this.currentConnection, this.masterConnection, false); + this.currentConnection = this.masterConnection; + } + return true; + } + + private synchronized boolean switchToSlavesConnection() throws SQLException { + if (this.slavesConnection == null || this.slavesConnection.isClosed()) { + try { + if (initializeSlavesConnection() == null) { + return false; + } + } catch (SQLException e) { + this.currentConnection = null; + throw e; + } + } + if (!isSlavesConnection() && this.slavesConnection != null) { + syncSessionState(this.currentConnection, this.slavesConnection, true); + this.currentConnection = this.slavesConnection; + } + return true; + } + + private boolean switchToSlavesConnectionIfNecessary() throws SQLException { + // Switch to slaves connection: + // - If the current connection is null. Or, + // - If we're currently on the master and in read-only mode - we didn't have any slaves to use until now. Or, + // - If we're currently on a closed master connection and there are no masters to connect to. Or, + // - If we're currently not on a master connection that is closed - means that we were on a closed slaves connection before it was re-initialized. + if (this.currentConnection == null || isMasterConnection() && (this.readOnly || this.masterHosts.isEmpty() && this.currentConnection.isClosed()) + || !isMasterConnection() && this.currentConnection.isClosed()) { + return switchToSlavesConnection(); + } + return false; + } + + public synchronized Connection getCurrentConnection() { + return this.currentConnection == null ? LoadBalancedConnectionProxy.getNullLoadBalancedConnectionInstance() : this.currentConnection; + } + + public long getConnectionGroupId() { + return this.connectionGroupID; + } + + public synchronized Connection getMasterConnection() { + return this.masterConnection; + } + + public synchronized void promoteSlaveToMaster(String hostPortPair) throws SQLException { + this.masterHosts.add(hostPortPair); + removeSlave(hostPortPair); + if (this.masterConnection != null) { + this.masterConnection.addHost(hostPortPair); + } + + // Switch back to the masters connection if this connection was running in fail-safe mode. + if (!this.readOnly && !isMasterConnection()) { + switchToMasterConnection(); + } + } + + public synchronized void removeMasterHost(String hostPortPair) throws SQLException { + this.removeMasterHost(hostPortPair, true); + } + + public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse) throws SQLException { + this.removeMasterHost(hostPortPair, waitUntilNotInUse, false); + } + + public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse, boolean isNowSlave) throws SQLException { + if (isNowSlave) { + this.slaveHosts.add(hostPortPair); + resetReadFromMasterWhenNoSlaves(); + } + this.masterHosts.remove(hostPortPair); + + // The master connection may have been implicitly closed by a previous op., don't let it stop us. + if (this.masterConnection == null || this.masterConnection.isClosed()) { + this.masterConnection = null; + return; + } + + if (waitUntilNotInUse) { + this.masterConnection.removeHostWhenNotInUse(hostPortPair); + } else { + this.masterConnection.removeHost(hostPortPair); + } + + // Close the connection if that was the last master. + if (this.masterHosts.isEmpty()) { + this.masterConnection.close(); + this.masterConnection = null; + + // Default behavior, no need to check this.readFromMasterWhenNoSlaves. + switchToSlavesConnectionIfNecessary(); + } + } + + public boolean isHostMaster(String hostPortPair) { + if (hostPortPair == null) { + return false; + } + for (String masterHost : this.masterHosts) { + if (masterHost.equalsIgnoreCase(hostPortPair)) { + return true; + } + } + return false; + } + + public synchronized Connection getSlavesConnection() { + return this.slavesConnection; + } + + public synchronized void addSlaveHost(String hostPortPair) throws SQLException { + if (this.isHostSlave(hostPortPair)) { + return; + } + this.slaveHosts.add(hostPortPair); + resetReadFromMasterWhenNoSlaves(); + if (this.slavesConnection == null) { + initializeSlavesConnection(); + switchToSlavesConnectionIfNecessary(); + } else { + this.slavesConnection.addHost(hostPortPair); + } + } + + public synchronized void removeSlave(String hostPortPair) throws SQLException { + removeSlave(hostPortPair, true); + } + + public synchronized void removeSlave(String hostPortPair, boolean closeGently) throws SQLException { + this.slaveHosts.remove(hostPortPair); + resetReadFromMasterWhenNoSlaves(); + + if (this.slavesConnection == null || this.slavesConnection.isClosed()) { + this.slavesConnection = null; + return; + } + + if (closeGently) { + this.slavesConnection.removeHostWhenNotInUse(hostPortPair); + } else { + this.slavesConnection.removeHost(hostPortPair); + } + + // Close the connection if that was the last slave. + if (this.slaveHosts.isEmpty()) { + this.slavesConnection.close(); + this.slavesConnection = null; + + // Default behavior, no need to check this.readFromMasterWhenNoSlaves. + switchToMasterConnection(); + if (isMasterConnection()) { + this.currentConnection.setReadOnly(this.readOnly); // Maintain. + } + } + } + + public boolean isHostSlave(String hostPortPair) { + if (hostPortPair == null) { + return false; + } + for (String test : this.slaveHosts) { + if (test.equalsIgnoreCase(hostPortPair)) { + return true; + } + } + return false; + + } + + public synchronized void setReadOnly(boolean readOnly) throws SQLException { + if (readOnly) { + if (!isSlavesConnection() || this.currentConnection.isClosed()) { + boolean switched = true; + SQLException exceptionCaught = null; + try { + switched = switchToSlavesConnection(); + } catch (SQLException e) { + switched = false; + exceptionCaught = e; + } + if (!switched && this.readFromMasterWhenNoSlaves && switchToMasterConnection()) { + exceptionCaught = null; // The connection is OK. Cancel the exception, if any. + } + if (exceptionCaught != null) { + throw exceptionCaught; + } + } + } else { + if (!isMasterConnection() || this.currentConnection.isClosed()) { + boolean switched = true; + SQLException exceptionCaught = null; + try { + switched = switchToMasterConnection(); + } catch (SQLException e) { + switched = false; + exceptionCaught = e; + } + if (!switched && switchToSlavesConnectionIfNecessary()) { + exceptionCaught = null; // The connection is OK. Cancel the exception, if any. + } + if (exceptionCaught != null) { + throw exceptionCaught; + } + } + } + this.readOnly = readOnly; + + /* + * Reset masters connection read-only state if 'readFromMasterWhenNoSlaves=true'. If there are no slaves then the masters connection will be used with + * read-only state in its place. Even if not, it must be reset from a possible previous read-only state. + */ + if (this.readFromMasterWhenNoSlaves && isMasterConnection()) { + this.currentConnection.setReadOnly(this.readOnly); + } + } + + public boolean isReadOnly() throws SQLException { + return !isMasterConnection() || this.readOnly; + } + + private void resetReadFromMasterWhenNoSlaves() { + this.readFromMasterWhenNoSlaves = this.slaveHosts.isEmpty() || this.readFromMasterWhenNoSlavesOriginal; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationDriver.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationDriver.java new file mode 100644 index 0000000..c60ce56 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationDriver.java @@ -0,0 +1,64 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface + * + *

    + * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to + * connect to the target URL. + * + *

    + * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast + * quantities of supporting code. + * + *

    + * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a + * driver by doing Class.forName("foo.bah.Driver") + */ +public class ReplicationDriver extends NonRegisteringReplicationDriver implements java.sql.Driver { + // + // Register ourselves with the DriverManager + // + static { + try { + java.sql.DriverManager.registerDriver(new NonRegisteringReplicationDriver()); + } catch (SQLException E) { + throw new RuntimeException("Can't register driver!"); + } + } + + /** + * Construct a new driver and register it with DriverManager + * + * @throws SQLException + * if a database error occurs. + */ + public ReplicationDriver() throws SQLException { + // Required for Class.forName().newInstance() + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationMySQLConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationMySQLConnection.java new file mode 100644 index 0000000..72a95d9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ReplicationMySQLConnection.java @@ -0,0 +1,224 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class ReplicationMySQLConnection extends MultiHostMySQLConnection implements ReplicationConnection { + public ReplicationMySQLConnection(MultiHostConnectionProxy proxy) { + super(proxy); + } + + @Override + protected ReplicationConnectionProxy getThisAsProxy() { + return (ReplicationConnectionProxy) super.getThisAsProxy(); + } + + @Override + protected MySQLConnection getActiveMySQLConnection() { + return (MySQLConnection) getCurrentConnection(); + } + + public synchronized Connection getCurrentConnection() { + return getThisAsProxy().getCurrentConnection(); + } + + public long getConnectionGroupId() { + return getThisAsProxy().getConnectionGroupId(); + } + + public synchronized Connection getMasterConnection() { + return getThisAsProxy().getMasterConnection(); + } + + private Connection getValidatedMasterConnection() { + Connection conn = getThisAsProxy().masterConnection; + try { + return conn == null || conn.isClosed() ? null : conn; + } catch (SQLException e) { + return null; + } + } + + public void promoteSlaveToMaster(String host) throws SQLException { + getThisAsProxy().promoteSlaveToMaster(host); + } + + public void removeMasterHost(String host) throws SQLException { + getThisAsProxy().removeMasterHost(host); + } + + public void removeMasterHost(String host, boolean waitUntilNotInUse) throws SQLException { + getThisAsProxy().removeMasterHost(host, waitUntilNotInUse); + } + + public boolean isHostMaster(String host) { + return getThisAsProxy().isHostMaster(host); + } + + public synchronized Connection getSlavesConnection() { + return getThisAsProxy().getSlavesConnection(); + } + + private Connection getValidatedSlavesConnection() { + Connection conn = getThisAsProxy().slavesConnection; + try { + return conn == null || conn.isClosed() ? null : conn; + } catch (SQLException e) { + return null; + } + } + + public void addSlaveHost(String host) throws SQLException { + getThisAsProxy().addSlaveHost(host); + } + + public void removeSlave(String host) throws SQLException { + getThisAsProxy().removeSlave(host); + } + + public void removeSlave(String host, boolean closeGently) throws SQLException { + getThisAsProxy().removeSlave(host, closeGently); + } + + public boolean isHostSlave(String host) { + return getThisAsProxy().isHostSlave(host); + } + + @Override + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + getThisAsProxy().setReadOnly(readOnlyFlag); + } + + @Override + public boolean isReadOnly() throws SQLException { + return getThisAsProxy().isReadOnly(); + } + + @Override + public synchronized void ping() throws SQLException { + Connection conn; + try { + if ((conn = getValidatedMasterConnection()) != null) { + conn.ping(); + } + } catch (SQLException e) { + if (isMasterConnection()) { + throw e; + } + } + try { + if ((conn = getValidatedSlavesConnection()) != null) { + conn.ping(); + } + } catch (SQLException e) { + if (!isMasterConnection()) { + throw e; + } + } + } + + @Override + public synchronized void changeUser(String userName, String newPassword) throws SQLException { + Connection conn; + if ((conn = getValidatedMasterConnection()) != null) { + conn.changeUser(userName, newPassword); + } + if ((conn = getValidatedSlavesConnection()) != null) { + conn.changeUser(userName, newPassword); + } + } + + @Override + public synchronized void setStatementComment(String comment) { + Connection conn; + if ((conn = getValidatedMasterConnection()) != null) { + conn.setStatementComment(comment); + } + if ((conn = getValidatedSlavesConnection()) != null) { + conn.setStatementComment(comment); + } + } + + @Override + public boolean hasSameProperties(Connection c) { + Connection connM = getValidatedMasterConnection(); + Connection connS = getValidatedSlavesConnection(); + if (connM == null && connS == null) { + return false; + } + return (connM == null || connM.hasSameProperties(c)) && (connS == null || connS.hasSameProperties(c)); + } + + @Override + public Properties getProperties() { + Properties props = new Properties(); + Connection conn; + if ((conn = getValidatedMasterConnection()) != null) { + props.putAll(conn.getProperties()); + } + if ((conn = getValidatedSlavesConnection()) != null) { + props.putAll(conn.getProperties()); + } + + return props; + } + + @Override + public void abort(Executor executor) throws SQLException { + getThisAsProxy().doAbort(executor); + } + + @Override + public void abortInternal() throws SQLException { + getThisAsProxy().doAbortInternal(); + } + + @Override + public boolean getAllowMasterDownConnections() { + return getThisAsProxy().allowMasterDownConnections; + } + + @Override + public void setAllowMasterDownConnections(boolean connectIfMasterDown) { + getThisAsProxy().allowMasterDownConnections = connectIfMasterDown; + } + + @Override + public boolean getReplicationEnableJMX() { + return getThisAsProxy().enableJMX; + } + + @Override + public void setReplicationEnableJMX(boolean replicationEnableJMX) { + getThisAsProxy().enableJMX = replicationEnableJMX; + } + + @Override + public void setProxy(MySQLConnection proxy) { + getThisAsProxy().setProxy(proxy); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetImpl.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetImpl.java new file mode 100644 index 0000000..d6cda59 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetImpl.java @@ -0,0 +1,7909 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.sql.Array; +import java.sql.Date; +import java.sql.Ref; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TimeZone; +import java.util.TreeMap; + +import com.mysql.jdbc.log.LogUtils; +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +/** + * A ResultSet provides access to a table of data generated by executing a Statement. The table rows are retrieved in sequence. Within a row its column + * values can be accessed in any order. + * + *

    + * A ResultSet maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The 'next' method moves the + * cursor to the next row. + *

    + * + *

    + * The getXXX methods retrieve column values for the current row. You can retrieve values either using the index number of the column, or by using the name of + * the column. In general using the column index will be more efficient. Columns are numbered from 1. + *

    + * + *

    + * For maximum portability, ResultSet columns within each row should be read in left-to-right order and each column should be read only once. + *

    + * + *

    + * For the getXXX methods, the JDBC driver attempts to convert the underlying data to the specified Java type and returns a suitable Java value. See the JDBC + * specification for allowable mappings from SQL types to Java types with the ResultSet getXXX methods. + *

    + * + *

    + * Column names used as input to getXXX methods are case insenstive. When performing a getXXX using a column name, if several columns have the same name, then + * the value of the first matching column will be returned. The column name option is designed to be used when column names are used in the SQL Query. For + * columns that are NOT explicitly named in the query, it is best to use column numbers. If column names were used there is no way for the programmer to + * guarentee that they actually refer to the intended columns. + *

    + * + *

    + * A ResultSet is automatically closed by the Statement that generated it when that Statement is closed, re-executed, or is used to retrieve the next result + * from a sequence of multiple results. + *

    + * + *

    + * The number, types and properties of a ResultSet's columns are provided by the ResultSetMetaData object returned by the getMetaData method. + *

    + */ +public class ResultSetImpl implements ResultSetInternalMethods { + + private static final Constructor JDBC_4_RS_4_ARG_CTOR; + private static final Constructor JDBC_4_RS_5_ARG_CTOR;; + private static final Constructor JDBC_4_UPD_RS_5_ARG_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42ResultSet" : "com.mysql.jdbc.JDBC4ResultSet"; + JDBC_4_RS_4_ARG_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { Long.TYPE, Long.TYPE, MySQLConnection.class, com.mysql.jdbc.StatementImpl.class }); + JDBC_4_RS_5_ARG_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { String.class, Field[].class, RowData.class, MySQLConnection.class, com.mysql.jdbc.StatementImpl.class }); + + jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42UpdatableResultSet" : "com.mysql.jdbc.JDBC4UpdatableResultSet"; + JDBC_4_UPD_RS_5_ARG_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { String.class, Field[].class, RowData.class, MySQLConnection.class, com.mysql.jdbc.StatementImpl.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_RS_4_ARG_CTOR = null; + JDBC_4_RS_5_ARG_CTOR = null; + JDBC_4_UPD_RS_5_ARG_CTOR = null; + } + } + + /** + * Epsillon between Float.MIN_VALUE and the double representation of said value. + */ + protected static final double MIN_DIFF_PREC = Float.parseFloat(Float.toString(Float.MIN_VALUE)) - Double.parseDouble(Float.toString(Float.MIN_VALUE)); + + /** + * Epsillon between Float.MAX_VALUE and the double representation of said value. + */ + protected static final double MAX_DIFF_PREC = Float.parseFloat(Float.toString(Float.MAX_VALUE)) - Double.parseDouble(Float.toString(Float.MAX_VALUE)); + + /** Counter used to generate IDs for profiling. */ + static int resultCounter = 1; + + /** + * Converts the given value as a java long, to an 'unsigned' long, using the java.math.BigInteger class. + */ + protected static BigInteger convertLongToUlong(long longVal) { + byte[] asBytes = new byte[8]; + asBytes[7] = (byte) (longVal & 0xff); + asBytes[6] = (byte) (longVal >>> 8); + asBytes[5] = (byte) (longVal >>> 16); + asBytes[4] = (byte) (longVal >>> 24); + asBytes[3] = (byte) (longVal >>> 32); + asBytes[2] = (byte) (longVal >>> 40); + asBytes[1] = (byte) (longVal >>> 48); + asBytes[0] = (byte) (longVal >>> 56); + + return new BigInteger(1, asBytes); + } + + /** The catalog that was in use when we were created */ + protected String catalog = null; + + /** Map column names (and all of their permutations) to column indices */ + protected Map columnLabelToIndex = null; + + /** + * The above map is a case-insensitive tree-map, it can be slow, this caches lookups into that map, because the other alternative is to create new + * object instances for every call to findColumn().... + */ + protected Map columnToIndexCache = null; + + /** Keep track of columns accessed */ + protected boolean[] columnUsed = null; + + /** The Connection instance that created us */ + protected volatile MySQLConnection connection; // The connection that created us + + protected long connectionId = 0; + + /** The current row #, -1 == before start of result set */ + protected int currentRow = -1; // Cursor to current row; + + /** Are we in the middle of doing updates to the current row? */ + protected boolean doingUpdates = false; + + protected ProfilerEventHandler eventSink = null; + + Calendar fastDefaultCal = null; + Calendar fastClientCal = null; + + /** The direction to fetch rows (always FETCH_FORWARD) */ + protected int fetchDirection = FETCH_FORWARD; + + /** The number of rows to fetch in one go... */ + protected int fetchSize = 0; + + /** The fields for this result set */ + protected Field[] fields; // The fields + + /** + * First character of the query that created this result set...Used to determine whether or not to parse server info messages in certain + * circumstances. + */ + protected char firstCharOfQuery; + + /** Map of fully-specified column names to column indices */ + protected Map fullColumnNameToIndex = null; + + protected Map columnNameToIndex = null; + + protected boolean hasBuiltIndexMapping = false; + + /** + * Is the data stored as strings (default) or natively (which is the case with results from PrepStmts) + */ + protected boolean isBinaryEncoded = false; + + /** Has this result set been closed? */ + protected boolean isClosed = false; + + protected ResultSetInternalMethods nextResultSet = null; + + /** Are we on the insert row? */ + protected boolean onInsertRow = false; + + /** The statement that created us */ + protected com.mysql.jdbc.StatementImpl owningStatement; + + /** + * StackTrace generated where ResultSet was created... used when profiling + */ + protected String pointOfOrigin; + + /** Are we tracking items for profileSql? */ + protected boolean profileSql = false; + + /** + * Do we actually contain rows, or just information about UPDATE/INSERT/DELETE? + */ + protected boolean reallyResult = false; + + /** The id (used when profiling) to identify us */ + protected int resultId; + + /** Are we read-only or updatable? */ + protected int resultSetConcurrency = 0; + + /** Are we scroll-sensitive/insensitive? */ + protected int resultSetType = 0; + + /** The actual rows */ + protected RowData rowData; // The results + + /** + * Any info message from the server that was created while generating this result set (if 'info parsing' is enabled for the connection). + */ + protected String serverInfo = null; + + PreparedStatement statementUsedForFetchingRows; + + /** Pointer to current row data */ + protected ResultSetRow thisRow = null; // Values for current row + + // These are longs for + // recent versions of the MySQL server. + // + // They get reduced to ints via the JDBC API, + // but can be retrieved through a MySQLStatement + // in their entirety. + // + + /** How many rows were affected by UPDATE/INSERT/DELETE? */ + protected long updateCount; + + /** Value generated for AUTO_INCREMENT columns */ + protected long updateId = -1; + + private boolean useStrictFloatingPoint = false; + + protected boolean useUsageAdvisor = false; + + /** The warning chain */ + protected java.sql.SQLWarning warningChain = null; + + /** Did the previous value retrieval find a NULL? */ + protected boolean wasNullFlag = false; + + protected java.sql.Statement wrapperStatement; + + protected boolean retainOwningStatement; + + protected Calendar gmtCalendar = null; + + protected boolean useFastDateParsing = false; + + private boolean padCharsWithSpace = false; + + private boolean jdbcCompliantTruncationForReads; + + private boolean useFastIntParsing = true; + private boolean useColumnNamesInFindColumn; + + private ExceptionInterceptor exceptionInterceptor; + + final static char[] EMPTY_SPACE = new char[255]; + + static { + for (int i = 0; i < EMPTY_SPACE.length; i++) { + EMPTY_SPACE[i] = ' '; + } + } + + protected static ResultSetImpl getInstance(long updateCount, long updateID, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + if (!Util.isJdbc4()) { + return new ResultSetImpl(updateCount, updateID, conn, creatorStmt); + } + + return (ResultSetImpl) Util.handleNewInstance(JDBC_4_RS_4_ARG_CTOR, + new Object[] { Long.valueOf(updateCount), Long.valueOf(updateID), conn, creatorStmt }, conn.getExceptionInterceptor()); + } + + /** + * Creates a result set instance that represents a query result -- We need + * to provide factory-style methods so we can support both JDBC3 (and older) + * and JDBC4 runtimes, otherwise the class verifier complains when it tries + * to load JDBC4-only interface classes that are present in JDBC4 method + * signatures. + */ + + protected static ResultSetImpl getInstance(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt, + boolean isUpdatable) throws SQLException { + if (!Util.isJdbc4()) { + if (!isUpdatable) { + return new ResultSetImpl(catalog, fields, tuples, conn, creatorStmt); + } + + return new UpdatableResultSet(catalog, fields, tuples, conn, creatorStmt); + } + + if (!isUpdatable) { + return (ResultSetImpl) Util.handleNewInstance(JDBC_4_RS_5_ARG_CTOR, new Object[] { catalog, fields, tuples, conn, creatorStmt }, + conn.getExceptionInterceptor()); + } + + return (ResultSetImpl) Util.handleNewInstance(JDBC_4_UPD_RS_5_ARG_CTOR, new Object[] { catalog, fields, tuples, conn, creatorStmt }, + conn.getExceptionInterceptor()); + } + + /** + * Create a result set for an executeUpdate statement. + * + * @param updateCount + * the number of rows affected by the update + * @param updateID + * the autoincrement value (if any) + * @param conn + * @param creatorStmt + */ + public ResultSetImpl(long updateCount, long updateID, MySQLConnection conn, StatementImpl creatorStmt) { + this.updateCount = updateCount; + this.updateId = updateID; + this.reallyResult = false; + this.fields = new Field[0]; + + this.connection = conn; + this.owningStatement = creatorStmt; + + this.retainOwningStatement = false; + + if (this.connection != null) { + this.exceptionInterceptor = this.connection.getExceptionInterceptor(); + + this.retainOwningStatement = this.connection.getRetainStatementAfterResultSetClose(); + + this.connectionId = this.connection.getId(); + this.serverTimeZoneTz = this.connection.getServerTimezoneTZ(); + this.padCharsWithSpace = this.connection.getPadCharsWithSpace(); + + this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode(); + } + } + + /** + * Creates a new ResultSet object. + * + * @param catalog + * the database in use when we were created + * @param fields + * an array of Field objects (basically, the ResultSet MetaData) + * @param tuples + * actual row data + * @param conn + * the Connection that created us. + * @param creatorStmt + * + * @throws SQLException + * if an error occurs + */ + public ResultSetImpl(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + this.connection = conn; + + this.retainOwningStatement = false; + + if (this.connection != null) { + this.exceptionInterceptor = this.connection.getExceptionInterceptor(); + this.useStrictFloatingPoint = this.connection.getStrictFloatingPoint(); + this.connectionId = this.connection.getId(); + this.useFastDateParsing = this.connection.getUseFastDateParsing(); + this.profileSql = this.connection.getProfileSql(); + this.retainOwningStatement = this.connection.getRetainStatementAfterResultSetClose(); + this.jdbcCompliantTruncationForReads = this.connection.getJdbcCompliantTruncationForReads(); + this.useFastIntParsing = this.connection.getUseFastIntParsing(); + this.serverTimeZoneTz = this.connection.getServerTimezoneTZ(); + this.padCharsWithSpace = this.connection.getPadCharsWithSpace(); + } + + this.owningStatement = creatorStmt; + + this.catalog = catalog; + + this.fields = fields; + this.rowData = tuples; + this.updateCount = this.rowData.size(); + + if (NonRegisteringDriver.DEBUG) { + System.out.println(Messages.getString("ResultSet.Retrieved__1") + this.updateCount + " rows"); + } + + this.reallyResult = true; + + // Check for no results + if (this.rowData.size() > 0) { + if (this.updateCount == 1) { + if (this.thisRow == null) { + this.rowData.close(); // empty result set + this.updateCount = -1; + } + } + } else { + this.thisRow = null; + } + + this.rowData.setOwner(this); + + if (this.fields != null) { + initializeWithMetadata(); + } // else called by Connection.initializeResultsMetadataFromCache() when cached + this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode(); + + this.useColumnNamesInFindColumn = this.connection.getUseColumnNamesInFindColumn(); + + setRowPositionValidity(); + } + + public void initializeWithMetadata() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.rowData.setMetadata(this.fields); + + this.columnToIndexCache = new HashMap(); + + if (this.profileSql || this.connection.getUseUsageAdvisor()) { + this.columnUsed = new boolean[this.fields.length]; + this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable()); + this.resultId = resultCounter++; + this.useUsageAdvisor = this.connection.getUseUsageAdvisor(); + this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + } + + if (this.connection.getGatherPerformanceMetrics()) { + this.connection.incrementNumberOfResultSetsCreated(); + + Set tableNamesSet = new HashSet(); + + for (int i = 0; i < this.fields.length; i++) { + Field f = this.fields[i]; + + String tableName = f.getOriginalTableName(); + + if (tableName == null) { + tableName = f.getTableName(); + } + + if (tableName != null) { + if (this.connection.lowerCaseTableNames()) { + tableName = tableName.toLowerCase(); // on windows, table + // names are not case-sens. + } + + tableNamesSet.add(tableName); + } + } + + this.connection.reportNumberOfTablesAccessed(tableNamesSet.size()); + } + } + } + + private synchronized Calendar getFastDefaultCalendar() { + if (this.fastDefaultCal == null) { + this.fastDefaultCal = new GregorianCalendar(Locale.US); + this.fastDefaultCal.setTimeZone(this.getDefaultTimeZone()); + } + return this.fastDefaultCal; + } + + private synchronized Calendar getFastClientCalendar() { + if (this.fastClientCal == null) { + this.fastClientCal = new GregorianCalendar(Locale.US); + } + return this.fastClientCal; + } + + /** + * JDBC 2.0 + * + *

    + * Move to an absolute row number in the result set. + *

    + * + *

    + * If row is positive, moves to an absolute row with respect to the beginning of the result set. The first row is row 1, the second is row 2, etc. + *

    + * + *

    + * If row is negative, moves to an absolute row position with respect to the end of result set. For example, calling absolute(-1) positions the cursor on + * the last row, absolute(-2) indicates the next-to-last row, etc. + *

    + * + *

    + * An attempt to position the cursor beyond the first/last row in the result set, leaves the cursor before/after the first/last row, respectively. + *

    + * + *

    + * Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last(). + *

    + * + * @param row + * the row number to move to + * + * @return true if on the result set, false if off. + * + * @exception SQLException + * if a database-access error occurs, or row is 0, or result + * set type is TYPE_FORWARD_ONLY. + */ + public boolean absolute(int row) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + boolean b; + + if (this.rowData.size() == 0) { + b = false; + } else { + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + if (row == 0) { + beforeFirst(); + b = false; + } else if (row == 1) { + b = first(); + } else if (row == -1) { + b = last(); + } else if (row > this.rowData.size()) { + afterLast(); + b = false; + } else { + if (row < 0) { + // adjust to reflect after end of result set + int newRowPosition = this.rowData.size() + row + 1; + + if (newRowPosition <= 0) { + beforeFirst(); + b = false; + } else { + b = absolute(newRowPosition); + } + } else { + row--; // adjust for index difference + this.rowData.setCurrentRow(row); + this.thisRow = this.rowData.getAt(row); + b = true; + } + } + } + + setRowPositionValidity(); + + return b; + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the end of the result set, just after the last row. Has no effect if the result set contains no rows. + *

    + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + public void afterLast() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + if (this.rowData.size() != 0) { + this.rowData.afterLast(); + this.thisRow = null; + } + + setRowPositionValidity(); + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the front of the result set, just before the first row. Has no effect if the result set contains no rows. + *

    + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY + */ + public void beforeFirst() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + if (this.rowData.size() == 0) { + return; + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + this.rowData.beforeFirst(); + this.thisRow = null; + + setRowPositionValidity(); + } + } + + // --------------------------------------------------------------------- + // Traversal/Positioning + // --------------------------------------------------------------------- + + /** + * Builds a hash between column names and their indices for fast retrieval. + */ + public void buildIndexMapping() throws SQLException { + int numFields = this.fields.length; + this.columnLabelToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER); + this.fullColumnNameToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER); + this.columnNameToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER); + + // We do this in reverse order, so that the 'first' column with a given name ends up as the final mapping in the hashtable... + // + // Quoting the JDBC Spec: + // + // "Column names used as input to getter methods are case insensitive. When a getter method is called with a column name and several columns have the + // same name, the value of the first matching column will be returned. " + // + for (int i = numFields - 1; i >= 0; i--) { + Integer index = Integer.valueOf(i); + String columnName = this.fields[i].getOriginalName(); + String columnLabel = this.fields[i].getName(); + String fullColumnName = this.fields[i].getFullName(); + + if (columnLabel != null) { + this.columnLabelToIndex.put(columnLabel, index); + } + + if (fullColumnName != null) { + this.fullColumnNameToIndex.put(fullColumnName, index); + } + + if (columnName != null) { + this.columnNameToIndex.put(columnName, index); + } + } + + // set the flag to prevent rebuilding... + this.hasBuiltIndexMapping = true; + } + + /** + * JDBC 2.0 The cancelRowUpdates() method may be called after calling an + * updateXXX() method(s) and before calling updateRow() to rollback the + * updates made to a row. If no updates have been made or updateRow() has + * already been called, then this method has no effect. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws NotUpdatable + */ + public void cancelRowUpdates() throws SQLException { + throw new NotUpdatable(); + } + + /** + * Ensures that the result set is not closed + * + * @throws SQLException + * if the result set is closed + */ + protected final MySQLConnection checkClosed() throws SQLException { + MySQLConnection c = this.connection; + + if (c == null) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + return c; + } + + /** + * Checks if columnIndex is within the number of columns in this result set. + * + * @param columnIndex + * the index to check + * + * @throws SQLException + * if the index is out of bounds + */ + protected final void checkColumnBounds(int columnIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((columnIndex < 1)) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Column_Index_out_of_range_low", + new Object[] { Integer.valueOf(columnIndex), Integer.valueOf(this.fields.length) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } else if ((columnIndex > this.fields.length)) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Column_Index_out_of_range_high", + new Object[] { Integer.valueOf(columnIndex), Integer.valueOf(this.fields.length) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.profileSql || this.useUsageAdvisor) { + this.columnUsed[columnIndex - 1] = true; + } + } + } + + /** + * Ensures that the cursor is positioned on a valid row and that the result + * set is not closed + * + * @throws SQLException + * if the result set is not in a valid state for traversal + */ + protected void checkRowPos() throws SQLException { + checkClosed(); + + if (!this.onValidRow) { + throw SQLError.createSQLException(this.invalidRowReason, SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + + private boolean onValidRow = false; + private String invalidRowReason = null; + protected boolean useLegacyDatetimeCode; + private TimeZone serverTimeZoneTz; + + private void setRowPositionValidity() throws SQLException { + if (!this.rowData.isDynamic() && (this.rowData.size() == 0)) { + this.invalidRowReason = Messages.getString("ResultSet.Illegal_operation_on_empty_result_set"); + this.onValidRow = false; + } else if (this.rowData.isBeforeFirst()) { + this.invalidRowReason = Messages.getString("ResultSet.Before_start_of_result_set_146"); + this.onValidRow = false; + } else if (this.rowData.isAfterLast()) { + this.invalidRowReason = Messages.getString("ResultSet.After_end_of_result_set_148"); + this.onValidRow = false; + } else { + this.onValidRow = true; + this.invalidRowReason = null; + } + } + + /** + * We can't do this ourselves, otherwise the contract for + * Statement.getMoreResults() won't work correctly. + */ + public synchronized void clearNextResult() { + this.nextResultSet = null; + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this ResultSet + * + * @exception SQLException + * if a database access error occurs + */ + public void clearWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.warningChain = null; + } + } + + /** + * In some cases, it is desirable to immediately release a ResultSet + * database and JDBC resources instead of waiting for this to happen when it + * is automatically closed. The close method provides this immediate + * release. + * + *

    + * Note: A ResultSet is automatically closed by the Statement the Statement that generated it when that Statement is closed, re-executed, or is used + * to retrieve the next result from a sequence of multiple results. A ResultSet is also automatically closed when it is garbage collected. + *

    + * + * @exception SQLException + * if a database access error occurs + */ + public void close() throws SQLException { + realClose(true); + } + + private int convertToZeroWithEmptyCheck() throws SQLException { + if (this.connection.getEmptyStringsConvertToZero()) { + return 0; + } + + throw SQLError.createSQLException("Can't convert empty string ('') to numeric", SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, + getExceptionInterceptor()); + } + + private String convertToZeroLiteralStringWithEmptyCheck() throws SQLException { + + if (this.connection.getEmptyStringsConvertToZero()) { + return "0"; + } + + throw SQLError.createSQLException("Can't convert empty string ('') to numeric", SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, + getExceptionInterceptor()); + } + + // + // Note, row data is linked between these two result sets + // + public ResultSetInternalMethods copy() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = ResultSetImpl.getInstance(this.catalog, this.fields, this.rowData, this.connection, this.owningStatement, false); // note, doesn't work for updatable result sets + + return rs; + } + } + + public void redefineFieldsForDBMD(Field[] f) { + this.fields = f; + + for (int i = 0; i < this.fields.length; i++) { + this.fields[i].setUseOldNameMetadata(true); + this.fields[i].setConnection(this.connection); + } + } + + public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData) throws SQLException { + cachedMetaData.fields = this.fields; + cachedMetaData.columnNameToIndex = this.columnLabelToIndex; + cachedMetaData.fullColumnNameToIndex = this.fullColumnNameToIndex; + cachedMetaData.metadata = getMetaData(); + } + + public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData) { + this.fields = cachedMetaData.fields; + this.columnLabelToIndex = cachedMetaData.columnNameToIndex; + this.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex; + this.hasBuiltIndexMapping = true; + } + + /** + * JDBC 2.0 Delete the current row from the result set and the underlying + * database. Cannot be called when on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws NotUpdatable + */ + public void deleteRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * @param columnIndex + * @param stringVal + * @param mysqlType + * @throws SQLException + */ + private String extractStringFromNativeColumn(int columnIndex, int mysqlType) throws SQLException { + int columnIndexMinusOne = columnIndex - 1; + + this.wasNullFlag = false; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + String encoding = this.fields[columnIndexMinusOne].getEncoding(); + + return this.thisRow.getString(columnIndex - 1, encoding, this.connection); + } + + protected Date fastDateCreate(Calendar cal, int year, int month, int day) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Calendar targetCalendar = cal; + + if (cal == null) { + if (this.connection.getNoTimezoneConversionForDateType()) { + targetCalendar = getFastClientCalendar(); + } else { + targetCalendar = getFastDefaultCalendar(); + } + } + + if (!this.useLegacyDatetimeCode) { + return TimeUtil.fastDateCreate(year, month, day, targetCalendar); + } + + boolean useGmtMillis = cal == null && !this.connection.getNoTimezoneConversionForDateType() && this.connection.getUseGmtMillisForDatetimes(); + + return TimeUtil.fastDateCreate(useGmtMillis, useGmtMillis ? getGmtCalendar() : targetCalendar, targetCalendar, year, month, day); + } + } + + protected Time fastTimeCreate(Calendar cal, int hour, int minute, int second) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.useLegacyDatetimeCode) { + return TimeUtil.fastTimeCreate(hour, minute, second, cal, getExceptionInterceptor()); + } + + if (cal == null) { + cal = getFastDefaultCalendar(); + } + + return TimeUtil.fastTimeCreate(cal, hour, minute, second, getExceptionInterceptor()); + } + } + + protected Timestamp fastTimestampCreate(Calendar cal, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) + throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(cal.getTimeZone(), year, month, day, hour, minute, seconds, secondsPart); + } + + if (cal == null) { + cal = getFastDefaultCalendar(); + } + + boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes(); + + return TimeUtil.fastTimestampCreate(useGmtMillis, useGmtMillis ? getGmtCalendar() : null, cal, year, month, day, hour, minute, seconds, + secondsPart); + } + } + + /* + * /** + * Required by JDBC spec + */ + /* + * protected void finalize() throws Throwable { + * if (!this.isClosed) { + * realClose(false); + * } + * } + */ + + // --------------------------JDBC 2.0----------------------------------- + // --------------------------------------------------------------------- + // Getter's and Setter's + // --------------------------------------------------------------------- + + /* + * [For JDBC-3.0 and older - http://java.sun.com/j2se/1.5.0/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)] + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName + * the name of the column + * + * @return the column index + * + * @exception SQLException + * if a database access error occurs + * + * [For JDBC-4.0 and newer - http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#findColumn(java.lang.String)] + * + * Maps the given ResultSet column label to its ResultSet column index. + * + * @param columnLabel + * the label for the column specified with the SQL AS clause. If the + * SQL AS clause was not specified, then the label is the name of the column + * + * @return the column index of the given column name + */ + public int findColumn(String columnName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Integer index; + + if (!this.hasBuiltIndexMapping) { + buildIndexMapping(); + } + + index = this.columnToIndexCache.get(columnName); + + if (index != null) { + return index.intValue() + 1; + } + + index = this.columnLabelToIndex.get(columnName); + + if (index == null && this.useColumnNamesInFindColumn) { + index = this.columnNameToIndex.get(columnName); + } + + if (index == null) { + index = this.fullColumnNameToIndex.get(columnName); + } + + if (index != null) { + this.columnToIndexCache.put(columnName, index); + + return index.intValue() + 1; + } + + // Try this inefficient way, now + + for (int i = 0; i < this.fields.length; i++) { + if (this.fields[i].getName().equalsIgnoreCase(columnName)) { + return i + 1; + } else if (this.fields[i].getFullName().equalsIgnoreCase(columnName)) { + return i + 1; + } + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Column____112") + columnName + Messages.getString("ResultSet.___not_found._113"), + SQLError.SQL_STATE_COLUMN_NOT_FOUND, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the first row in the result set. + *

    + * + * @return true if on a valid row, false if no rows in the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + public boolean first() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + boolean b = true; + + if (this.rowData.isEmpty()) { + b = false; + } else { + + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + this.rowData.beforeFirst(); + this.thisRow = this.rowData.next(); + } + + setRowPositionValidity(); + + return b; + } + } + + /** + * JDBC 2.0 Get an array column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing an SQL array + * + * @throws SQLException + * if a database error occurs + * @throws NotImplemented + */ + public java.sql.Array getArray(int i) throws SQLException { + checkColumnBounds(i); + + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Get an array column. + * + * @param colName + * the column name + * + * @return an object representing an SQL array + * + * @throws SQLException + * if a database error occurs + * @throws NotImplemented + */ + public java.sql.Array getArray(String colName) throws SQLException { + return getArray(findColumn(colName)); + } + + /** + * A column value can be retrieved as a stream of ASCII characters and then + * read in chunks from the stream. This method is particulary suitable for + * retrieving large LONGVARCHAR values. The JDBC driver will do any + * necessary conversion from the database format into ASCII. + * + *

    + * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a get method implicitly + * closes the stream. Also, a stream may return 0 for available() whether there is data available or not. + *

    + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return a Java InputStream that delivers the database column value as a + * stream of one byte ASCII characters. If the value is SQL NULL + * then the result is null + * + * @exception SQLException + * if a database access error occurs + * + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException { + checkRowPos(); + + if (!this.isBinaryEncoded) { + return getBinaryStream(columnIndex); + } + + return getNativeBinaryStream(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public InputStream getAsciiStream(String columnName) throws SQLException { + return getAsciiStream(findColumn(columnName)); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a + * java.math.BigDecimal object. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return the column value (full precision); if the value is SQL NULL, the + * result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + String stringVal = getString(columnIndex); + BigDecimal val; + + if (stringVal != null) { + if (stringVal.length() == 0) { + + val = new BigDecimal(convertToZeroLiteralStringWithEmptyCheck()); + + return val; + } + + try { + val = new BigDecimal(stringVal); + + return val; + } catch (NumberFormatException ex) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return null; + } + + return getNativeBigDecimal(columnIndex); + } + + /** + * Get the value of a column in the current row as a java.math.BigDecimal + * object + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param scale + * the number of digits to the right of the decimal + * + * @return the column value; if the value is SQL NULL, null + * + * @exception SQLException + * if a database access error occurs + * + * @deprecated + */ + @Deprecated + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + if (!this.isBinaryEncoded) { + String stringVal = getString(columnIndex); + BigDecimal val; + + if (stringVal != null) { + if (stringVal.length() == 0) { + val = new BigDecimal(convertToZeroLiteralStringWithEmptyCheck()); + + try { + return val.setScale(scale); + } catch (ArithmeticException ex) { + try { + return val.setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + try { + val = new BigDecimal(stringVal); + } catch (NumberFormatException ex) { + if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex); + + val = new BigDecimal(valueAsLong); + } else { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { Integer.valueOf(columnIndex), stringVal }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + try { + return val.setScale(scale); + } catch (ArithmeticException ex) { + try { + return val.setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arithEx) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { Integer.valueOf(columnIndex), stringVal }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + return null; + } + + return getNativeBigDecimal(columnIndex, scale); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a + * java.math.BigDecimal object. + * + * @param columnName + * the name of the column to retrieve the value from + * + * @return the BigDecimal value in the column + * + * @throws SQLException + * if an error occurs + */ + public BigDecimal getBigDecimal(String columnName) throws SQLException { + return getBigDecimal(findColumn(columnName)); + } + + /** + * @param columnName + * @param scale + * + * @throws SQLException + * + * @deprecated + */ + @Deprecated + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { + return getBigDecimal(findColumn(columnName), scale); + } + + private final BigDecimal getBigDecimalFromString(String stringVal, int columnIndex, int scale) throws SQLException { + BigDecimal bdVal; + + if (stringVal != null) { + if (stringVal.length() == 0) { + bdVal = new BigDecimal(convertToZeroLiteralStringWithEmptyCheck()); + + try { + return bdVal.setScale(scale); + } catch (ArithmeticException ex) { + try { + return bdVal.setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx) { + throw new SQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + } + + try { + try { + return new BigDecimal(stringVal).setScale(scale); + } catch (ArithmeticException ex) { + try { + return new BigDecimal(stringVal).setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx) { + throw new SQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + } catch (NumberFormatException ex) { + if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex); + + try { + return new BigDecimal(valueAsLong).setScale(scale); + } catch (ArithmeticException arEx1) { + try { + return new BigDecimal(valueAsLong).setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx2) { + throw new SQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + } + + if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TINY && this.connection.getTinyInt1isBit() + && this.fields[columnIndex - 1].getLength() == 1) { + return new BigDecimal(stringVal.equalsIgnoreCase("true") ? 1 : 0).setScale(scale); + } + + throw new SQLException(Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + + return null; + } + + /** + * A column value can also be retrieved as a binary stream. This method is + * suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Java InputStream that delivers the database column value as a + * stream of bytes. If the value is SQL NULL, then the result is + * null + * + * @exception SQLException + * if a database access error occurs + * + * @see getAsciiStream + * @see getUnicodeStream + */ + public InputStream getBinaryStream(int columnIndex) throws SQLException { + checkRowPos(); + + if (!this.isBinaryEncoded) { + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return this.thisRow.getBinaryInputStream(columnIndexMinusOne); + } + + return getNativeBinaryStream(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public InputStream getBinaryStream(String columnName) throws SQLException { + return getBinaryStream(findColumn(columnName)); + } + + /** + * JDBC 2.0 Get a BLOB column. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return an object representing a BLOB + * + * @throws SQLException + * if an error occurs. + */ + public java.sql.Blob getBlob(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + checkRowPos(); + + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + if (!this.connection.getEmulateLocators()) { + return new Blob(this.thisRow.getColumnValue(columnIndexMinusOne), getExceptionInterceptor()); + } + + return new BlobFromLocator(this, columnIndex, getExceptionInterceptor()); + } + + return getNativeBlob(columnIndex); + } + + /** + * JDBC 2.0 Get a BLOB column. + * + * @param colName + * the column name + * + * @return an object representing a BLOB + * + * @throws SQLException + * if an error occurs. + */ + public java.sql.Blob getBlob(String colName) throws SQLException { + return getBlob(findColumn(colName)); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value, false for SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException { + + checkColumnBounds(columnIndex); + + // + // MySQL 5.0 and newer have an actual BIT type, so we need to check for that here... + // + + int columnIndexMinusOne = columnIndex - 1; + + Field field = this.fields[columnIndexMinusOne]; + + if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + return byteArrayToBoolean(columnIndexMinusOne); + } + + this.wasNullFlag = false; + + int sqlType = field.getSQLType(); + + switch (sqlType) { + case Types.BOOLEAN: + if (field.getMysqlType() == -1) { // from dbmd + String stringVal = getString(columnIndex); + + return getBooleanFromString(stringVal); + } + + long boolVal = getLong(columnIndex, false); + + return (boolVal == -1 || boolVal > 0); + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.DECIMAL: + case Types.NUMERIC: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + boolVal = getLong(columnIndex, false); + + return (boolVal == -1 || boolVal > 0); + default: + if (this.connection.getPedantic()) { + // Table B-6 from JDBC spec + switch (sqlType) { + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + case Types.CLOB: + case Types.BLOB: + case Types.ARRAY: + case Types.REF: + case Types.DATALINK: + case Types.STRUCT: + case Types.JAVA_OBJECT: + throw SQLError.createSQLException("Required type conversion not allowed", SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, + getExceptionInterceptor()); + } + } + + if (sqlType == Types.BINARY || sqlType == Types.VARBINARY || sqlType == Types.LONGVARBINARY || sqlType == Types.BLOB) { + return byteArrayToBoolean(columnIndexMinusOne); + } + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getBoolean()", columnIndex, this.thisRow.getColumnValue(columnIndexMinusOne), this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_BIT, MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, + MysqlDefs.FIELD_TYPE_LONG, MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + String stringVal = getString(columnIndex); + + return getBooleanFromString(stringVal); + } + } + + private boolean byteArrayToBoolean(int columnIndexMinusOne) throws SQLException { + Object value = this.thisRow.getColumnValue(columnIndexMinusOne); + + if (value == null) { + this.wasNullFlag = true; + + return false; + } + + this.wasNullFlag = false; + + if (((byte[]) value).length == 0) { + return false; + } + + byte boolVal = ((byte[]) value)[0]; + + if (boolVal == (byte) '1') { + return true; + } else if (boolVal == (byte) '0') { + return false; + } + + return (boolVal == -1 || boolVal > 0); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public boolean getBoolean(String columnName) throws SQLException { + return getBoolean(findColumn(columnName)); + } + + private final boolean getBooleanFromString(String stringVal) throws SQLException { + if ((stringVal != null) && (stringVal.length() > 0)) { + int c = Character.toLowerCase(stringVal.charAt(0)); + + return ((c == 't') || (c == 'y') || (c == '1') || stringVal.equals("-1")); + } + + return false; + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + String stringVal = getString(columnIndex); + + if (this.wasNullFlag || (stringVal == null)) { + return 0; + } + + return getByteFromString(stringVal, columnIndex); + } + + return getNativeByte(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public byte getByte(String columnName) throws SQLException { + return getByte(findColumn(columnName)); + } + + private final byte getByteFromString(String stringVal, int columnIndex) throws SQLException { + + if (stringVal != null && stringVal.length() == 0) { + return (byte) convertToZeroWithEmptyCheck(); + } + + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace is + // present + // + + if (stringVal == null) { + return 0; + } + + stringVal = stringVal.trim(); + + try { + int decimalIndex = stringVal.indexOf("."); + + if (decimalIndex != -1) { + double valueAsDouble = Double.parseDouble(stringVal); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Byte.MIN_VALUE || valueAsDouble > Byte.MAX_VALUE) { + throwRangeException(stringVal, columnIndex, Types.TINYINT); + } + } + + return (byte) valueAsDouble; + } + + long valueAsLong = Long.parseLong(stringVal); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsLong < Byte.MIN_VALUE || valueAsLong > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex, Types.TINYINT); + } + } + + return (byte) valueAsLong; + } catch (NumberFormatException NFE) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Value____173") + stringVal + Messages.getString("ResultSet.___is_out_of_range_[-127,127]_174"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

    + * Be warned If the blob is huge, then you may run out of memory. + *

    + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException { + return getBytes(columnIndex, false); + } + + protected byte[] getBytes(int columnIndex, boolean noConversion) throws SQLException { + if (!this.isBinaryEncoded) { + checkRowPos(); + + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + return this.thisRow.getColumnValue(columnIndexMinusOne); + } + + return getNativeBytes(columnIndex, noConversion); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public byte[] getBytes(String columnName) throws SQLException { + return getBytes(findColumn(columnName)); + } + + private final byte[] getBytesFromString(String stringVal) throws SQLException { + if (stringVal != null) { + return StringUtils.getBytes(stringVal, this.connection.getEncoding(), this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), + this.connection, getExceptionInterceptor()); + } + + return null; + } + + public int getBytesSize() throws SQLException { + RowData localRowData = this.rowData; + + checkClosed(); + + if (localRowData instanceof RowDataStatic) { + int bytesSize = 0; + + int numRows = localRowData.size(); + + for (int i = 0; i < numRows; i++) { + bytesSize += localRowData.getAt(i).getBytesSize(); + } + + return bytesSize; + } + + return -1; + } + + /** + * Optimization to only use one calendar per-session, or calculate it for + * each call, depending on user configuration + */ + protected Calendar getCalendarInstanceForSessionOrNew() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection != null) { + return this.connection.getCalendarInstanceForSessionOrNew(); + } + + // punt, no connection around + return new GregorianCalendar(); + } + } + + /** + * JDBC 2.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnIndex + * the column to get the value from + * + * @return the value in the column as a java.io.Reader. + * + * @throws SQLException + * if an error occurs + */ + public java.io.Reader getCharacterStream(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return this.thisRow.getReader(columnIndexMinusOne); + } + + return getNativeCharacterStream(columnIndex); + } + + /** + * JDBC 2.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnName + * the column name to retrieve the value from + * + * @return the value as a java.io.Reader + * + * @throws SQLException + * if an error occurs + */ + public java.io.Reader getCharacterStream(String columnName) throws SQLException { + return getCharacterStream(findColumn(columnName)); + } + + private final java.io.Reader getCharacterStreamFromString(String stringVal) throws SQLException { + if (stringVal != null) { + return new StringReader(stringVal); + } + + return null; + } + + /** + * JDBC 2.0 Get a CLOB column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing a CLOB + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Clob getClob(int i) throws SQLException { + if (!this.isBinaryEncoded) { + String asString = getStringForClob(i); + + if (asString == null) { + return null; + } + + return new com.mysql.jdbc.Clob(asString, getExceptionInterceptor()); + } + + return getNativeClob(i); + } + + /** + * JDBC 2.0 Get a CLOB column. + * + * @param colName + * the column name + * + * @return an object representing a CLOB + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Clob getClob(String colName) throws SQLException { + return getClob(findColumn(colName)); + } + + private final java.sql.Clob getClobFromString(String stringVal) throws SQLException { + return new com.mysql.jdbc.Clob(stringVal, getExceptionInterceptor()); + } + + /** + * JDBC 2.0 Return the concurrency of this result set. The concurrency used + * is determined by the statement that created the result set. + * + * @return the concurrency type, CONCUR_READ_ONLY, etc. + * + * @throws SQLException + * if a database-access error occurs + */ + public int getConcurrency() throws SQLException { + return (CONCUR_READ_ONLY); + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + *

    + * In SQL, a result table is retrieved though a cursor that is named. The current row of a result can be updated or deleted using a positioned update/delete + * statement that references the cursor name. + *

    + * + *

    + * JDBC supports this SQL feature by providing the name of the SQL cursor used by a ResultSet. The current row of a ResulSet is also the current row of this + * SQL cursor. + *

    + * + *

    + * Note: If positioned update is not supported, a SQLException is thrown. + *

    + * + * @return the ResultSet's SQL cursor name. + * + * @exception SQLException + * if a database access error occurs + */ + public String getCursorName() throws SQLException { + throw SQLError.createSQLException(Messages.getString("ResultSet.Positioned_Update_not_supported"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, + getExceptionInterceptor()); + } + + /** + * Get the value of a column in the current row as a java.sql.Date object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws java.sql.SQLException { + return getDate(columnIndex, null); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date + * object. Use the calendar to construct an appropriate millisecond value + * for the Date, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the calendar to use in constructing the date + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Date getDate(int columnIndex, Calendar cal) throws SQLException { + if (this.isBinaryEncoded) { + return getNativeDate(columnIndex, cal); + } + + if (!this.useFastDateParsing) { + String stringVal = getStringInternal(columnIndex, false); + + if (stringVal == null) { + return null; + } + + return getDateFromString(stringVal, columnIndex, cal); + } + + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + Date tmpDate = this.thisRow.getDateFast(columnIndexMinusOne, this.connection, this, cal); + if ((this.thisRow.isNull(columnIndexMinusOne)) || (tmpDate == null)) { + + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return tmpDate; + } + + /** + * @param columnName + * + * @throws java.sql.SQLException + */ + public java.sql.Date getDate(String columnName) throws java.sql.SQLException { + return getDate(findColumn(columnName)); + } + + /** + * Get the value of a column in the current row as a java.sql.Date object. + * Use the calendar to construct an appropriate millisecond value for the + * Date, if the underlying database doesn't store timezone information. + * + * @param columnName + * is the SQL name of the column + * @param cal + * the calendar to use in constructing the date + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException { + return getDate(findColumn(columnName), cal); + } + + private final java.sql.Date getDateFromString(String stringVal, int columnIndex, Calendar targetCalendar) throws SQLException { + int year = 0; + int month = 0; + int day = 0; + + try { + this.wasNullFlag = false; + + if (stringVal == null) { + this.wasNullFlag = true; + + return null; + } + + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace is + // present + // + + stringVal = stringVal.trim(); + + // truncate fractional part + int dec = stringVal.indexOf("."); + if (dec > -1) { + stringVal = stringVal.substring(0, dec); + } + + if (stringVal.equals("0") || stringVal.equals("0000-00-00") || stringVal.equals("0000-00-00 00:00:00") || stringVal.equals("00000000000000") + || stringVal.equals("0")) { + + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(this.connection.getZeroDateTimeBehavior())) { + this.wasNullFlag = true; + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(this.connection.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + stringVal + "' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + // We're left with the case of 'round' to a date Java _can_ represent, which is '0001-01-01'. + return fastDateCreate(targetCalendar, 1, 1, 1); + + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + // Convert from TIMESTAMP + switch (stringVal.length()) { + case 21: + case 19: { // java.sql.Timestamp format + year = Integer.parseInt(stringVal.substring(0, 4)); + month = Integer.parseInt(stringVal.substring(5, 7)); + day = Integer.parseInt(stringVal.substring(8, 10)); + + return fastDateCreate(targetCalendar, year, month, day); + } + + case 14: + case 8: { + year = Integer.parseInt(stringVal.substring(0, 4)); + month = Integer.parseInt(stringVal.substring(4, 6)); + day = Integer.parseInt(stringVal.substring(6, 8)); + + return fastDateCreate(targetCalendar, year, month, day); + } + + case 12: + case 10: + case 6: { + year = Integer.parseInt(stringVal.substring(0, 2)); + + if (year <= 69) { + year = year + 100; + } + + month = Integer.parseInt(stringVal.substring(2, 4)); + day = Integer.parseInt(stringVal.substring(4, 6)); + + return fastDateCreate(targetCalendar, year + 1900, month, day); + } + + case 4: { + year = Integer.parseInt(stringVal.substring(0, 4)); + + if (year <= 69) { + year = year + 100; + } + + month = Integer.parseInt(stringVal.substring(2, 4)); + + return fastDateCreate(targetCalendar, year + 1900, month, 1); + } + + case 2: { + year = Integer.parseInt(stringVal.substring(0, 2)); + + if (year <= 69) { + year = year + 100; + } + + return fastDateCreate(targetCalendar, year + 1900, 1, 1); + } + + default: + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + if (stringVal.length() == 2 || stringVal.length() == 1) { + year = Integer.parseInt(stringVal); + + if (year <= 69) { + year = year + 100; + } + + year += 1900; + } else { + year = Integer.parseInt(stringVal.substring(0, 4)); + } + + return fastDateCreate(targetCalendar, year, 1, 1); + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) { + return fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH + } else { + if (stringVal.length() < 10) { + if (stringVal.length() == 8) { + return fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH for TIME + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (stringVal.length() != 18) { + year = Integer.parseInt(stringVal.substring(0, 4)); + month = Integer.parseInt(stringVal.substring(5, 7)); + day = Integer.parseInt(stringVal.substring(8, 10)); + } else { + // JDK-1.3 timestamp format, not real easy to parse positionally :p + StringTokenizer st = new StringTokenizer(stringVal, "- "); + + year = Integer.parseInt(st.nextToken()); + month = Integer.parseInt(st.nextToken()); + day = Integer.parseInt(st.nextToken()); + } + } + + return fastDateCreate(targetCalendar, year, month, day); + } catch (SQLException sqlEx) { + throw sqlEx; // don't re-wrap + } catch (Exception e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + + sqlEx.initCause(e); + + throw sqlEx; + } + } + + private TimeZone getDefaultTimeZone() { + return this.useLegacyDatetimeCode ? this.connection.getDefaultTimeZone() : this.serverTimeZoneTz; + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + return getDoubleInternal(columnIndex); + } + + return getNativeDouble(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public double getDouble(String columnName) throws SQLException { + return getDouble(findColumn(columnName)); + } + + private final double getDoubleFromString(String stringVal, int columnIndex) throws SQLException { + return getDoubleInternal(stringVal, columnIndex); + } + + /** + * Converts a string representation of a number to a double. Need a faster + * way to do this. + * + * @param colIndex + * the 1-based index of the column to retrieve a double from. + * + * @return the double value represented by the string in buf + * + * @throws SQLException + * if an error occurs + */ + protected double getDoubleInternal(int colIndex) throws SQLException { + return getDoubleInternal(getString(colIndex), colIndex); + } + + /** + * Converts a string representation of a number to a double. Need a faster + * way to do this. + * + * @param stringVal + * the double as a String + * @param colIndex + * the 1-based index of the column to retrieve a double from. + * + * @return the double value represented by the string in buf + * + * @throws SQLException + * if an error occurs + */ + protected double getDoubleInternal(String stringVal, int colIndex) throws SQLException { + try { + if ((stringVal == null)) { + return 0; + } + + if (stringVal.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + double d = Double.parseDouble(stringVal); + + if (this.useStrictFloatingPoint) { + // Fix endpoint rounding precision loss in MySQL server + if (d == 2.147483648E9) { + // Fix Odd end-point rounding on MySQL + d = 2.147483647E9; + } else if (d == 1.0000000036275E-15) { + // Fix odd end-point rounding on MySQL + d = 1.0E-15; + } else if (d == 9.999999869911E14) { + d = 9.99999999999999E14; + } else if (d == 1.4012984643248E-45) { + d = 1.4E-45; + } else if (d == 1.4013E-45) { + d = 1.4E-45; + } else if (d == 3.4028234663853E37) { + d = 3.4028235E37; + } else if (d == -2.14748E9) { + d = -2.147483648E9; + } else if (d == 3.40282E37) { + d = 3.4028235E37; + } + } + + return d; + } catch (NumberFormatException e) { + if (this.fields[colIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(colIndex); + + return valueAsLong; + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Bad_format_for_number", new Object[] { stringVal, Integer.valueOf(colIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Returns the fetch direction for this result set. + * + * @return the fetch direction for this result set. + * + * @exception SQLException + * if a database-access error occurs + */ + public int getFetchDirection() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.fetchDirection; + } + } + + /** + * JDBC 2.0 Return the fetch size for this result set. + * + * @return the fetch size for this result set. + * + * @exception SQLException + * if a database-access error occurs + */ + public int getFetchSize() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.fetchSize; + } + } + + /** + * Returns the first character of the query that this result set was created + * from. + * + * @return the first character of the query...uppercased + */ + public char getFirstCharOfQuery() { + try { + synchronized (checkClosed().getConnectionMutex()) { + return this.firstCharOfQuery; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve interface + } + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + String val = null; + + val = getString(columnIndex); + + return getFloatFromString(val, columnIndex); + } + + return getNativeFloat(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public float getFloat(String columnName) throws SQLException { + return getFloat(findColumn(columnName)); + } + + private final float getFloatFromString(String val, int columnIndex) throws SQLException { + try { + if ((val != null)) { + if (val.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + float f = Float.parseFloat(val); + + if (this.jdbcCompliantTruncationForReads) { + if (f == Float.MIN_VALUE || f == Float.MAX_VALUE) { + double valAsDouble = Double.parseDouble(val); + + // Straight comparison is not reliable when at absolute endpoints of Float.MIN_VALUE or Float.MAX_VALUE, so use epsillons with DOUBLEs + + if ((valAsDouble < Float.MIN_VALUE - MIN_DIFF_PREC) || (valAsDouble > Float.MAX_VALUE - MAX_DIFF_PREC)) { + throwRangeException(String.valueOf(valAsDouble), columnIndex, Types.FLOAT); + } + } + } + + return f; + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + Double valueAsDouble = new Double(val); + float valueAsFloat = valueAsDouble.floatValue(); + + if (this.jdbcCompliantTruncationForReads) { + + if (this.jdbcCompliantTruncationForReads && valueAsFloat == Float.NEGATIVE_INFINITY || valueAsFloat == Float.POSITIVE_INFINITY) { + throwRangeException(valueAsDouble.toString(), columnIndex, Types.FLOAT); + } + } + + return valueAsFloat; + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getFloat()_-____200") + val + + Messages.getString("ResultSet.___in_column__201") + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (!this.isBinaryEncoded) { + int columnIndexMinusOne = columnIndex - 1; + + if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex); + + if (this.jdbcCompliantTruncationForReads && (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) { + throwRangeException(String.valueOf(valueAsLong), columnIndex, Types.INTEGER); + } + + return (int) valueAsLong; + } + + if (this.useFastIntParsing) { + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return 0; + } + + if (this.thisRow.length(columnIndexMinusOne) == 0) { + return convertToZeroWithEmptyCheck(); + } + + boolean needsFullParse = this.thisRow.isFloatingPointNumber(columnIndexMinusOne); + + if (!needsFullParse) { + try { + return getIntWithOverflowCheck(columnIndexMinusOne); + } catch (NumberFormatException nfe) { + try { + return parseIntAsDouble(columnIndex, + this.thisRow.getString(columnIndexMinusOne, this.fields[columnIndexMinusOne].getEncoding(), this.connection)); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Invalid_value_for_getInt()_-____74") + + this.thisRow.getString(columnIndexMinusOne, this.fields[columnIndexMinusOne].getEncoding(), this.connection) + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + String val = null; + + try { + val = getString(columnIndex); + + if ((val != null)) { + if (val.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { + int intVal = Integer.parseInt(val); + + checkForIntegerTruncation(columnIndexMinusOne, null, intVal); + + return intVal; + } + + // Convert floating point + int intVal = parseIntAsDouble(columnIndex, val); + + checkForIntegerTruncation(columnIndex, null, intVal); + + return intVal; + } + + return 0; + } catch (NumberFormatException nfe) { + try { + return parseIntAsDouble(columnIndex, val); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getInt()_-____74") + val + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return getNativeInt(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public int getInt(String columnName) throws SQLException { + return getInt(findColumn(columnName)); + } + + private final int getIntFromString(String val, int columnIndex) throws SQLException { + try { + if ((val != null)) { + + if (val.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if + // whitespace is present + // + + val = val.trim(); + + int valueAsInt = Integer.parseInt(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsInt == Integer.MIN_VALUE || valueAsInt == Integer.MAX_VALUE) { + long valueAsLong = Long.parseLong(val); + + if (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex, Types.INTEGER); + } + } + } + + return valueAsInt; + } + + // Convert floating point + + double valueAsDouble = Double.parseDouble(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Integer.MIN_VALUE || valueAsDouble > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex, Types.INTEGER); + } + } + + return (int) valueAsDouble; + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + double valueAsDouble = Double.parseDouble(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Integer.MIN_VALUE || valueAsDouble > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex, Types.INTEGER); + } + } + + return (int) valueAsDouble; + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Invalid_value_for_getInt()_-____206") + val + Messages.getString("ResultSet.___in_column__207") + columnIndex, + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException { + return getLong(columnIndex, true); + } + + private long getLong(int columnIndex, boolean overflowCheck) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (!this.isBinaryEncoded) { + int columnIndexMinusOne = columnIndex - 1; + + if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + return getNumericRepresentationOfSQLBitType(columnIndex); + } + + if (this.useFastIntParsing) { + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return 0; + } + + if (this.thisRow.length(columnIndexMinusOne) == 0) { + return convertToZeroWithEmptyCheck(); + } + + boolean needsFullParse = this.thisRow.isFloatingPointNumber(columnIndexMinusOne); + + if (!needsFullParse) { + try { + return getLongWithOverflowCheck(columnIndexMinusOne, overflowCheck); + } catch (NumberFormatException nfe) { + try { + return parseLongAsDouble(columnIndexMinusOne, + this.thisRow.getString(columnIndexMinusOne, this.fields[columnIndexMinusOne].getEncoding(), this.connection)); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Invalid_value_for_getLong()_-____79") + + this.thisRow.getString(columnIndexMinusOne, this.fields[columnIndexMinusOne].getEncoding(), this.connection) + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + String val = null; + + try { + val = getString(columnIndex); + + if ((val != null)) { + if (val.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) { + return parseLongWithOverflowCheck(columnIndexMinusOne, null, val, overflowCheck); + } + + // Convert floating point + return parseLongAsDouble(columnIndexMinusOne, val); + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + return parseLongAsDouble(columnIndexMinusOne, val); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getLong()_-____79") + val + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return getNativeLong(columnIndex, overflowCheck, true); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public long getLong(String columnName) throws SQLException { + return getLong(findColumn(columnName)); + } + + private final long getLongFromString(String val, int columnIndexZeroBased) throws SQLException { + try { + if ((val != null)) { + + if (val.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) { + return parseLongWithOverflowCheck(columnIndexZeroBased, null, val, true); + } + + // Convert floating point + return parseLongAsDouble(columnIndexZeroBased, val); + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + // To do: Warn of over/underflow??? + return parseLongAsDouble(columnIndexZeroBased, val); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getLong()_-____211") + val + + Messages.getString("ResultSet.___in_column__212") + (columnIndexZeroBased + 1), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + /** + * The numbers, types and properties of a ResultSet's columns are provided + * by the getMetaData method + * + * @return a description of the ResultSet's columns + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException { + checkClosed(); + + return new com.mysql.jdbc.ResultSetMetaData(this.fields, this.connection.getUseOldAliasMetadataBehavior(), this.connection.getYearIsDateType(), + getExceptionInterceptor()); + } + + /** + * JDBC 2.0 Get an array column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing an SQL array + * + * @throws SQLException + * if a database error occurs + * @throws NotImplemented + */ + protected java.sql.Array getNativeArray(int i) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * A column value can be retrieved as a stream of ASCII characters and then + * read in chunks from the stream. This method is particulary suitable for + * retrieving large LONGVARCHAR values. The JDBC driver will do any + * necessary conversion from the database format into ASCII. + * + *

    + * Note: All the data in the returned stream must be read prior to getting the value of any other column. The next call to a get method implicitly + * closes the stream. Also, a stream may return 0 for available() whether there is data available or not. + *

    + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return a Java InputStream that delivers the database column value as a + * stream of one byte ASCII characters. If the value is SQL NULL + * then the result is null + * + * @exception SQLException + * if a database access error occurs + * + * @see getBinaryStream + */ + protected InputStream getNativeAsciiStream(int columnIndex) throws SQLException { + checkRowPos(); + + return getNativeBinaryStream(columnIndex); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a + * java.math.BigDecimal object. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return the column value (full precision); if the value is SQL NULL, the + * result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + protected BigDecimal getNativeBigDecimal(int columnIndex) throws SQLException { + + checkColumnBounds(columnIndex); + + int scale = this.fields[columnIndex - 1].getDecimals(); + + return getNativeBigDecimal(columnIndex, scale); + } + + /** + * Get the value of a column in the current row as a java.math.BigDecimal + * object + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param scale + * the number of digits to the right of the decimal + * + * @return the column value; if the value is SQL NULL, null + * + * @exception SQLException + * if a database access error occurs + */ + protected BigDecimal getNativeBigDecimal(int columnIndex, int scale) throws SQLException { + checkColumnBounds(columnIndex); + + String stringVal = null; + + Field f = this.fields[columnIndex - 1]; + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + switch (f.getSQLType()) { + case Types.DECIMAL: + case Types.NUMERIC: + stringVal = StringUtils.toAsciiString((byte[]) value); + break; + default: + stringVal = getNativeString(columnIndex); + } + + return getBigDecimalFromString(stringVal, columnIndex, scale); + } + + /** + * A column value can also be retrieved as a binary stream. This method is + * suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Java InputStream that delivers the database column value as a + * stream of bytes. If the value is SQL NULL, then the result is + * null + * + * @exception SQLException + * if a database access error occurs + * + * @see getAsciiStream + * @see getUnicodeStream + */ + protected InputStream getNativeBinaryStream(int columnIndex) throws SQLException { + checkRowPos(); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + switch (this.fields[columnIndexMinusOne].getSQLType()) { + case Types.BIT: + case Types.BINARY: + case Types.VARBINARY: + case Types.BLOB: + case Types.LONGVARBINARY: + return this.thisRow.getBinaryInputStream(columnIndexMinusOne); + } + + byte[] b = getNativeBytes(columnIndex, false); + + if (b != null) { + return new ByteArrayInputStream(b); + } + + return null; + } + + /** + * JDBC 2.0 Get a BLOB column. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return an object representing a BLOB + * + * @throws SQLException + * if an error occurs. + */ + protected java.sql.Blob getNativeBlob(int columnIndex) throws SQLException { + checkRowPos(); + + checkColumnBounds(columnIndex); + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + int mysqlType = this.fields[columnIndex - 1].getMysqlType(); + + byte[] dataAsBytes = null; + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + dataAsBytes = (byte[]) value; + break; + + default: + dataAsBytes = getNativeBytes(columnIndex, false); + } + + if (!this.connection.getEmulateLocators()) { + return new Blob(dataAsBytes, getExceptionInterceptor()); + } + + return new BlobFromLocator(this, columnIndex, getExceptionInterceptor()); + } + + public static boolean arraysEqual(byte[] left, byte[] right) { + if (left == null) { + return right == null; + } + if (right == null) { + return false; + } + if (left.length != right.length) { + return false; + } + for (int i = 0; i < left.length; i++) { + if (left[i] != right[i]) { + return false; + } + } + return true; + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected byte getNativeByte(int columnIndex) throws SQLException { + return getNativeByte(columnIndex, true); + } + + protected byte getNativeByte(int columnIndex, boolean overflowCheck) throws SQLException { + checkRowPos(); + + checkColumnBounds(columnIndex); + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + columnIndex--; + + Field field = this.fields[columnIndex]; + + switch (field.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && (valueAsLong < Byte.MIN_VALUE || valueAsLong > Byte.MAX_VALUE)) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.TINYINT); + } + + return (byte) valueAsLong; + case MysqlDefs.FIELD_TYPE_TINY: + byte valueAsByte = ((byte[]) value)[0]; + + if (!field.isUnsigned()) { + return valueAsByte; + } + + short valueAsShort = (valueAsByte >= 0) ? valueAsByte : (short) (valueAsByte + (short) 256); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsShort > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsShort), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsShort; + + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + valueAsShort = getNativeShort(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsShort < Byte.MIN_VALUE || valueAsShort > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsShort), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsShort; + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + int valueAsInt = getNativeInt(columnIndex + 1, false); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsInt < Byte.MIN_VALUE || valueAsInt > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsInt), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsInt; + + case MysqlDefs.FIELD_TYPE_FLOAT: + float valueAsFloat = getNativeFloat(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsFloat < Byte.MIN_VALUE || valueAsFloat > Byte.MAX_VALUE) { + + throwRangeException(String.valueOf(valueAsFloat), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsFloat; + + case MysqlDefs.FIELD_TYPE_DOUBLE: + double valueAsDouble = getNativeDouble(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Byte.MIN_VALUE || valueAsDouble > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsDouble; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + valueAsLong = getNativeLong(columnIndex + 1, false, true); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsLong < Byte.MIN_VALUE || valueAsLong > Byte.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.TINYINT); + } + } + + return (byte) valueAsLong; + + default: + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getByte()", columnIndex, this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getByteFromString(getNativeString(columnIndex + 1), columnIndex + 1); + } + } + + /** + * Get the value of a column in the current row as a Java byte array. + * + *

    + * Be warned If the blob is huge, then you may run out of memory. + *

    + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database access error occurs + */ + protected byte[] getNativeBytes(int columnIndex, boolean noConversion) throws SQLException { + checkRowPos(); + + checkColumnBounds(columnIndex); + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + Field field = this.fields[columnIndex - 1]; + + int mysqlType = field.getMysqlType(); + + // Workaround for emulated locators in servers > 4.1, as server returns SUBSTRING(blob) as STRING type... + if (noConversion) { + mysqlType = MysqlDefs.FIELD_TYPE_BLOB; + } + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_BIT: + return (byte[]) value; + + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + if (value instanceof byte[]) { + return (byte[]) value; + } + break; + default: + break; + } + int sqlType = field.getSQLType(); + + if (sqlType == Types.VARBINARY || sqlType == Types.BINARY) { + return (byte[]) value; + } + + return getBytesFromString(getNativeString(columnIndex)); + } + + /** + * JDBC 2.0 + * + *

    + * Get the value of a column in the current row as a java.io.Reader. + *

    + * + * @param columnIndex + * the column to get the value from + * + * @return the value in the column as a java.io.Reader. + * + * @throws SQLException + * if an error occurs + */ + protected java.io.Reader getNativeCharacterStream(int columnIndex) throws SQLException { + int columnIndexMinusOne = columnIndex - 1; + + switch (this.fields[columnIndexMinusOne].getSQLType()) { + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.CLOB: + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return this.thisRow.getReader(columnIndexMinusOne); + } + + String asString = getStringForClob(columnIndex); + + if (asString == null) { + return null; + } + + return getCharacterStreamFromString(asString); + } + + /** + * JDBC 2.0 Get a CLOB column. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @return an object representing a CLOB + * + * @throws SQLException + * if an error occurs + */ + protected java.sql.Clob getNativeClob(int columnIndex) throws SQLException { + String stringVal = getStringForClob(columnIndex); + + if (stringVal == null) { + return null; + } + + return getClobFromString(stringVal); + } + + private String getNativeConvertToString(int columnIndex, Field field) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + int sqlType = field.getSQLType(); + int mysqlType = field.getMysqlType(); + + switch (sqlType) { + case Types.BIT: + return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex)); + case Types.BOOLEAN: + boolean booleanVal = getBoolean(columnIndex); + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(booleanVal); + + case Types.TINYINT: + byte tinyintVal = getNativeByte(columnIndex, false); + + if (this.wasNullFlag) { + return null; + } + + if (!field.isUnsigned() || tinyintVal >= 0) { + return String.valueOf(tinyintVal); + } + + short unsignedTinyVal = (short) (tinyintVal & 0xff); + + return String.valueOf(unsignedTinyVal); + + case Types.SMALLINT: + + int intVal = getNativeInt(columnIndex, false); + + if (this.wasNullFlag) { + return null; + } + + if (!field.isUnsigned() || intVal >= 0) { + return String.valueOf(intVal); + } + + intVal = intVal & 0xffff; + + return String.valueOf(intVal); + + case Types.INTEGER: + intVal = getNativeInt(columnIndex, false); + + if (this.wasNullFlag) { + return null; + } + + if (!field.isUnsigned() || intVal >= 0 || field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) { + + return String.valueOf(intVal); + } + + long longVal = intVal & 0xffffffffL; + + return String.valueOf(longVal); + + case Types.BIGINT: + + if (!field.isUnsigned()) { + longVal = getNativeLong(columnIndex, false, true); + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(longVal); + } + + longVal = getNativeLong(columnIndex, false, false); + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(convertLongToUlong(longVal)); + case Types.REAL: + float floatVal = getNativeFloat(columnIndex); + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(floatVal); + + case Types.FLOAT: + case Types.DOUBLE: + double doubleVal = getNativeDouble(columnIndex); + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(doubleVal); + + case Types.DECIMAL: + case Types.NUMERIC: + String stringVal = StringUtils.toAsciiString(this.thisRow.getColumnValue(columnIndex - 1)); + + BigDecimal val; + + if (stringVal != null) { + this.wasNullFlag = false; + + if (stringVal.length() == 0) { + val = new BigDecimal(0); + + return val.toString(); + } + + try { + val = new BigDecimal(stringVal); + } catch (NumberFormatException ex) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return val.toString(); + } + + this.wasNullFlag = true; + + return null; + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + + return extractStringFromNativeColumn(columnIndex, mysqlType); + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + + if (!field.isBlob()) { + return extractStringFromNativeColumn(columnIndex, mysqlType); + } else if (!field.isBinary()) { + return extractStringFromNativeColumn(columnIndex, mysqlType); + } else { + byte[] data = getBytes(columnIndex); + Object obj = data; + + if ((data != null) && (data.length >= 2)) { + if ((data[0] == -84) && (data[1] == -19)) { + // Serialized object? + try { + ByteArrayInputStream bytesIn = new ByteArrayInputStream(data); + ObjectInputStream objIn = new ObjectInputStream(bytesIn); + obj = objIn.readObject(); + objIn.close(); + bytesIn.close(); + } catch (ClassNotFoundException cnfe) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Class_not_found___91") + cnfe.toString() + + Messages.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor()); + } catch (IOException ex) { + obj = data; // not serialized? + } + } + + return obj.toString(); + } + + return extractStringFromNativeColumn(columnIndex, mysqlType); + } + + case Types.DATE: + + // The YEAR datatype needs to be handled differently here. + if (mysqlType == MysqlDefs.FIELD_TYPE_YEAR) { + short shortVal = getNativeShort(columnIndex); + + if (!this.connection.getYearIsDateType()) { + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(shortVal); + } + + if (field.getLength() == 2) { + + if (shortVal <= 69) { + shortVal = (short) (shortVal + 100); + } + + shortVal += 1900; + } + + return fastDateCreate(null, shortVal, 1, 1).toString(); + + } + + if (this.connection.getNoDatetimeStringSync()) { + byte[] asBytes = getNativeBytes(columnIndex, true); + + if (asBytes == null) { + return null; + } + + if (asBytes.length == 0 /* + * newer versions of the server + * seem to do this when they see all-zero datetime data + */) { + return "0000-00-00"; + } + + int year = (asBytes[0] & 0xff) | ((asBytes[1] & 0xff) << 8); + int month = asBytes[2]; + int day = asBytes[3]; + + if (year == 0 && month == 0 && day == 0) { + return "0000-00-00"; + } + } + + Date dt = getNativeDate(columnIndex); + + if (dt == null) { + return null; + } + + return String.valueOf(dt); + + case Types.TIME: + Time tm = getNativeTime(columnIndex, null, this.connection.getDefaultTimeZone(), false); + + if (tm == null) { + return null; + } + + return String.valueOf(tm); + + case Types.TIMESTAMP: + if (this.connection.getNoDatetimeStringSync()) { + byte[] asBytes = getNativeBytes(columnIndex, true); + + if (asBytes == null) { + return null; + } + + if (asBytes.length == 0 /* + * newer versions of the server + * seem to do this when they see all-zero datetime data + */) { + return "0000-00-00 00:00:00"; + } + + int year = (asBytes[0] & 0xff) | ((asBytes[1] & 0xff) << 8); + int month = asBytes[2]; + int day = asBytes[3]; + + if (year == 0 && month == 0 && day == 0) { + return "0000-00-00 00:00:00"; + } + } + + Timestamp tstamp = getNativeTimestamp(columnIndex, null, this.connection.getDefaultTimeZone(), false); + + if (tstamp == null) { + return null; + } + + String result = String.valueOf(tstamp); + + if (!this.connection.getNoDatetimeStringSync()) { + return result; + } + + if (result.endsWith(".0")) { + return result.substring(0, result.length() - 2); + } + return extractStringFromNativeColumn(columnIndex, mysqlType); + + default: + return extractStringFromNativeColumn(columnIndex, mysqlType); + } + } + } + + /** + * Get the value of a column in the current row as a java.sql.Date object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected java.sql.Date getNativeDate(int columnIndex) throws SQLException { + return getNativeDate(columnIndex, null); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date + * object. Use the calendar to construct an appropriate millisecond value + * for the Date, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param tz + * the calendar to use in constructing the date + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + protected java.sql.Date getNativeDate(int columnIndex, Calendar cal) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + int mysqlType = this.fields[columnIndexMinusOne].getMysqlType(); + + java.sql.Date dateToReturn = null; + + if (mysqlType == MysqlDefs.FIELD_TYPE_DATE) { + + dateToReturn = this.thisRow.getNativeDate(columnIndexMinusOne, this.connection, this, cal); + } else { + TimeZone tz = (cal != null) ? cal.getTimeZone() : this.getDefaultTimeZone(); + + boolean rollForward = (tz != null && !tz.equals(this.getDefaultTimeZone())); + + dateToReturn = (Date) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne, null, Types.DATE, mysqlType, tz, rollForward, this.connection, this); + } + + // + // normally, we allow ResultSetImpl methods to check for null first, but with DATETIME values we have this wacky need to support + // 0000-00-00 00:00:00 -> NULL, so we have to defer to the RowHolder implementation, and check the return value. + // + + if (dateToReturn == null) { + + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return dateToReturn; + } + + java.sql.Date getNativeDateViaParseConversion(int columnIndex) throws SQLException { + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getDate()", columnIndex, this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1], + new int[] { MysqlDefs.FIELD_TYPE_DATE }); + } + + String stringVal = getNativeString(columnIndex); + + return getDateFromString(stringVal, columnIndex, null); + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected double getNativeDouble(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + columnIndex--; // / JDBC is 1-based + + if (this.thisRow.isNull(columnIndex)) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + Field f = this.fields[columnIndex]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_DOUBLE: + return this.thisRow.getNativeDouble(columnIndex); + case MysqlDefs.FIELD_TYPE_TINY: + if (!f.isUnsigned()) { + return getNativeByte(columnIndex + 1); + } + + return getNativeShort(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + if (!f.isUnsigned()) { + return getNativeShort(columnIndex + 1); + } + + return getNativeInt(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + if (!f.isUnsigned()) { + return getNativeInt(columnIndex + 1); + } + + return getNativeLong(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_LONGLONG: + long valueAsLong = getNativeLong(columnIndex + 1); + + if (!f.isUnsigned()) { + return valueAsLong; + } + + BigInteger asBigInt = convertLongToUlong(valueAsLong); + + // TODO: Check for overflow + + return asBigInt.doubleValue(); + case MysqlDefs.FIELD_TYPE_FLOAT: + return getNativeFloat(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_BIT: + return getNumericRepresentationOfSQLBitType(columnIndex + 1); + default: + String stringVal = getNativeString(columnIndex + 1); + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getDouble()", columnIndex, stringVal, this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getDoubleFromString(stringVal, columnIndex + 1); + } + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected float getNativeFloat(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + columnIndex--; // / JDBC is 1-based + + if (this.thisRow.isNull(columnIndex)) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + Field f = this.fields[columnIndex]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1); + + return valueAsLong; + case MysqlDefs.FIELD_TYPE_DOUBLE: + + // Only foolproof way to check for overflow Not efficient, but if you don't want to be inefficient, use the correct binding for the type! + + Double valueAsDouble = new Double(getNativeDouble(columnIndex + 1)); + + float valueAsFloat = valueAsDouble.floatValue(); + + if (this.jdbcCompliantTruncationForReads && valueAsFloat == Float.NEGATIVE_INFINITY || valueAsFloat == Float.POSITIVE_INFINITY) { + throwRangeException(valueAsDouble.toString(), columnIndex + 1, Types.FLOAT); + } + + return (float) getNativeDouble(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_TINY: + if (!f.isUnsigned()) { + return getNativeByte(columnIndex + 1); + } + + return getNativeShort(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + if (!f.isUnsigned()) { + return getNativeShort(columnIndex + 1); + } + + return getNativeInt(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + if (!f.isUnsigned()) { + return getNativeInt(columnIndex + 1); + } + + return getNativeLong(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_LONGLONG: + valueAsLong = getNativeLong(columnIndex + 1); + + if (!f.isUnsigned()) { + return valueAsLong; + } + + BigInteger asBigInt = convertLongToUlong(valueAsLong); + + // TODO: Check for overflow + + return asBigInt.floatValue(); + case MysqlDefs.FIELD_TYPE_FLOAT: + + return this.thisRow.getNativeFloat(columnIndex); + + default: + String stringVal = getNativeString(columnIndex + 1); + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getFloat()", columnIndex, stringVal, this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getFloatFromString(stringVal, columnIndex + 1); + } + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected int getNativeInt(int columnIndex) throws SQLException { + return getNativeInt(columnIndex, true); + } + + protected int getNativeInt(int columnIndex, boolean overflowCheck) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + columnIndex--; // / JDBC is 1-based + + if (this.thisRow.isNull(columnIndex)) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + Field f = this.fields[columnIndex]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.INTEGER); + } + + return (int) valueAsLong; + case MysqlDefs.FIELD_TYPE_TINY: + byte tinyintVal = getNativeByte(columnIndex + 1, false); + + if (!f.isUnsigned() || tinyintVal >= 0) { + return tinyintVal; + } + + return tinyintVal + 256; + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + short asShort = getNativeShort(columnIndex + 1, false); + + if (!f.isUnsigned() || asShort >= 0) { + return asShort; + } + + return asShort + 65536; + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + + int valueAsInt = this.thisRow.getNativeInt(columnIndex); + + if (!f.isUnsigned()) { + return valueAsInt; + } + + valueAsLong = (valueAsInt >= 0) ? valueAsInt : valueAsInt + 4294967296L; + + if (overflowCheck && this.jdbcCompliantTruncationForReads && valueAsLong > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.INTEGER); + } + + return (int) valueAsLong; + case MysqlDefs.FIELD_TYPE_LONGLONG: + valueAsLong = getNativeLong(columnIndex + 1, false, true); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.INTEGER); + } + } + + return (int) valueAsLong; + case MysqlDefs.FIELD_TYPE_DOUBLE: + double valueAsDouble = getNativeDouble(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Integer.MIN_VALUE || valueAsDouble > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.INTEGER); + } + } + + return (int) valueAsDouble; + case MysqlDefs.FIELD_TYPE_FLOAT: + valueAsDouble = getNativeFloat(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Integer.MIN_VALUE || valueAsDouble > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.INTEGER); + } + } + + return (int) valueAsDouble; + + default: + String stringVal = getNativeString(columnIndex + 1); + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getInt()", columnIndex, stringVal, this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getIntFromString(stringVal, columnIndex + 1); + } + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected long getNativeLong(int columnIndex) throws SQLException { + return getNativeLong(columnIndex, true, true); + } + + protected long getNativeLong(int columnIndex, boolean overflowCheck, boolean expandUnsignedLong) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + columnIndex--; // / JDBC is 1-based + + if (this.thisRow.isNull(columnIndex)) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + Field f = this.fields[columnIndex]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + return getNumericRepresentationOfSQLBitType(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_TINY: + if (!f.isUnsigned()) { + return getNativeByte(columnIndex + 1); + } + + return getNativeInt(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_SHORT: + if (!f.isUnsigned()) { + return getNativeShort(columnIndex + 1); + } + + return getNativeInt(columnIndex + 1, false); + case MysqlDefs.FIELD_TYPE_YEAR: + + return getNativeShort(columnIndex + 1); + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + int asInt = getNativeInt(columnIndex + 1, false); + + if (!f.isUnsigned() || asInt >= 0) { + return asInt; + } + + return asInt + 4294967296L; + case MysqlDefs.FIELD_TYPE_LONGLONG: + long valueAsLong = this.thisRow.getNativeLong(columnIndex); + + if (!f.isUnsigned() || !expandUnsignedLong) { + return valueAsLong; + } + + BigInteger asBigInt = convertLongToUlong(valueAsLong); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && ((asBigInt.compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) + || (asBigInt.compareTo(new BigInteger(String.valueOf(Long.MIN_VALUE))) < 0))) { + throwRangeException(asBigInt.toString(), columnIndex + 1, Types.BIGINT); + } + + return getLongFromString(asBigInt.toString(), columnIndex); + + case MysqlDefs.FIELD_TYPE_DOUBLE: + double valueAsDouble = getNativeDouble(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Long.MIN_VALUE || valueAsDouble > Long.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.BIGINT); + } + } + + return (long) valueAsDouble; + case MysqlDefs.FIELD_TYPE_FLOAT: + valueAsDouble = getNativeFloat(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Long.MIN_VALUE || valueAsDouble > Long.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.BIGINT); + } + } + + return (long) valueAsDouble; + default: + String stringVal = getNativeString(columnIndex + 1); + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getLong()", columnIndex, stringVal, this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getLongFromString(stringVal, columnIndex + 1); + } + } + + /** + * JDBC 2.0 Get a REF(<structured-type>) column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing data of an SQL REF type + * + * @throws SQLException + * as this is not implemented + * @throws NotImplemented + */ + protected java.sql.Ref getNativeRef(int i) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected short getNativeShort(int columnIndex) throws SQLException { + return getNativeShort(columnIndex, true); + } + + protected short getNativeShort(int columnIndex, boolean overflowCheck) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + columnIndex--; // / JDBC is 1-based + + if (this.thisRow.isNull(columnIndex)) { + this.wasNullFlag = true; + + return 0; + } + + this.wasNullFlag = false; + + Field f = this.fields[columnIndex]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && (valueAsLong < Short.MIN_VALUE || valueAsLong > Short.MAX_VALUE)) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.SMALLINT); + } + + return (short) valueAsLong; + + case MysqlDefs.FIELD_TYPE_TINY: + byte tinyintVal = getNativeByte(columnIndex + 1, false); + + if (!f.isUnsigned() || tinyintVal >= 0) { + return tinyintVal; + } + + return (short) (tinyintVal + (short) 256); + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_YEAR: + + short asShort = this.thisRow.getNativeShort(columnIndex); + + if (!f.isUnsigned()) { + return asShort; + } + + int valueAsInt = asShort & 0xffff; + + if (overflowCheck && this.jdbcCompliantTruncationForReads && valueAsInt > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsInt), columnIndex + 1, Types.SMALLINT); + } + + return (short) valueAsInt; + case MysqlDefs.FIELD_TYPE_INT24: + case MysqlDefs.FIELD_TYPE_LONG: + if (!f.isUnsigned()) { + valueAsInt = getNativeInt(columnIndex + 1, false); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && valueAsInt > Short.MAX_VALUE || valueAsInt < Short.MIN_VALUE) { + throwRangeException(String.valueOf(valueAsInt), columnIndex + 1, Types.SMALLINT); + } + + return (short) valueAsInt; + } + + valueAsLong = getNativeLong(columnIndex + 1, false, true); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && valueAsLong > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.SMALLINT); + } + + return (short) valueAsLong; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + valueAsLong = getNativeLong(columnIndex + 1, false, false); + + if (!f.isUnsigned()) { + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsLong < Short.MIN_VALUE || valueAsLong > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsLong), columnIndex + 1, Types.SMALLINT); + } + } + + return (short) valueAsLong; + } + + BigInteger asBigInt = convertLongToUlong(valueAsLong); + + if (overflowCheck && this.jdbcCompliantTruncationForReads && ((asBigInt.compareTo(new BigInteger(String.valueOf(Short.MAX_VALUE))) > 0) + || (asBigInt.compareTo(new BigInteger(String.valueOf(Short.MIN_VALUE))) < 0))) { + throwRangeException(asBigInt.toString(), columnIndex + 1, Types.SMALLINT); + } + + return (short) getIntFromString(asBigInt.toString(), columnIndex + 1); + + case MysqlDefs.FIELD_TYPE_DOUBLE: + double valueAsDouble = getNativeDouble(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Short.MIN_VALUE || valueAsDouble > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex + 1, Types.SMALLINT); + } + } + + return (short) valueAsDouble; + case MysqlDefs.FIELD_TYPE_FLOAT: + float valueAsFloat = getNativeFloat(columnIndex + 1); + + if (overflowCheck && this.jdbcCompliantTruncationForReads) { + if (valueAsFloat < Short.MIN_VALUE || valueAsFloat > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsFloat), columnIndex + 1, Types.SMALLINT); + } + } + + return (short) valueAsFloat; + default: + String stringVal = getNativeString(columnIndex + 1); + + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getShort()", columnIndex, stringVal, this.fields[columnIndex], + new int[] { MysqlDefs.FIELD_TYPE_DOUBLE, MysqlDefs.FIELD_TYPE_TINY, MysqlDefs.FIELD_TYPE_SHORT, MysqlDefs.FIELD_TYPE_LONG, + MysqlDefs.FIELD_TYPE_LONGLONG, MysqlDefs.FIELD_TYPE_FLOAT }); + } + + return getShortFromString(stringVal, columnIndex + 1); + } + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value, null for SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected String getNativeString(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (this.fields == null) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Query_generated_no_fields_for_ResultSet_133"), + SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, getExceptionInterceptor()); + } + + if (this.thisRow.isNull(columnIndex - 1)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + String stringVal = null; + + Field field = this.fields[columnIndex - 1]; + + // TODO: Check Types Here. + stringVal = getNativeConvertToString(columnIndex, field); + int mysqlType = field.getMysqlType(); + + if (mysqlType != MysqlDefs.FIELD_TYPE_TIMESTAMP && mysqlType != MysqlDefs.FIELD_TYPE_DATE && field.isZeroFill() && (stringVal != null)) { + int origLength = stringVal.length(); + + StringBuilder zeroFillBuf = new StringBuilder(origLength); + + long numZeros = field.getLength() - origLength; + + for (long i = 0; i < numZeros; i++) { + zeroFillBuf.append('0'); + } + + zeroFillBuf.append(stringVal); + + stringVal = zeroFillBuf.toString(); + } + + return stringVal; + } + + private Time getNativeTime(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + int mysqlType = this.fields[columnIndexMinusOne].getMysqlType(); + + Time timeVal = null; + + if (mysqlType == MysqlDefs.FIELD_TYPE_TIME) { + timeVal = this.thisRow.getNativeTime(columnIndexMinusOne, targetCalendar, tz, rollForward, this.connection, this); + + } else { + timeVal = (Time) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne, null, Types.TIME, mysqlType, tz, rollForward, this.connection, this); + } + + // + // normally, we allow ResultSetImpl methods to check for null first, but with DATETIME values we have this wacky need to support + // 0000-00-00 00:00:00 -> NULL, so we have to defer to the RowHolder implementation, and check the return value. + // + + if (timeVal == null) { + + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return timeVal; + } + + Time getNativeTimeViaParseConversion(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getTime()", columnIndex, this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1], + new int[] { MysqlDefs.FIELD_TYPE_TIME }); + } + + String strTime = getNativeString(columnIndex); + + return getTimeFromString(strTime, targetCalendar, columnIndex, tz, rollForward); + } + + private Timestamp getNativeTimestamp(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + Timestamp tsVal = null; + + int mysqlType = this.fields[columnIndexMinusOne].getMysqlType(); + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + tsVal = this.thisRow.getNativeTimestamp(columnIndexMinusOne, targetCalendar, tz, rollForward, this.connection, this); + break; + + default: + + tsVal = (Timestamp) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne, null, Types.TIMESTAMP, mysqlType, tz, rollForward, this.connection, + this); + } + + // + // normally, we allow ResultSetImpl methods to check for null first but with DATETIME values we have this wacky need to support + // 0000-00-00 00:00:00 -> NULL, so we have to defer to the RowHolder implementation, and check the return value. + // + + if (tsVal == null) { + + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return tsVal; + } + + Timestamp getNativeTimestampViaParseConversion(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + if (this.useUsageAdvisor) { + issueConversionViaParsingWarning("getTimestamp()", columnIndex, this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1], + new int[] { MysqlDefs.FIELD_TYPE_TIMESTAMP, MysqlDefs.FIELD_TYPE_DATETIME }); + } + + String strTimestamp = getNativeString(columnIndex); + + return getTimestampFromString(columnIndex, targetCalendar, strTimestamp, tz, rollForward); + } + + // --------------------------------------------------------------------- + // Updates + // --------------------------------------------------------------------- + + /** + * A column value can also be retrieved as a stream of Unicode characters. + * We implement this as a binary stream. + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Java InputStream that delivers the database column value as a + * stream of two byte Unicode characters. If the value is SQL NULL, + * then the result is null + * + * @exception SQLException + * if a database access error occurs + * + * @see getAsciiStream + * @see getBinaryStream + */ + protected InputStream getNativeUnicodeStream(int columnIndex) throws SQLException { + checkRowPos(); + + return getBinaryStream(columnIndex); + } + + /** + * @see ResultSetInternalMethods#getURL(int) + */ + protected URL getNativeURL(int colIndex) throws SQLException { + String val = getString(colIndex); + + if (val == null) { + return null; + } + + try { + return new URL(val); + } catch (MalformedURLException mfe) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Malformed_URL____141") + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + /** + * @return Returns the nextResultSet, if any, null if none exists. + */ + public synchronized ResultSetInternalMethods getNextResultSet() { + return this.nextResultSet; + } + + /** + * Get the value of a column in the current row as a Java object + * + *

    + * This method will return the value of the given column as a Java object. The type of the Java object will be the default Java Object type corresponding to + * the column's SQL type, following the mapping specified in the JDBC specification. + *

    + * + *

    + * This method may also be used to read database specific abstract data types. + *

    + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Object holding the column value + * + * @exception SQLException + * if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + Field field; + field = this.fields[columnIndexMinusOne]; + + switch (field.getSQLType()) { + case Types.BIT: + if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT && !field.isSingleBit()) { + return getObjectDeserializingIfNeeded(columnIndex); + } + return Boolean.valueOf(getBoolean(columnIndex)); + + case Types.BOOLEAN: + return Boolean.valueOf(getBoolean(columnIndex)); + + case Types.TINYINT: + if (!field.isUnsigned()) { + return Integer.valueOf(getByte(columnIndex)); + } + + return Integer.valueOf(getInt(columnIndex)); + + case Types.SMALLINT: + + return Integer.valueOf(getInt(columnIndex)); + + case Types.INTEGER: + + if (!field.isUnsigned() || field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) { + return Integer.valueOf(getInt(columnIndex)); + } + + return Long.valueOf(getLong(columnIndex)); + + case Types.BIGINT: + + if (!field.isUnsigned()) { + return Long.valueOf(getLong(columnIndex)); + } + + String stringVal = getString(columnIndex); + + if (stringVal == null) { + return null; + } + + try { + return new BigInteger(stringVal); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigInteger", new Object[] { Integer.valueOf(columnIndex), stringVal }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + case Types.DECIMAL: + case Types.NUMERIC: + stringVal = getString(columnIndex); + + BigDecimal val; + + if (stringVal != null) { + if (stringVal.length() == 0) { + val = new BigDecimal(0); + + return val; + } + + try { + val = new BigDecimal(stringVal); + } catch (NumberFormatException ex) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return val; + } + + return null; + + case Types.REAL: + return new Float(getFloat(columnIndex)); + + case Types.FLOAT: + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + + case Types.CHAR: + case Types.VARCHAR: + if (!field.isOpaqueBinary()) { + return getString(columnIndex); + } + + return getBytes(columnIndex); + case Types.LONGVARCHAR: + if (!field.isOpaqueBinary()) { + return getStringForClob(columnIndex); + } + + return getBytes(columnIndex); + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_GEOMETRY) { + return getBytes(columnIndex); + } + return getObjectDeserializingIfNeeded(columnIndex); + + case Types.DATE: + if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR && !this.connection.getYearIsDateType()) { + return Short.valueOf(getShort(columnIndex)); + } + + return getDate(columnIndex); + + case Types.TIME: + return getTime(columnIndex); + + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + + default: + return getString(columnIndex); + } + } + + private Object getObjectDeserializingIfNeeded(int columnIndex) throws SQLException { + final Field field = this.fields[columnIndex - 1]; + + if (field.isBinary() || field.isBlob()) { + byte[] data = getBytes(columnIndex); + + if (this.connection.getAutoDeserialize()) { + Object obj = data; + + if ((data != null) && (data.length >= 2)) { + if ((data[0] == -84) && (data[1] == -19)) { + // Serialized object? + try { + ByteArrayInputStream bytesIn = new ByteArrayInputStream(data); + ObjectInputStream objIn = new ObjectInputStream(bytesIn); + obj = objIn.readObject(); + objIn.close(); + bytesIn.close(); + } catch (ClassNotFoundException cnfe) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Class_not_found___91") + cnfe.toString() + + Messages.getString("ResultSet._while_reading_serialized_object_92"), getExceptionInterceptor()); + } catch (IOException ex) { + obj = data; // not serialized? + } + } else { + return getString(columnIndex); + } + } + + return obj; + } + + return data; + } + + return getBytes(columnIndex); + } + + @SuppressWarnings("unchecked") + public T getObject(int columnIndex, Class type) throws SQLException { + if (type == null) { + throw SQLError.createSQLException("Type parameter can not be null", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (type.equals(String.class)) { + return (T) getString(columnIndex); + } else if (type.equals(BigDecimal.class)) { + return (T) getBigDecimal(columnIndex); + } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) { + return (T) Boolean.valueOf(getBoolean(columnIndex)); + } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) { + return (T) Integer.valueOf(getInt(columnIndex)); + } else if (type.equals(Long.class) || type.equals(Long.TYPE)) { + return (T) Long.valueOf(getLong(columnIndex)); + } else if (type.equals(Float.class) || type.equals(Float.TYPE)) { + return (T) Float.valueOf(getFloat(columnIndex)); + } else if (type.equals(Double.class) || type.equals(Double.TYPE)) { + return (T) Double.valueOf(getDouble(columnIndex)); + } else if (type.equals(byte[].class)) { + return (T) getBytes(columnIndex); + } else if (type.equals(java.sql.Date.class)) { + return (T) getDate(columnIndex); + } else if (type.equals(Time.class)) { + return (T) getTime(columnIndex); + } else if (type.equals(Timestamp.class)) { + return (T) getTimestamp(columnIndex); + } else if (type.equals(Clob.class)) { + return (T) getClob(columnIndex); + } else if (type.equals(Blob.class)) { + return (T) getBlob(columnIndex); + } else if (type.equals(Array.class)) { + return (T) getArray(columnIndex); + } else if (type.equals(Ref.class)) { + return (T) getRef(columnIndex); + } else if (type.equals(URL.class)) { + return (T) getURL(columnIndex); + } else { + if (this.connection.getAutoDeserialize()) { + try { + return type.cast(getObject(columnIndex)); + } catch (ClassCastException cce) { + SQLException sqlEx = SQLError.createSQLException("Conversion not supported for type " + type.getName(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + sqlEx.initCause(cce); + + throw sqlEx; + } + } + + throw SQLError.createSQLException("Conversion not supported for type " + type.getName(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + // JDBC-4.1 + public T getObject(String columnLabel, Class type) throws SQLException { + return getObject(findColumn(columnLabel), type); + } + + /** + * JDBC 2.0 Returns the value of column i as a Java object. Use the map to + * determine the class from which to construct data of SQL structured and + * distinct types. + * + * @param i + * the first column is 1, the second is 2, ... + * @param map + * the mapping from SQL type names to Java classes + * + * @return an object representing the SQL value + * + * @throws SQLException + * because this is not implemented + */ + public Object getObject(int i, java.util.Map> map) throws SQLException { + return getObject(i); + } + + /** + * Get the value of a column in the current row as a Java object + * + *

    + * This method will return the value of the given column as a Java object. The type of the Java object will be the default Java Object type corresponding to + * the column's SQL type, following the mapping specified in the JDBC specification. + *

    + * + *

    + * This method may also be used to read database specific abstract data types. + *

    + * + * @param columnName + * is the SQL name of the column + * + * @return a Object holding the column value + * + * @exception SQLException + * if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException { + return getObject(findColumn(columnName)); + } + + /** + * JDBC 2.0 Returns the value of column i as a Java object. Use the map to + * determine the class from which to construct data of SQL structured and + * distinct types. + * + * @param colName + * the column name + * @param map + * the mapping from SQL type names to Java classes + * + * @return an object representing the SQL value + * + * @throws SQLException + * as this is not implemented + */ + public Object getObject(String colName, java.util.Map> map) throws SQLException { + return getObject(findColumn(colName), map); + } + + public Object getObjectStoredProc(int columnIndex, int desiredSqlType) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + Field field; + field = this.fields[columnIndex - 1]; + + switch (desiredSqlType) { + case Types.BIT: + case Types.BOOLEAN: + // valueOf would be nicer here, but it isn't present in JDK-1.3.1, which is what the CTS uses. + return Boolean.valueOf(getBoolean(columnIndex)); + + case Types.TINYINT: + return Integer.valueOf(getInt(columnIndex)); + + case Types.SMALLINT: + return Integer.valueOf(getInt(columnIndex)); + + case Types.INTEGER: + + if (!field.isUnsigned() || field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) { + return Integer.valueOf(getInt(columnIndex)); + } + + return Long.valueOf(getLong(columnIndex)); + + case Types.BIGINT: + + if (field.isUnsigned()) { + return getBigDecimal(columnIndex); + } + + return Long.valueOf(getLong(columnIndex)); + + case Types.DECIMAL: + case Types.NUMERIC: + + String stringVal = getString(columnIndex); + BigDecimal val; + + if (stringVal != null) { + if (stringVal.length() == 0) { + val = new BigDecimal(0); + + return val; + } + + try { + val = new BigDecimal(stringVal); + } catch (NumberFormatException ex) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_BigDecimal", new Object[] { stringVal, Integer.valueOf(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return val; + } + + return null; + + case Types.REAL: + return new Float(getFloat(columnIndex)); + + case Types.FLOAT: + + if (!this.connection.getRunningCTS13()) { + return new Double(getFloat(columnIndex)); + } + return new Float(getFloat(columnIndex)); // NB - bug in JDBC compliance test, according to JDBC spec, FLOAT type should return DOUBLE + // but causes ClassCastException in CTS :( + + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.LONGVARCHAR: + return getStringForClob(columnIndex); + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return getBytes(columnIndex); + + case Types.DATE: + if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR && !this.connection.getYearIsDateType()) { + return Short.valueOf(getShort(columnIndex)); + } + + return getDate(columnIndex); + + case Types.TIME: + return getTime(columnIndex); + + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + + default: + return getString(columnIndex); + } + } + + public Object getObjectStoredProc(int i, java.util.Map map, int desiredSqlType) throws SQLException { + return getObjectStoredProc(i, desiredSqlType); + } + + public Object getObjectStoredProc(String columnName, int desiredSqlType) throws SQLException { + return getObjectStoredProc(findColumn(columnName), desiredSqlType); + } + + public Object getObjectStoredProc(String colName, java.util.Map map, int desiredSqlType) throws SQLException { + return getObjectStoredProc(findColumn(colName), map, desiredSqlType); + } + + /** + * JDBC 2.0 Get a REF(<structured-type>) column. + * + * @param i + * the first column is 1, the second is 2, ... + * + * @return an object representing data of an SQL REF type + * + * @throws SQLException + * as this is not implemented + * @throws NotImplemented + */ + public java.sql.Ref getRef(int i) throws SQLException { + checkColumnBounds(i); + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Get a REF(<structured-type>) column. + * + * @param colName + * the column name + * + * @return an object representing data of an SQL REF type + * + * @throws SQLException + * as this method is not implemented. + * @throws NotImplemented + */ + public java.sql.Ref getRef(String colName) throws SQLException { + return getRef(findColumn(colName)); + } + + /** + * JDBC 2.0 + * + *

    + * Determine the current row number. The first row is number 1, the second number 2, etc. + *

    + * + * @return the current row number, else return 0 if there is no current row + * + * @exception SQLException + * if a database-access error occurs. + */ + public int getRow() throws SQLException { + checkClosed(); + + int currentRowNumber = this.rowData.getCurrentRowNumber(); + int row = 0; + + // Non-dynamic result sets can be interrogated for this information + if (!this.rowData.isDynamic()) { + if ((currentRowNumber < 0) || this.rowData.isAfterLast() || this.rowData.isEmpty()) { + row = 0; + } else { + row = currentRowNumber + 1; + } + } else { + // dynamic (streaming) can not + row = currentRowNumber + 1; + } + + return row; + } + + /** + * Returns the server info (if any), or null if none. + * + * @return server info created for this ResultSet + */ + public String getServerInfo() { + try { + synchronized (checkClosed().getConnectionMutex()) { + return this.serverInfo; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + private long getNumericRepresentationOfSQLBitType(int columnIndex) throws SQLException { + + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (this.fields[columnIndex - 1].isSingleBit() || ((byte[]) value).length == 1) { + return ((byte[]) value)[0]; + } + + byte[] asBytes = (byte[]) value; + + int shift = 0; + + long[] steps = new long[asBytes.length]; + + for (int i = asBytes.length - 1; i >= 0; i--) { + steps[i] = (long) (asBytes[i] & 0xff) << shift; + shift += 8; + } + + long valueAsLong = 0; + + for (int i = 0; i < asBytes.length; i++) { + valueAsLong |= steps[i]; + } + + return valueAsLong; + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (!this.isBinaryEncoded) { + if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex); + + if (this.jdbcCompliantTruncationForReads && (valueAsLong < Short.MIN_VALUE || valueAsLong > Short.MAX_VALUE)) { + throwRangeException(String.valueOf(valueAsLong), columnIndex, Types.SMALLINT); + } + + return (short) valueAsLong; + } + + if (this.useFastIntParsing) { + Object value = this.thisRow.getColumnValue(columnIndex - 1); + + if (value == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return 0; + } + + byte[] shortAsBytes = (byte[]) value; + + if (shortAsBytes.length == 0) { + return (short) convertToZeroWithEmptyCheck(); + } + + boolean needsFullParse = false; + + for (int i = 0; i < shortAsBytes.length; i++) { + if (((char) shortAsBytes[i] == 'e') || ((char) shortAsBytes[i] == 'E')) { + needsFullParse = true; + + break; + } + } + + if (!needsFullParse) { + try { + return parseShortWithOverflowCheck(columnIndex, shortAsBytes, null); + } catch (NumberFormatException nfe) { + try { + return parseShortAsDouble(columnIndex, StringUtils.toString(shortAsBytes)); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Invalid_value_for_getShort()_-____96") + StringUtils.toString(shortAsBytes) + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + String val = null; + + try { + val = getString(columnIndex); + + if ((val != null)) { + + if (val.length() == 0) { + return (short) convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { + return parseShortWithOverflowCheck(columnIndex, null, val); + } + + // Convert floating point + return parseShortAsDouble(columnIndex, val); + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + return parseShortAsDouble(columnIndex, val); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getShort()_-____96") + val + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return getNativeShort(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + */ + public short getShort(String columnName) throws SQLException { + return getShort(findColumn(columnName)); + } + + private final short getShortFromString(String val, int columnIndex) throws SQLException { + try { + if ((val != null)) { + + if (val.length() == 0) { + return (short) convertToZeroWithEmptyCheck(); + } + + if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1) && (val.indexOf(".") == -1)) { + return parseShortWithOverflowCheck(columnIndex, null, val); + } + + // Convert floating point + return parseShortAsDouble(columnIndex, val); + } + + return 0; // for NULL + } catch (NumberFormatException nfe) { + try { + return parseShortAsDouble(columnIndex, val); + } catch (NumberFormatException newNfe) { + // ignore, it's not a number + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Invalid_value_for_getShort()_-____217") + val + + Messages.getString("ResultSet.___in_column__218") + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Return the Statement that produced the ResultSet. + * + * @return the Statment that produced the result set, or null if the result + * was produced some other way. + * + * @exception SQLException + * if a database-access error occurs + */ + public java.sql.Statement getStatement() throws SQLException { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.wrapperStatement != null) { + return this.wrapperStatement; + } + + return this.owningStatement; + } + + } catch (SQLException sqlEx) { + if (!this.retainOwningStatement) { + throw SQLError.createSQLException("Operation not allowed on closed ResultSet. Statements " + + "can be retained over result set closure by setting the connection property " + "\"retainStatementAfterResultSetClose\" to \"true\".", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + if (this.wrapperStatement != null) { + return this.wrapperStatement; + } + + return this.owningStatement; + } + + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value, null for SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException { + String stringVal = getStringInternal(columnIndex, true); + + if (this.padCharsWithSpace && stringVal != null) { + Field f = this.fields[columnIndex - 1]; + + if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING) { + int fieldLength = (int) f.getLength() /* safe, bytes in a CHAR <= 1024 */ / f.getMaxBytesPerCharacter(); /* safe, this will never be 0 */ + + int currentLength = stringVal.length(); + + if (currentLength < fieldLength) { + StringBuilder paddedBuf = new StringBuilder(fieldLength); + paddedBuf.append(stringVal); + + int difference = fieldLength - currentLength; + + paddedBuf.append(EMPTY_SPACE, 0, difference); + + stringVal = paddedBuf.toString(); + } + } + } + + return stringVal; + } + + /** + * The following routines simply convert the columnName into a columnIndex + * and then call the appropriate routine above. + * + * @param columnName + * is the SQL name of the column + * + * @return the column value + * + * @exception SQLException + * if a database access error occurs + */ + public String getString(String columnName) throws SQLException { + return getString(findColumn(columnName)); + } + + private String getStringForClob(int columnIndex) throws SQLException { + String asString = null; + + String forcedEncoding = this.connection.getClobCharacterEncoding(); + + if (forcedEncoding == null) { + if (!this.isBinaryEncoded) { + asString = getString(columnIndex); + } else { + asString = getNativeString(columnIndex); + } + } else { + try { + byte[] asBytes = null; + + if (!this.isBinaryEncoded) { + asBytes = getBytes(columnIndex); + } else { + asBytes = getNativeBytes(columnIndex, true); + } + + if (asBytes != null) { + asString = StringUtils.toString(asBytes, forcedEncoding); + } + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + return asString; + } + + protected String getStringInternal(int columnIndex, boolean checkDateTypes) throws SQLException { + if (!this.isBinaryEncoded) { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (this.fields == null) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Query_generated_no_fields_for_ResultSet_99"), + SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, getExceptionInterceptor()); + } + + // JDBC is 1-based, Java is not !? + + int internalColumnIndex = columnIndex - 1; + + if (this.thisRow.isNull(internalColumnIndex)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + Field metadata = this.fields[internalColumnIndex]; + + String stringVal = null; + + if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + if (metadata.isSingleBit()) { + byte[] value = this.thisRow.getColumnValue(internalColumnIndex); + + if (value.length == 0) { + return String.valueOf(convertToZeroWithEmptyCheck()); + } + + return String.valueOf(value[0]); + } + + return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex)); + } + + String encoding = metadata.getEncoding(); + + stringVal = this.thisRow.getString(internalColumnIndex, encoding, this.connection); + + // + // Special handling for YEAR type from mysql, some people want it as a DATE, others want to treat it as a SHORT + // + + if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + if (!this.connection.getYearIsDateType()) { + return stringVal; + } + + Date dt = getDateFromString(stringVal, columnIndex, null); + + if (dt == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return dt.toString(); + } + + // Handles timezone conversion and zero-date behavior + + if (checkDateTypes && !this.connection.getNoDatetimeStringSync()) { + switch (metadata.getSQLType()) { + case Types.TIME: + Time tm = getTimeFromString(stringVal, null, columnIndex, this.getDefaultTimeZone(), false); + + if (tm == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return tm.toString(); + case Types.DATE: + + Date dt = getDateFromString(stringVal, columnIndex, null); + + if (dt == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return dt.toString(); + case Types.TIMESTAMP: + Timestamp ts = getTimestampFromString(columnIndex, null, stringVal, this.getDefaultTimeZone(), false); + + if (ts == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return ts.toString(); + default: + break; + } + } + + return stringVal; + } + + return getNativeString(columnIndex); + } + + /** + * Get the value of a column in the current row as a java.sql.Time object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @throws java.sql.SQLException + * if a database access error occurs + */ + public Time getTime(int columnIndex) throws java.sql.SQLException { + return getTimeInternal(columnIndex, null, this.getDefaultTimeZone(), false); + } + + /** + * Get the value of a column in the current row as a java.sql.Time object. + * Use the calendar to construct an appropriate millisecond value for the + * Time, if the underlying database doesn't store timezone information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the calendar to use in constructing the time + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLException { + return getTimeInternal(columnIndex, cal, cal.getTimeZone(), true); + } + + /** + * Get the value of a column in the current row as a java.sql.Time object. + * + * @param columnName + * is the SQL name of the column + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @throws java.sql.SQLException + * if a database-access error occurs. + */ + public Time getTime(String columnName) throws java.sql.SQLException { + return getTime(findColumn(columnName)); + } + + /** + * Get the value of a column in the current row as a java.sql.Time object. + * Use the calendar to construct an appropriate millisecond value for the + * Time, if the underlying database doesn't store timezone information. + * + * @param columnName + * is the SQL name of the column + * @param cal + * the calendar to use in constructing the time + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException { + return getTime(findColumn(columnName), cal); + } + + private Time getTimeFromString(String timeAsString, Calendar targetCalendar, int columnIndex, TimeZone tz, boolean rollForward) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + int hr = 0; + int min = 0; + int sec = 0; + + try { + + if (timeAsString == null) { + this.wasNullFlag = true; + + return null; + } + + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace + // is present + // + + timeAsString = timeAsString.trim(); + + // truncate fractional part + int dec = timeAsString.indexOf("."); + if (dec > -1) { + timeAsString = timeAsString.substring(0, dec); + } + + if (timeAsString.equals("0") || timeAsString.equals("0000-00-00") || timeAsString.equals("0000-00-00 00:00:00") + || timeAsString.equals("00000000000000")) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(this.connection.getZeroDateTimeBehavior())) { + this.wasNullFlag = true; + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(this.connection.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + timeAsString + "' can not be represented as java.sql.Time", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + // We're left with the case of 'round' to a time Java _can_ represent, which is '00:00:00' + return fastTimeCreate(targetCalendar, 0, 0, 0); + } + + this.wasNullFlag = false; + + Field timeColField = this.fields[columnIndex - 1]; + + if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + // It's a timestamp + int length = timeAsString.length(); + + switch (length) { + case 19: { // YYYY-MM-DD hh:mm:ss + + hr = Integer.parseInt(timeAsString.substring(length - 8, length - 6)); + min = Integer.parseInt(timeAsString.substring(length - 5, length - 3)); + sec = Integer.parseInt(timeAsString.substring(length - 2, length)); + } + + break; + case 14: + case 12: { + hr = Integer.parseInt(timeAsString.substring(length - 6, length - 4)); + min = Integer.parseInt(timeAsString.substring(length - 4, length - 2)); + sec = Integer.parseInt(timeAsString.substring(length - 2, length)); + } + + break; + + case 10: { + hr = Integer.parseInt(timeAsString.substring(6, 8)); + min = Integer.parseInt(timeAsString.substring(8, 10)); + sec = 0; + } + + break; + + default: + throw SQLError.createSQLException(Messages.getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") + + columnIndex + "(" + this.fields[columnIndex - 1] + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + SQLWarning precisionLost = new SQLWarning( + Messages.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") + columnIndex + "(" + + this.fields[columnIndex - 1] + ")."); + + if (this.warningChain == null) { + this.warningChain = precisionLost; + } else { + this.warningChain.setNextWarning(precisionLost); + } + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { + hr = Integer.parseInt(timeAsString.substring(11, 13)); + min = Integer.parseInt(timeAsString.substring(14, 16)); + sec = Integer.parseInt(timeAsString.substring(17, 19)); + + SQLWarning precisionLost = new SQLWarning( + Messages.getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") + columnIndex + "(" + + this.fields[columnIndex - 1] + ")."); + + if (this.warningChain == null) { + this.warningChain = precisionLost; + } else { + this.warningChain.setNextWarning(precisionLost); + } + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) { + return fastTimeCreate(targetCalendar, 0, 0, 0); // midnight on the given + // date + } else { + // convert a String to a Time + if ((timeAsString.length() != 5) && (timeAsString.length() != 8)) { + throw SQLError + .createSQLException( + Messages.getString("ResultSet.Bad_format_for_Time____267") + timeAsString + + Messages.getString("ResultSet.___in_column__268") + columnIndex, + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + hr = Integer.parseInt(timeAsString.substring(0, 2)); + min = Integer.parseInt(timeAsString.substring(3, 5)); + sec = (timeAsString.length() == 5) ? 0 : Integer.parseInt(timeAsString.substring(6)); + } + + Calendar sessionCalendar = this.getCalendarInstanceForSessionOrNew(); + + return TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, fastTimeCreate(sessionCalendar, hr, min, sec), + this.connection.getServerTimezoneTZ(), tz, rollForward); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + } + + /** + * Get the value of a column in the current row as a java.sql.Time object in + * the given timezone + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param tz + * the Timezone to use + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + private Time getTimeInternal(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws java.sql.SQLException { + checkRowPos(); + + if (this.isBinaryEncoded) { + return getNativeTime(columnIndex, targetCalendar, tz, rollForward); + } + + if (!this.useFastDateParsing) { + String timeAsString = getStringInternal(columnIndex, false); + + return getTimeFromString(timeAsString, targetCalendar, columnIndex, tz, rollForward); + } + + checkColumnBounds(columnIndex); + + int columnIndexMinusOne = columnIndex - 1; + + if (this.thisRow.isNull(columnIndexMinusOne)) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + return this.thisRow.getTimeFast(columnIndexMinusOne, targetCalendar, tz, rollForward, this.connection, this); + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws java.sql.SQLException { + return getTimestampInternal(columnIndex, null, this.getDefaultTimeZone(), false); + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object. Use the calendar to construct an appropriate millisecond value + * for the Timestamp, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the calendar to use in constructing the timestamp + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return getTimestampInternal(columnIndex, cal, cal.getTimeZone(), true); + } + + /** + * @param columnName + * + * @throws java.sql.SQLException + */ + public Timestamp getTimestamp(String columnName) throws java.sql.SQLException { + return getTimestamp(findColumn(columnName)); + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object. Use the calendar to construct an appropriate millisecond value + * for the Timestamp, if the underlying database doesn't store timezone + * information. + * + * @param columnName + * is the SQL name of the column + * @param cal + * the calendar to use in constructing the timestamp + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { + return getTimestamp(findColumn(columnName), cal); + } + + private Timestamp getTimestampFromString(int columnIndex, Calendar targetCalendar, String timestampValue, TimeZone tz, boolean rollForward) + throws java.sql.SQLException { + try { + this.wasNullFlag = false; + + if (timestampValue == null) { + this.wasNullFlag = true; + + return null; + } + + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace is + // present + // + + timestampValue = timestampValue.trim(); + + int length = timestampValue.length(); + + Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + + if ((length > 0) && (timestampValue.charAt(0) == '0') && (timestampValue.equals("0000-00-00") || timestampValue.equals("0000-00-00 00:00:00") + || timestampValue.equals("00000000000000") || timestampValue.equals("0"))) { + + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(this.connection.getZeroDateTimeBehavior())) { + this.wasNullFlag = true; + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(this.connection.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + timestampValue + "' can not be represented as java.sql.Timestamp", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + // We're left with the case of 'round' to a date Java _can_ represent, which is '0001-01-01'. + return fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0); + + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + if (!this.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, Integer.parseInt(timestampValue.substring(0, 4)), 1, 1, 0, 0, 0, 0); + } + + return TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, + fastTimestampCreate(sessionCalendar, Integer.parseInt(timestampValue.substring(0, 4)), 1, 1, 0, 0, 0, 0), + this.connection.getServerTimezoneTZ(), tz, rollForward); + + } else { + // Convert from TIMESTAMP or DATE + + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minutes = 0; + int seconds = 0; + int nanos = 0; + + // check for the fractional part + int decimalIndex = timestampValue.indexOf("."); + + if (decimalIndex == length - 1) { + // if the dot is in last position + length--; + + } else if (decimalIndex != -1) { + + if ((decimalIndex + 2) <= length) { + nanos = Integer.parseInt(timestampValue.substring(decimalIndex + 1)); + + int numDigits = length - (decimalIndex + 1); + + if (numDigits < 9) { + int factor = (int) (Math.pow(10, 9 - numDigits)); + nanos = nanos * factor; + } + + length = decimalIndex; + } else { + throw new IllegalArgumentException(); // re-thrown further down with a much better error message + } + } + + switch (length) { + case 26: + case 25: + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: { + year = Integer.parseInt(timestampValue.substring(0, 4)); + month = Integer.parseInt(timestampValue.substring(5, 7)); + day = Integer.parseInt(timestampValue.substring(8, 10)); + hour = Integer.parseInt(timestampValue.substring(11, 13)); + minutes = Integer.parseInt(timestampValue.substring(14, 16)); + seconds = Integer.parseInt(timestampValue.substring(17, 19)); + + break; + } + + case 14: { + year = Integer.parseInt(timestampValue.substring(0, 4)); + month = Integer.parseInt(timestampValue.substring(4, 6)); + day = Integer.parseInt(timestampValue.substring(6, 8)); + hour = Integer.parseInt(timestampValue.substring(8, 10)); + minutes = Integer.parseInt(timestampValue.substring(10, 12)); + seconds = Integer.parseInt(timestampValue.substring(12, 14)); + + break; + } + + case 12: { + year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + + month = Integer.parseInt(timestampValue.substring(2, 4)); + day = Integer.parseInt(timestampValue.substring(4, 6)); + hour = Integer.parseInt(timestampValue.substring(6, 8)); + minutes = Integer.parseInt(timestampValue.substring(8, 10)); + seconds = Integer.parseInt(timestampValue.substring(10, 12)); + + break; + } + + case 10: { + if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) || (timestampValue.indexOf("-") != -1)) { + year = Integer.parseInt(timestampValue.substring(0, 4)); + month = Integer.parseInt(timestampValue.substring(5, 7)); + day = Integer.parseInt(timestampValue.substring(8, 10)); + hour = 0; + minutes = 0; + } else { + year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + month = Integer.parseInt(timestampValue.substring(2, 4)); + day = Integer.parseInt(timestampValue.substring(4, 6)); + hour = Integer.parseInt(timestampValue.substring(6, 8)); + minutes = Integer.parseInt(timestampValue.substring(8, 10)); + + year += 1900; // two-digit year + } + + break; + } + + case 8: { + if (timestampValue.indexOf(":") != -1) { + hour = Integer.parseInt(timestampValue.substring(0, 2)); + minutes = Integer.parseInt(timestampValue.substring(3, 5)); + seconds = Integer.parseInt(timestampValue.substring(6, 8)); + year = 1970; + month = 1; + day = 1; + break; + } + + year = Integer.parseInt(timestampValue.substring(0, 4)); + month = Integer.parseInt(timestampValue.substring(4, 6)); + day = Integer.parseInt(timestampValue.substring(6, 8)); + + year -= 1900; + month--; + + break; + } + + case 6: { + year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + + month = Integer.parseInt(timestampValue.substring(2, 4)); + day = Integer.parseInt(timestampValue.substring(4, 6)); + + break; + } + + case 4: { + year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + + month = Integer.parseInt(timestampValue.substring(2, 4)); + + day = 1; + + break; + } + + case 2: { + year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + month = 1; + day = 1; + + break; + } + + default: + throw new java.sql.SQLException("Bad format for Timestamp '" + timestampValue + "' in column " + columnIndex + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + if (!this.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minutes, seconds, nanos); + } + + return TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, + fastTimestampCreate(sessionCalendar, year, month, day, hour, minutes, seconds, nanos), this.connection.getServerTimezoneTZ(), tz, + rollForward); + } + } catch (RuntimeException e) { + SQLException sqlEx = SQLError.createSQLException("Cannot convert value '" + timestampValue + "' from column " + columnIndex + " to TIMESTAMP.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object in the given timezone + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param tz + * the timezone to use + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + private Timestamp getTimestampInternal(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws java.sql.SQLException { + if (this.isBinaryEncoded) { + return getNativeTimestamp(columnIndex, targetCalendar, tz, rollForward); + } + + Timestamp tsVal = null; + + if (!this.useFastDateParsing) { + String timestampValue = getStringInternal(columnIndex, false); + + tsVal = getTimestampFromString(columnIndex, targetCalendar, timestampValue, tz, rollForward); + } else { + checkClosed(); + checkRowPos(); + checkColumnBounds(columnIndex); + + tsVal = this.thisRow.getTimestampFast(columnIndex - 1, targetCalendar, tz, rollForward, this.connection, this); + } + + if (tsVal == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + return tsVal; + } + + /** + * JDBC 2.0 Return the type of this result set. The type is determined based + * on the statement that created the result set. + * + * @return TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, or + * TYPE_SCROLL_SENSITIVE + * + * @exception SQLException + * if a database-access error occurs + */ + public int getType() throws SQLException { + return this.resultSetType; + } + + /** + * A column value can also be retrieved as a stream of Unicode characters. + * We implement this as a binary stream. + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Java InputStream that delivers the database column value as a + * stream of two byte Unicode characters. If the value is SQL NULL, + * then the result is null + * + * @exception SQLException + * if a database access error occurs + * + * @see getAsciiStream + * @see getBinaryStream + * @deprecated + */ + @Deprecated + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + checkRowPos(); + + return getBinaryStream(columnIndex); + } + + return getNativeBinaryStream(columnIndex); + } + + /** + * @param columnName + * + * @throws SQLException + * + * @deprecated + */ + @Deprecated + public InputStream getUnicodeStream(String columnName) throws SQLException { + return getUnicodeStream(findColumn(columnName)); + } + + public long getUpdateCount() { + return this.updateCount; + } + + public long getUpdateID() { + return this.updateId; + } + + /** + * @see ResultSetInternalMethods#getURL(int) + */ + public URL getURL(int colIndex) throws SQLException { + String val = getString(colIndex); + + if (val == null) { + return null; + } + + try { + return new URL(val); + } catch (MalformedURLException mfe) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Malformed_URL____104") + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + /** + * @see ResultSetInternalMethods#getURL(String) + */ + public URL getURL(String colName) throws SQLException { + String val = getString(colName); + + if (val == null) { + return null; + } + + try { + return new URL(val); + } catch (MalformedURLException mfe) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Malformed_URL____107") + val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + /** + * The first warning reported by calls on this ResultSet is returned. + * Subsequent ResultSet warnings will be chained to this + * java.sql.SQLWarning. + * + *

    + * The warning chain is automatically cleared each time a new row is read. + *

    + * + *

    + * Note: This warning chain only covers warnings caused by ResultSet methods. Any warnings caused by statement methods (such as reading OUT + * parameters) will be chained on the Statement object. + *

    + * + * @return the first java.sql.SQLWarning or null; + * + * @exception SQLException + * if a database access error occurs. + */ + public java.sql.SQLWarning getWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.warningChain; + } + } + + /** + * JDBC 2.0 Insert the contents of the insert row into the result set and + * the database. Must be on the insert row when this method is called. + * + * @exception SQLException + * if a database-access error occurs, if called when not on + * the insert row, or if all non-nullable columns in the + * insert row have not been given a value + * @throws NotUpdatable + */ + public void insertRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is after the last row in the result set. + *

    + * + * @return true if after the last row, false otherwise. Returns false when + * the result set contains no rows. + * + * @exception SQLException + * if a database-access error occurs. + */ + public boolean isAfterLast() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + boolean b = this.rowData.isAfterLast(); + + return b; + } + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is before the first row in the result set. + *

    + * + * @return true if before the first row, false otherwise. Returns false when + * the result set contains no rows. + * + * @exception SQLException + * if a database-access error occurs. + */ + public boolean isBeforeFirst() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.rowData.isBeforeFirst(); + } + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is on the first row of the result set. + *

    + * + * @return true if on the first row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs. + */ + public boolean isFirst() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.rowData.isFirst(); + } + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is on the last row of the result set. Note: Calling isLast() may be expensive since the JDBC driver might need to fetch ahead one + * row in order to determine whether the current row is the last row in the result set. + *

    + * + * @return true if on the last row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs. + */ + public boolean isLast() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.rowData.isLast(); + } + } + + /** + * @param string + * @param mysqlType + * @param s + */ + private void issueConversionViaParsingWarning(String methodName, int columnIndex, Object value, Field fieldInfo, int[] typesWithNoParseConversion) + throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + StringBuilder originalQueryBuf = new StringBuilder(); + + if (this.owningStatement != null && this.owningStatement instanceof com.mysql.jdbc.PreparedStatement) { + originalQueryBuf.append(Messages.getString("ResultSet.CostlyConversionCreatedFromQuery")); + originalQueryBuf.append(((com.mysql.jdbc.PreparedStatement) this.owningStatement).originalSql); + originalQueryBuf.append("\n\n"); + } else { + originalQueryBuf.append("."); + } + + StringBuilder convertibleTypesBuf = new StringBuilder(); + + for (int i = 0; i < typesWithNoParseConversion.length; i++) { + convertibleTypesBuf.append(MysqlDefs.typeToName(typesWithNoParseConversion[i])); + convertibleTypesBuf.append("\n"); + } + + String message = Messages.getString("ResultSet.CostlyConversion", + new Object[] { methodName, Integer.valueOf(columnIndex + 1), fieldInfo.getOriginalName(), fieldInfo.getOriginalTableName(), + originalQueryBuf.toString(), + value != null ? value.getClass().getName() + : ResultSetMetaData.getClassNameForJavaType(fieldInfo.getSQLType(), fieldInfo.isUnsigned(), fieldInfo.getMysqlType(), + fieldInfo.isBinary() || fieldInfo.isBlob(), fieldInfo.isOpaqueBinary(), this.connection.getYearIsDateType()), + MysqlDefs.typeToName(fieldInfo.getMysqlType()), convertibleTypesBuf.toString() }); + + this.eventSink + .consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, System.currentTimeMillis(), + 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the last row in the result set. + *

    + * + * @return true if on a valid row, false if no rows in the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + public boolean last() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + boolean b = true; + + if (this.rowData.size() == 0) { + b = false; + } else { + + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + this.rowData.beforeLast(); + this.thisRow = this.rowData.next(); + } + + setRowPositionValidity(); + + return b; + } + } + + // ///////////////////////////////////////// + // + // These number conversion routines save + // a ton of "new()s", especially for the heavily + // used getInt() and getDouble() methods + // + // ///////////////////////////////////////// + + /** + * JDBC 2.0 Move the cursor to the remembered cursor position, usually the + * current row. Has no effect unless the cursor is on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or the result set is + * not updatable + * @throws NotUpdatable + */ + public void moveToCurrentRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Move to the insert row. The current cursor position is + * remembered while the cursor is positioned on the insert row. The insert + * row is a special row associated with an updatable result set. It is + * essentially a buffer where a new row may be constructed by calling the + * updateXXX() methods prior to inserting the row into the result set. Only + * the updateXXX(), getXXX(), and insertRow() methods may be called when the + * cursor is on the insert row. All of the columns in a result set must be + * given a value each time this method is called before calling insertRow(). + * UpdateXXX()must be called before getXXX() on a column. + * + * @exception SQLException + * if a database-access error occurs, or the result set is + * not updatable + * @throws NotUpdatable + */ + public void moveToInsertRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * A ResultSet is initially positioned before its first row, the first call + * to next makes the first row the current row; the second call makes the + * second row the current row, etc. + * + *

    + * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read + *

    + * + * @return true if the new current is valid; false if there are no more rows + * + * @exception SQLException + * if a database access error occurs + */ + public boolean next() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + boolean b; + + if (!reallyResult()) { + throw SQLError.createSQLException(Messages.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"), SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + if (this.rowData.size() == 0) { + b = false; + } else { + this.thisRow = this.rowData.next(); + + if (this.thisRow == null) { + b = false; + } else { + clearWarnings(); + + b = true; + + } + } + + setRowPositionValidity(); + + return b; + } + } + + private int parseIntAsDouble(int columnIndex, String val) throws NumberFormatException, SQLException { + if (val == null) { + return 0; + } + + double valueAsDouble = Double.parseDouble(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Integer.MIN_VALUE || valueAsDouble > Integer.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex, Types.INTEGER); + } + } + + return (int) valueAsDouble; + } + + private int getIntWithOverflowCheck(int columnIndex) throws SQLException { + int intValue = this.thisRow.getInt(columnIndex); + + checkForIntegerTruncation(columnIndex, null, intValue); + + return intValue; + } + + private void checkForIntegerTruncation(int columnIndex, byte[] valueAsBytes, int intValue) throws SQLException { + if (this.jdbcCompliantTruncationForReads) { + if (intValue == Integer.MIN_VALUE || intValue == Integer.MAX_VALUE) { + String valueAsString = null; + + if (valueAsBytes == null) { + valueAsString = this.thisRow.getString(columnIndex, this.fields[columnIndex].getEncoding(), this.connection); + } + + long valueAsLong = Long.parseLong(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString); + + if (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE) { + throwRangeException(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString, columnIndex + 1, Types.INTEGER); + } + } + } + } + + private long parseLongAsDouble(int columnIndexZeroBased, String val) throws NumberFormatException, SQLException { + if (val == null) { + return 0; + } + + double valueAsDouble = Double.parseDouble(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Long.MIN_VALUE || valueAsDouble > Long.MAX_VALUE) { + throwRangeException(val, columnIndexZeroBased + 1, Types.BIGINT); + } + } + + return (long) valueAsDouble; + } + + private long getLongWithOverflowCheck(int columnIndexZeroBased, boolean doOverflowCheck) throws SQLException { + long longValue = this.thisRow.getLong(columnIndexZeroBased); + + if (doOverflowCheck) { + checkForLongTruncation(columnIndexZeroBased, null, longValue); + } + + return longValue; + } + + private long parseLongWithOverflowCheck(int columnIndexZeroBased, byte[] valueAsBytes, String valueAsString, boolean doCheck) + throws NumberFormatException, SQLException { + + long longValue = 0; + + if (valueAsBytes == null && valueAsString == null) { + return 0; + } + + if (valueAsBytes != null) { + longValue = StringUtils.getLong(valueAsBytes); + } else { + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace is + // present + // + + valueAsString = valueAsString.trim(); + + longValue = Long.parseLong(valueAsString); + } + + if (doCheck && this.jdbcCompliantTruncationForReads) { + checkForLongTruncation(columnIndexZeroBased, valueAsBytes, longValue); + } + + return longValue; + } + + private void checkForLongTruncation(int columnIndexZeroBased, byte[] valueAsBytes, long longValue) throws SQLException { + if (longValue == Long.MIN_VALUE || longValue == Long.MAX_VALUE) { + String valueAsString = null; + + if (valueAsBytes == null) { + valueAsString = this.thisRow.getString(columnIndexZeroBased, this.fields[columnIndexZeroBased].getEncoding(), this.connection); + } + + double valueAsDouble = Double.parseDouble(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString); + + if (valueAsDouble < Long.MIN_VALUE || valueAsDouble > Long.MAX_VALUE) { + throwRangeException(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString, columnIndexZeroBased + 1, Types.BIGINT); + } + } + } + + private short parseShortAsDouble(int columnIndex, String val) throws NumberFormatException, SQLException { + if (val == null) { + return 0; + } + + double valueAsDouble = Double.parseDouble(val); + + if (this.jdbcCompliantTruncationForReads) { + if (valueAsDouble < Short.MIN_VALUE || valueAsDouble > Short.MAX_VALUE) { + throwRangeException(String.valueOf(valueAsDouble), columnIndex, Types.SMALLINT); + } + } + + return (short) valueAsDouble; + } + + private short parseShortWithOverflowCheck(int columnIndex, byte[] valueAsBytes, String valueAsString) throws NumberFormatException, SQLException { + + short shortValue = 0; + + if (valueAsBytes == null && valueAsString == null) { + return 0; + } + + if (valueAsBytes != null) { + shortValue = StringUtils.getShort(valueAsBytes); + } else { + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other than the iteration over the string, as String.trim() will return a new string only if whitespace is + // present + // + + valueAsString = valueAsString.trim(); + + shortValue = Short.parseShort(valueAsString); + } + + if (this.jdbcCompliantTruncationForReads) { + if (shortValue == Short.MIN_VALUE || shortValue == Short.MAX_VALUE) { + long valueAsLong = Long.parseLong(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString); + + if (valueAsLong < Short.MIN_VALUE || valueAsLong > Short.MAX_VALUE) { + throwRangeException(valueAsString == null ? StringUtils.toString(valueAsBytes) : valueAsString, columnIndex, Types.SMALLINT); + } + } + } + + return shortValue; + } + + // --------------------------JDBC 2.0----------------------------------- + // --------------------------------------------------------------------- + // Getter's and Setter's + // --------------------------------------------------------------------- + + /** + * The prev method is not part of JDBC, but because of the architecture of + * this driver it is possible to move both forward and backward within the + * result set. + * + *

    + * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read + *

    + * + * @return true if the new current is valid; false if there are no more rows + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + public boolean prev() throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + int rowIndex = this.rowData.getCurrentRowNumber(); + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + boolean b = true; + + if ((rowIndex - 1) >= 0) { + rowIndex--; + this.rowData.setCurrentRow(rowIndex); + this.thisRow = this.rowData.getAt(rowIndex); + + b = true; + } else if ((rowIndex - 1) == -1) { + rowIndex--; + this.rowData.setCurrentRow(rowIndex); + this.thisRow = null; + + b = false; + } else { + b = false; + } + + setRowPositionValidity(); + + return b; + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the previous row in the result set. + *

    + * + *

    + * Note: previous() is not the same as relative(-1) since it makes sense to call previous() when there is no current row. + *

    + * + * @return true if on a valid row, false if off the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWAR_DONLY. + */ + public boolean previous() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.onInsertRow) { + this.onInsertRow = false; + } + + if (this.doingUpdates) { + this.doingUpdates = false; + } + + return prev(); + } + } + + /** + * Closes this ResultSet and releases resources. + * + * @param calledExplicitly + * was realClose called by the standard ResultSet.close() method, or was it closed internally by the + * driver? + * + * @throws SQLException + * if an error occurs + */ + public void realClose(boolean calledExplicitly) throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + // additional check in case ResultSet was closed + // while current thread was waiting for lock + if (this.isClosed) { + return; + } + + try { + if (this.useUsageAdvisor) { + + // Report on result set closed by driver instead of application + + if (!calledExplicitly) { + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", + (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, this.connectionId, + (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, System.currentTimeMillis(), 0, + Constants.MILLIS_I18N, null, this.pointOfOrigin, Messages.getString("ResultSet.ResultSet_implicitly_closed_by_driver"))); + } + + if (this.rowData instanceof RowDataStatic) { + + // Report on possibly too-large result sets + + if (this.rowData.size() > this.connection.getResultSetSizeThreshold()) { + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", + (this.owningStatement == null) ? Messages.getString("ResultSet.N/A_159") : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, + Messages.getString("ResultSet.Too_Large_Result_Set", new Object[] { Integer.valueOf(this.rowData.size()), + Integer.valueOf(this.connection.getResultSetSizeThreshold()) }))); + } + + if (!isLast() && !isAfterLast() && (this.rowData.size() != 0)) { + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", + (this.owningStatement == null) ? Messages.getString("ResultSet.N/A_159") : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, + Messages.getString("ResultSet.Possible_incomplete_traversal_of_result_set", + new Object[] { Integer.valueOf(getRow()), Integer.valueOf(this.rowData.size()) }))); + } + } + + // + // Report on any columns that were selected but not referenced + // + + if (this.columnUsed.length > 0 && !this.rowData.wasEmpty()) { + StringBuilder buf = new StringBuilder(Messages.getString("ResultSet.The_following_columns_were_never_referenced")); + + boolean issueWarn = false; + + for (int i = 0; i < this.columnUsed.length; i++) { + if (!this.columnUsed[i]) { + if (!issueWarn) { + issueWarn = true; + } else { + buf.append(", "); + } + + buf.append(this.fields[i].getFullName()); + } + } + + if (issueWarn) { + this.eventSink.consumeEvent( + new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), 0, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, buf.toString())); + } + } + } + } finally { + if (this.owningStatement != null && calledExplicitly) { + this.owningStatement.removeOpenResultSet(this); + } + + SQLException exceptionDuringClose = null; + + if (this.rowData != null) { + try { + this.rowData.close(); + } catch (SQLException sqlEx) { + exceptionDuringClose = sqlEx; + } + } + + if (this.statementUsedForFetchingRows != null) { + try { + this.statementUsedForFetchingRows.realClose(true, false); + } catch (SQLException sqlEx) { + if (exceptionDuringClose != null) { + exceptionDuringClose.setNextException(sqlEx); + } else { + exceptionDuringClose = sqlEx; + } + } + } + + this.rowData = null; + this.fields = null; + this.columnLabelToIndex = null; + this.fullColumnNameToIndex = null; + this.columnToIndexCache = null; + this.eventSink = null; + this.warningChain = null; + + if (!this.retainOwningStatement) { + this.owningStatement = null; + } + + this.catalog = null; + this.serverInfo = null; + this.thisRow = null; + this.fastDefaultCal = null; + this.fastClientCal = null; + this.connection = null; + + this.isClosed = true; + + if (exceptionDuringClose != null) { + throw exceptionDuringClose; + } + } + } + } + + /** + * Returns true if this ResultSet is closed. + */ + public boolean isClosed() throws SQLException { + return this.isClosed; + } + + public boolean reallyResult() { + if (this.rowData != null) { + return true; + } + + return this.reallyResult; + } + + /** + * JDBC 2.0 Refresh the value of the current row with its current value in + * the database. Cannot be called when on the insert row. The refreshRow() + * method provides a way for an application to explicitly tell the JDBC + * driver to refetch a row(s) from the database. An application may want to + * call refreshRow() when caching or prefetching is being done by the JDBC + * driver to fetch the latest value of a row from the database. The JDBC + * driver may actually refresh multiple rows at once if the fetch size is + * greater than one. All values are refetched subject to the transaction + * isolation level and cursor sensitivity. If refreshRow() is called after + * calling updateXXX(), but before calling updateRow() then the updates made + * to the row are lost. Calling refreshRow() frequently will likely slow + * performance. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws NotUpdatable + */ + public void refreshRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 + * + *

    + * Moves a relative number of rows, either positive or negative. Attempting to move beyond the first/last row in the result set positions the cursor + * before/after the the first/last row. Calling relative(0) is valid, but does not change the cursor position. + *

    + * + *

    + * Note: Calling relative(1) is different than calling next() since is makes sense to call next() when there is no current row, for example, when the cursor + * is positioned before the first row or after the last row of the result set. + *

    + * + * @param rows + * the number of relative rows to move the cursor. + * + * @return true if on a row, false otherwise. + * + * @throws SQLException + * if a database-access error occurs, or there is no current + * row, or result set type is TYPE_FORWARD_ONLY. + */ + public boolean relative(int rows) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.rowData.size() == 0) { + setRowPositionValidity(); + + return false; + } + + if (this.thisRow != null) { + this.thisRow.closeOpenStreams(); + } + + this.rowData.moveRowRelative(rows); + this.thisRow = this.rowData.getAt(this.rowData.getCurrentRowNumber()); + + setRowPositionValidity(); + + return (!this.rowData.isAfterLast() && !this.rowData.isBeforeFirst()); + } + } + + /** + * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave + * a visible "hole" in a result set. This method can be used to detect holes + * in a result set. The value returned depends on whether or not the result + * set can detect deletions. + * + * @return true if deleted and deletes are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#deletesAreDetected + */ + public boolean rowDeleted() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Determine if the current row has been inserted. The value + * returned depends on whether or not the result set can detect visible + * inserts. + * + * @return true if inserted and inserts are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#insertsAreDetected + */ + public boolean rowInserted() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Determine if the current row has been updated. The value + * returned depends on whether or not the result set can detect updates. + * + * @return true if the row has been visibly updated by the owner or another, + * and updates are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#updatesAreDetected + */ + public boolean rowUpdated() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Flag that this result set is 'binary' encoded (from a PreparedStatement), + * not stored as strings. + */ + protected void setBinaryEncoded() { + this.isBinaryEncoded = true; + } + + /** + * JDBC 2.0 Give a hint as to the direction in which the rows in this result + * set will be processed. The initial value is determined by the statement + * that produced the result set. The fetch direction may be changed at any + * time. + * + * @param direction + * the direction to fetch rows in. + * + * @exception SQLException + * if a database-access error occurs, or the result set type + * is TYPE_FORWARD_ONLY and direction is not FETCH_FORWARD. + * MM.MySQL actually ignores this, because it has the whole + * result set anyway, so the direction is immaterial. + */ + public void setFetchDirection(int direction) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE) && (direction != FETCH_UNKNOWN)) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Illegal_value_for_fetch_direction_64"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + this.fetchDirection = direction; + } + } + + /** + * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should + * be fetched from the database when more rows are needed for this result + * set. If the fetch size specified is zero, then the JDBC driver ignores + * the value, and is free to make its own best guess as to what the fetch + * size should be. The default value is set by the statement that creates + * the result set. The fetch size may be changed at any time. + * + * @param rows + * the number of rows to fetch + * + * @exception SQLException + * if a database-access error occurs, or the condition 0 lteq + * rows lteq this.getMaxRows() is not satisfied. Currently + * ignored by this driver. + */ + public void setFetchSize(int rows) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (rows < 0) { /* || rows > getMaxRows() */ + throw SQLError.createSQLException(Messages.getString("ResultSet.Value_must_be_between_0_and_getMaxRows()_66"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.fetchSize = rows; + } + } + + /** + * Sets the first character of the query that this result set was created + * from. + * + * @param c + * the first character of the query...uppercased + */ + public void setFirstCharOfQuery(char c) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.firstCharOfQuery = c; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + /** + * @param nextResultSet + * Sets the next result set in the result set chain for multiple + * result sets. + */ + protected synchronized void setNextResultSet(ResultSetInternalMethods nextResultSet) { + this.nextResultSet = nextResultSet; + } + + public void setOwningStatement(com.mysql.jdbc.StatementImpl owningStatement) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.owningStatement = owningStatement; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + /** + * Sets the concurrency (JDBC2) + * + * @param concurrencyFlag + * CONCUR_UPDATABLE or CONCUR_READONLY + */ + protected synchronized void setResultSetConcurrency(int concurrencyFlag) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.resultSetConcurrency = concurrencyFlag; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + /** + * Sets the result set type for (JDBC2) + * + * @param typeFlag + * SCROLL_SENSITIVE or SCROLL_INSENSITIVE (we only support + * SCROLL_INSENSITIVE) + */ + protected synchronized void setResultSetType(int typeFlag) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.resultSetType = typeFlag; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + /** + * Sets server info (if any) + * + * @param info + * the server info message + */ + protected void setServerInfo(String info) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.serverInfo = info; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + public synchronized void setStatementUsedForFetchingRows(PreparedStatement stmt) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.statementUsedForFetchingRows = stmt; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + /** + * @param wrapperStatement + * The wrapperStatement to set. + */ + public synchronized void setWrapperStatement(java.sql.Statement wrapperStatement) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.wrapperStatement = wrapperStatement; + } + } catch (SQLException e) { + throw new RuntimeException(e); // FIXME: Need to evolve public interface + } + } + + private void throwRangeException(String valueAsString, int columnIndex, int jdbcType) throws SQLException { + String datatype = null; + + switch (jdbcType) { + case Types.TINYINT: + datatype = "TINYINT"; + break; + case Types.SMALLINT: + datatype = "SMALLINT"; + break; + case Types.INTEGER: + datatype = "INTEGER"; + break; + case Types.BIGINT: + datatype = "BIGINT"; + break; + case Types.REAL: + datatype = "REAL"; + break; + case Types.FLOAT: + datatype = "FLOAT"; + break; + case Types.DOUBLE: + datatype = "DOUBLE"; + break; + case Types.DECIMAL: + datatype = "DECIMAL"; + break; + default: + datatype = " (JDBC type '" + jdbcType + "')"; + } + + throw SQLError.createSQLException("'" + valueAsString + "' in column '" + columnIndex + "' is outside valid range for the datatype " + datatype + ".", + SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, getExceptionInterceptor()); + } + + @Override + public String toString() { + if (this.reallyResult) { + return super.toString(); + } + + return "Result set representing update count of " + this.updateCount; + } + + /** + * @see ResultSetInternalMethods#updateArray(int, Array) + */ + public void updateArray(int arg0, Array arg1) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see ResultSetInternalMethods#updateArray(String, Array) + */ + public void updateArray(String arg0, Array arg1) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException { + updateAsciiStream(findColumn(columnName), x, length); + } + + /** + * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { + updateBigDecimal(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a binary stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a binary stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException { + updateBinaryStream(findColumn(columnName), x, length); + } + + /** + * @see ResultSetInternalMethods#updateBlob(int, Blob) + */ + public void updateBlob(int arg0, java.sql.Blob arg1) throws SQLException { + throw new NotUpdatable(); + } + + /** + * @see ResultSetInternalMethods#updateBlob(String, Blob) + */ + public void updateBlob(String arg0, java.sql.Blob arg1) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateBoolean(String columnName, boolean x) throws SQLException { + updateBoolean(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateByte(int columnIndex, byte x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateByte(String columnName, byte x) throws SQLException { + updateByte(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateBytes(String columnName, byte[] x) throws SQLException { + updateBytes(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param reader + * the stream to update the column with + * @param length + * of the stream + * + * @throws SQLException + * if a database-access error occurs + */ + public void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { + updateCharacterStream(findColumn(columnName), reader, length); + } + + /** + * @see ResultSetInternalMethods#updateClob(int, Clob) + */ + public void updateClob(int arg0, java.sql.Clob arg1) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see ResultSetInternalMethods#updateClob(String, Clob) + */ + public void updateClob(String columnName, java.sql.Clob clob) throws SQLException { + updateClob(findColumn(columnName), clob); + } + + /** + * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateDate(String columnName, java.sql.Date x) throws SQLException { + updateDate(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Double value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateDouble(int columnIndex, double x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a double value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateDouble(String columnName, double x) throws SQLException { + updateDouble(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a float value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateFloat(int columnIndex, float x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a float value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateFloat(String columnName, float x) throws SQLException { + updateFloat(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with an integer value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateInt(int columnIndex, int x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with an integer value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateInt(String columnName, int x) throws SQLException { + updateInt(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a long value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateLong(int columnIndex, long x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a long value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateLong(String columnName, long x) throws SQLException { + updateLong(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Give a nullable column a null value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateNull(int columnIndex) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a null value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateNull(String columnName) throws SQLException { + updateNull(findColumn(columnName)); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateObject(int columnIndex, Object x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateObject(int columnIndex, Object x, int scale) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateObject(String columnName, Object x) throws SQLException { + updateObject(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param scale + * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateObject(String columnName, Object x, int scale) throws SQLException { + updateObject(findColumn(columnName), x); + } + + /** + * @see ResultSetInternalMethods#updateRef(int, Ref) + */ + public void updateRef(int arg0, Ref arg1) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see ResultSetInternalMethods#updateRef(String, Ref) + */ + public void updateRef(String arg0, Ref arg1) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Update the underlying database with the new contents of the + * current row. Cannot be called when on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row + * @throws NotUpdatable + */ + public void updateRow() throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a short value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateShort(int columnIndex, short x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a short value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateShort(String columnName, short x) throws SQLException { + updateShort(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a String value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateString(int columnIndex, String x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a String value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateString(String columnName, String x) throws SQLException { + updateString(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateTime(int columnIndex, java.sql.Time x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateTime(String columnName, java.sql.Time x) throws SQLException { + updateTime(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + * @throws NotUpdatable + */ + public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException { + throw new NotUpdatable(); + } + + /** + * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { + updateTimestamp(findColumn(columnName), x); + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether the + * last column read had this special value. Note that you must first call + * getXXX on a column to try to read its value and then call wasNull() to + * find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * + * @exception SQLException + * if a database access error occurred + */ + public boolean wasNull() throws SQLException { + return this.wasNullFlag; + } + + protected Calendar getGmtCalendar() { + // Worst case we allocate this twice and the other gets GC'd, + // however prevents deadlock + if (this.gmtCalendar == null) { + this.gmtCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + } + + return this.gmtCalendar; + } + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetInternalMethods.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetInternalMethods.java new file mode 100644 index 0000000..0bd208e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetInternalMethods.java @@ -0,0 +1,176 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * This interface is intended to be used by implementors of statement interceptors so that implementors can create static or dynamic (via + * java.lang.reflect.Proxy) proxy instances of ResultSets. It consists of methods outside of java.sql.Result that are used internally by other classes in the + * driver. + * + * This interface, although public is not designed to be consumed publicly other than for the statement interceptor use case. + */ +public interface ResultSetInternalMethods extends java.sql.ResultSet { + + /** + * Returns a new instance of this result set, that shares the + * underlying row data. + */ + public abstract ResultSetInternalMethods copy() throws SQLException; + + /** + * Does the result set contain rows, or is it the result of a DDL or DML + * statement? + */ + public abstract boolean reallyResult(); + + /** + * Functions like ResultSet.getObject(), but using the given SQL type + * (as registered during CallableStatement.registerOutParameter()). + */ + public abstract Object getObjectStoredProc(int columnIndex, int desiredSqlType) throws SQLException; + + /** + * Functions like ResultSet.getObject(), but using the given SQL type + * (as registered during CallableStatement.registerOutParameter()). + */ + public abstract Object getObjectStoredProc(int i, java.util.Map map, int desiredSqlType) throws SQLException; + + /** + * Functions like ResultSet.getObject(), but using the given SQL type + * (as registered during CallableStatement.registerOutParameter()). + */ + public abstract Object getObjectStoredProc(String columnName, int desiredSqlType) throws SQLException; + + /** + * Functions like ResultSet.getObject(), but using the given SQL type + * (as registered during CallableStatement.registerOutParameter()). + */ + public abstract Object getObjectStoredProc(String colName, java.util.Map map, int desiredSqlType) throws SQLException; + + /** + * Returns the server informational message returned from a DDL or DML + * statement (if any), or null if none. + */ + public String getServerInfo(); + + /** + * Returns the update count for this result set (if one exists), otherwise + * -1. + * + * @ return the update count for this result set (if one exists), otherwise + * -1. + */ + public long getUpdateCount(); + + /** + * Returns the AUTO_INCREMENT value for the DDL/DML statement which created + * this result set. + * + * @return the AUTO_INCREMENT value for the DDL/DML statement which created + * this result set. + */ + public long getUpdateID(); + + /** + * Closes this ResultSet and releases resources. + * + * @param calledExplicitly + * was realClose called by the standard ResultSet.close() method, or was it closed internally by the + * driver? + */ + public void realClose(boolean calledExplicitly) throws SQLException; + + /** + * Returns true if this ResultSet is closed + */ + public boolean isClosed() throws SQLException; + + /** + * Sets the first character of the query that was issued to create + * this result set. The character should be upper-cased. + */ + public void setFirstCharOfQuery(char firstCharUpperCase); + + /** + * Sets the statement that "owns" this result set (usually used when the + * result set should internally "belong" to one statement, but is created + * by another. + */ + public void setOwningStatement(com.mysql.jdbc.StatementImpl owningStatement); + + /** + * Returns the first character of the query that was issued to create this + * result set, upper-cased. + */ + public char getFirstCharOfQuery(); + + /** + * Clears the reference to the next result set in a multi-result set + * "chain". + */ + public void clearNextResult(); + + /** + * Returns the next ResultSet in a multi-resultset "chain", if any, + * null if none exists. + */ + public ResultSetInternalMethods getNextResultSet(); + + public void setStatementUsedForFetchingRows(PreparedStatement stmt); + + /** + * @param wrapperStatement + * The wrapperStatement to set. + */ + public void setWrapperStatement(java.sql.Statement wrapperStatement); + + /** + * Builds a hash between column names and their indices for fast retrieval. + * This is done lazily to support findColumn() and get*(String), as it + * can be more expensive than just retrieving result set values by ordinal + * index. + */ + public void buildIndexMapping() throws SQLException; + + public void initializeWithMetadata() throws SQLException; + + /** + * Used by DatabaseMetadata implementations to coerce the metadata returned + * by metadata queries into that required by the JDBC specification. + * + * @param metadataFields + * the coerced metadata to be applied to result sets + * returned by "SHOW ..." or SELECTs on INFORMATION_SCHEMA performed on behalf + * of methods in DatabaseMetadata. + */ + public void redefineFieldsForDBMD(Field[] metadataFields); + + public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData) throws SQLException; + + public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData); + + public int getBytesSize() throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetMetaData.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetMetaData.java new file mode 100644 index 0000000..27f3e86 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetMetaData.java @@ -0,0 +1,819 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * A ResultSetMetaData object can be used to find out about the types and properties of the columns in a ResultSet + */ +public class ResultSetMetaData implements java.sql.ResultSetMetaData { + private static int clampedGetLength(Field f) { + long fieldLength = f.getLength(); + + if (fieldLength > Integer.MAX_VALUE) { + fieldLength = Integer.MAX_VALUE; + } + + return (int) fieldLength; + } + + /** + * Checks if the SQL Type is a Decimal/Number Type + * + * @param type + * SQL Type + */ + private static final boolean isDecimalType(int type) { + switch (type) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: + return true; + } + + return false; + } + + Field[] fields; + boolean useOldAliasBehavior = false; + boolean treatYearAsDate = true; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Initialize for a result with a tuple set and a field descriptor set + * + * @param fields + * the array of field descriptors + */ + public ResultSetMetaData(Field[] fields, boolean useOldAliasBehavior, boolean treatYearAsDate, ExceptionInterceptor exceptionInterceptor) { + this.fields = fields; + this.useOldAliasBehavior = useOldAliasBehavior; + this.treatYearAsDate = treatYearAsDate; + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * What's a column's table's catalog name? + * + * @param column + * the first column is 1, the second is 2... + * + * @return catalog name, or "" if not applicable + * + * @throws SQLException + * if a database access error occurs + */ + public String getCatalogName(int column) throws SQLException { + Field f = getField(column); + + String database = f.getDatabaseName(); + + return (database == null) ? "" : database; + } + + /** + * What's the Java character encoding name for the given column? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the Java character encoding name for the given column, or null if + * no Java character encoding maps to the MySQL character set for + * the given column. + * + * @throws SQLException + * if an invalid column index is given. + */ + public String getColumnCharacterEncoding(int column) throws SQLException { + String mysqlName = getColumnCharacterSet(column); + + String javaName = null; + + if (mysqlName != null) { + try { + javaName = CharsetMapping.getJavaEncodingForMysqlCharset(mysqlName); + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + } + + return javaName; + } + + /** + * What's the MySQL character set name for the given column? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the MySQL character set name for the given column + * + * @throws SQLException + * if an invalid column index is given. + */ + public String getColumnCharacterSet(int column) throws SQLException { + return getField(column).getEncoding(); + } + + // --------------------------JDBC 2.0----------------------------------- + + /** + * JDBC 2.0 + * + *

    + * Return the fully qualified name of the Java class whose instances are manufactured if ResultSet.getObject() is called to retrieve a value from the + * column. ResultSet.getObject() may return a subClass of the class returned by this method. + *

    + * + * @param column + * the column number to retrieve information for + * + * @return the fully qualified name of the Java class whose instances are + * manufactured if ResultSet.getObject() is called to retrieve a + * value from the column. + * + * @throws SQLException + * if an error occurs + */ + public String getColumnClassName(int column) throws SQLException { + Field f = getField(column); + + return getClassNameForJavaType(f.getSQLType(), f.isUnsigned(), f.getMysqlType(), f.isBinary() || f.isBlob(), f.isOpaqueBinary(), this.treatYearAsDate); + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * + * @throws SQLException + * if a database access error occurs + */ + public int getColumnCount() throws SQLException { + return this.fields.length; + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the maximum width + * + * @throws SQLException + * if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException { + Field f = getField(column); + + int lengthInBytes = clampedGetLength(f); + + return lengthInBytes / f.getMaxBytesPerCharacter(); + } + + /** + * What is the suggested column title for use in printouts and displays? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the column label + * + * @throws SQLException + * if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException { + if (this.useOldAliasBehavior) { + return getColumnName(column); + } + + return getField(column).getColumnLabel(); + } + + /** + * What's a column's name? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the column name + * + * @throws SQLException + * if a databvase access error occurs + */ + public String getColumnName(int column) throws SQLException { + if (this.useOldAliasBehavior) { + return getField(column).getName(); + } + + String name = getField(column).getNameNoAliases(); + + if (name != null && name.length() == 0) { + return getField(column).getName(); + } + + return name; + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the java.sql.Type value + * + * @throws SQLException + * if a database access error occurs + * + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return the type name + * + * @throws SQLException + * if a database access error occurs + */ + public String getColumnTypeName(int column) throws java.sql.SQLException { + Field field = getField(column); + + int mysqlType = field.getMysqlType(); + int jdbcType = field.getSQLType(); + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_BIT: + return "BIT"; + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL"; + + case MysqlDefs.FIELD_TYPE_TINY: + return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT"; + + case MysqlDefs.FIELD_TYPE_SHORT: + return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT"; + + case MysqlDefs.FIELD_TYPE_LONG: + return field.isUnsigned() ? "INT UNSIGNED" : "INT"; + + case MysqlDefs.FIELD_TYPE_FLOAT: + return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT"; + + case MysqlDefs.FIELD_TYPE_DOUBLE: + return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE"; + + case MysqlDefs.FIELD_TYPE_NULL: + return "NULL"; + + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + return "TIMESTAMP"; + + case MysqlDefs.FIELD_TYPE_LONGLONG: + return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT"; + + case MysqlDefs.FIELD_TYPE_INT24: + return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT"; + + case MysqlDefs.FIELD_TYPE_DATE: + return "DATE"; + + case MysqlDefs.FIELD_TYPE_TIME: + return "TIME"; + + case MysqlDefs.FIELD_TYPE_DATETIME: + return "DATETIME"; + + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + return "TINYBLOB"; + + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + return "MEDIUMBLOB"; + + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + return "LONGBLOB"; + + case MysqlDefs.FIELD_TYPE_BLOB: + if (getField(column).isBinary()) { + return "BLOB"; + } + + return "TEXT"; + + case MysqlDefs.FIELD_TYPE_VARCHAR: + return "VARCHAR"; + + case MysqlDefs.FIELD_TYPE_VAR_STRING: + if (jdbcType == Types.VARBINARY) { + return "VARBINARY"; + } + + return "VARCHAR"; + + case MysqlDefs.FIELD_TYPE_STRING: + if (jdbcType == Types.BINARY) { + return "BINARY"; + } + + return "CHAR"; + + case MysqlDefs.FIELD_TYPE_ENUM: + return "ENUM"; + + case MysqlDefs.FIELD_TYPE_YEAR: + return "YEAR"; + + case MysqlDefs.FIELD_TYPE_SET: + return "SET"; + + case MysqlDefs.FIELD_TYPE_GEOMETRY: + return "GEOMETRY"; + + case MysqlDefs.FIELD_TYPE_JSON: + return "JSON"; + + default: + return "UNKNOWN"; + } + } + + /** + * Returns the field instance for the given column index + * + * @param columnIndex + * the column number to retrieve a field instance for + * + * @return the field instance for the given column index + * + * @throws SQLException + * if an error occurs + */ + protected Field getField(int columnIndex) throws SQLException { + if ((columnIndex < 1) || (columnIndex > this.fields.length)) { + throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, this.exceptionInterceptor); + } + + return this.fields[columnIndex - 1]; + } + + /** + * What is a column's number of decimal digits. + * + * @param column + * the first column is 1, the second is 2... + * + * @return the precision + * + * @throws SQLException + * if a database access error occurs + */ + public int getPrecision(int column) throws SQLException { + Field f = getField(column); + + // if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_NEW_DECIMAL) { + // return f.getLength(); + // } + + if (isDecimalType(f.getSQLType())) { + if (f.getDecimals() > 0) { + return clampedGetLength(f) - 1 + f.getPrecisionAdjustFactor(); + } + + return clampedGetLength(f) + f.getPrecisionAdjustFactor(); + } + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + return clampedGetLength(f); // this may change in the future for now, the server only returns FIELD_TYPE_BLOB for _all_ BLOB types, but varying + // lengths indicating the _maximum_ size for each BLOB type. + default: + return clampedGetLength(f) / f.getMaxBytesPerCharacter(); + + } + } + + /** + * What is a column's number of digits to the right of the decimal point? + * + * @param column + * the first column is 1, the second is 2... + * + * @return the scale + * + * @throws SQLException + * if a database access error occurs + */ + public int getScale(int column) throws SQLException { + Field f = getField(column); + + if (isDecimalType(f.getSQLType())) { + return f.getDecimals(); + } + + return 0; + } + + /** + * What is a column's table's schema? This relies on us knowing the table + * name. The JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column + * the first column is 1, the second is 2... + * + * @return the Schema + * + * @throws SQLException + * if a database access error occurs + */ + public String getSchemaName(int column) throws SQLException { + return ""; + } + + /** + * Whats a column's table's name? + * + * @param column + * the first column is 1, the second is 2... + * + * @return column name, or "" if not applicable + * + * @throws SQLException + * if a database access error occurs + */ + public String getTableName(int column) throws SQLException { + if (this.useOldAliasBehavior) { + return getField(column).getTableName(); + } + + return getField(column).getTableNameNoAliases(); + } + + /** + * Is the column automatically numbered (and thus read-only) + * + * @param column + * the first column is 1, the second is 2... + * + * @return true if so + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException { + Field f = getField(column); + + return f.isAutoIncrement(); + } + + /** + * Does a column's case matter? + * + * @param column + * the first column is 1, the second is 2... + * + * @return true if so + * + * @throws java.sql.SQLException + * if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws java.sql.SQLException { + Field field = getField(column); + + int sqlType = field.getSQLType(); + + switch (sqlType) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + + if (field.isBinary()) { + return true; + } + + String collationName = field.getCollation(); + + return ((collationName != null) && !collationName.endsWith("_ci")); + + default: + return true; + } + } + + /** + * Is the column a cash value? + * + * @param column + * the first column is 1, the second is 2... + * + * @return true if its a cash column + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException { + return false; + } + + /** + * Will a write on this column definately succeed? + * + * @param column + * the first column is 1, the second is 2, etc.. + * + * @return true if so + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException { + return isWritable(column); + } + + /** + * Can you put a NULL in this column? + * + * @param column + * the first column is 1, the second is 2... + * + * @return one of the columnNullable values + * + * @throws SQLException + * if a database access error occurs + */ + public int isNullable(int column) throws SQLException { + if (!getField(column).isNotNull()) { + return java.sql.ResultSetMetaData.columnNullable; + } + + return java.sql.ResultSetMetaData.columnNoNulls; + } + + /** + * Is the column definitely not writable? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return true if so + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException { + return getField(column).isReadOnly(); + } + + /** + * Can the column be used in a WHERE clause? Basically for this, I split the + * functions into two types: recognised types (which are always useable), + * and OTHER types (which may or may not be useable). The OTHER types, for + * now, I will assume they are useable. We should really query the catalog + * to see if they are useable. + * + * @param column + * the first column is 1, the second is 2... + * + * @return true if they can be used in a WHERE clause + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException { + return true; + } + + /** + * Is the column a signed number? + * + * @param column + * the first column is 1, the second is 2... + * + * @return true if so + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException { + Field f = getField(column); + int sqlType = f.getSQLType(); + + switch (sqlType) { + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: + return !f.isUnsigned(); + + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + + default: + return false; + } + } + + /** + * Is it possible for a write on the column to succeed? + * + * @param column + * the first column is 1, the second is 2, etc. + * + * @return true if so + * + * @throws SQLException + * if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException { + return !isReadOnly(column); + } + + /** + * Returns a string representation of this object + * + * @return ... + */ + @Override + public String toString() { + StringBuilder toStringBuf = new StringBuilder(); + toStringBuf.append(super.toString()); + toStringBuf.append(" - Field level information: "); + + for (int i = 0; i < this.fields.length; i++) { + toStringBuf.append("\n\t"); + toStringBuf.append(this.fields[i].toString()); + } + + return toStringBuf.toString(); + } + + static String getClassNameForJavaType(int javaType, boolean isUnsigned, int mysqlTypeIfKnown, boolean isBinaryOrBlob, boolean isOpaqueBinary, + boolean treatYearAsDate) { + switch (javaType) { + case Types.BIT: + case Types.BOOLEAN: + return "java.lang.Boolean"; + + case Types.TINYINT: + + if (isUnsigned) { + return "java.lang.Integer"; + } + + return "java.lang.Integer"; + + case Types.SMALLINT: + + if (isUnsigned) { + return "java.lang.Integer"; + } + + return "java.lang.Integer"; + + case Types.INTEGER: + + if (!isUnsigned || mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) { + return "java.lang.Integer"; + } + + return "java.lang.Long"; + + case Types.BIGINT: + + if (!isUnsigned) { + return "java.lang.Long"; + } + + return "java.math.BigInteger"; + + case Types.DECIMAL: + case Types.NUMERIC: + return "java.math.BigDecimal"; + + case Types.REAL: + return "java.lang.Float"; + + case Types.FLOAT: + case Types.DOUBLE: + return "java.lang.Double"; + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + if (!isOpaqueBinary) { + return "java.lang.String"; + } + + return "[B"; + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + + if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) { + return "[B"; + } else if (isBinaryOrBlob) { + return "[B"; + } else { + return "java.lang.String"; + } + + case Types.DATE: + return (treatYearAsDate || mysqlTypeIfKnown != MysqlDefs.FIELD_TYPE_YEAR) ? "java.sql.Date" : "java.lang.Short"; + + case Types.TIME: + return "java.sql.Time"; + + case Types.TIMESTAMP: + return "java.sql.Timestamp"; + + default: + return "java.lang.Object"; + } + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetRow.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetRow.java new file mode 100644 index 0000000..72a1017 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ResultSetRow.java @@ -0,0 +1,1246 @@ +/* + Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** + * Classes that implement this interface represent one row of data from the MySQL server that might be stored in different ways depending on whether the result + * set was streaming (so they wrap a reusable packet), or whether the result set was cached or via a server-side cursor (so they represent a byte[][]). + * + * Notice that no bounds checking is expected for implementors of this interface, it happens in ResultSetImpl. + */ +public abstract class ResultSetRow { + protected ExceptionInterceptor exceptionInterceptor; + + protected ResultSetRow(ExceptionInterceptor exceptionInterceptor) { + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * The metadata of the fields of this result set. + */ + protected Field[] metadata; + + /** + * Called during navigation to next row to close all open + * streams. + */ + public abstract void closeOpenStreams(); + + /** + * Returns data at the given index as an InputStream with no + * character conversion. + * + * @param columnIndex + * of the column value (starting at 0) to return. + * @return the value at the given index as an InputStream or null + * if null. + * + * @throws SQLException + * if an error occurs while retrieving the value. + */ + public abstract InputStream getBinaryInputStream(int columnIndex) throws SQLException; + + /** + * Returns the value at the given column (index starts at 0) "raw" (i.e. + * as-returned by the server). + * + * @param index + * of the column value (starting at 0) to return. + * @return the value for the given column (including NULL if it is) + * @throws SQLException + * if an error occurs while retrieving the value. + */ + public abstract byte[] getColumnValue(int index) throws SQLException; + + protected final java.sql.Date getDateFast(int columnIndex, byte[] dateAsBytes, int offset, int length, MySQLConnection conn, ResultSetImpl rs, + Calendar targetCalendar) throws SQLException { + + int year = 0; + int month = 0; + int day = 0; + + try { + if (dateAsBytes == null) { + return null; + } + + boolean allZeroDate = true; + + boolean onlyTimePresent = false; + + for (int i = 0; i < length; i++) { + if (dateAsBytes[offset + i] == ':') { + onlyTimePresent = true; + break; + } + } + + for (int i = 0; i < length; i++) { + byte b = dateAsBytes[offset + i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { + allZeroDate = false; + + break; + } + } + + // check for the fractional part + int decimalIndex = -1; + for (int i = 0; i < length; i++) { + if (dateAsBytes[offset + i] == '.') { + decimalIndex = i; + break; + } + } + + // ignore milliseconds + if (decimalIndex > -1) { + length = decimalIndex; + } + + if (!onlyTimePresent && allZeroDate) { + + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + StringUtils.toString(dateAsBytes) + "' can not be represented as java.sql.Date", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + // We're left with the case of 'round' to a date Java _can_ represent, which is '0001-01-01'. + return rs.fastDateCreate(targetCalendar, 1, 1, 1); + + } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + // Convert from TIMESTAMP + switch (length) { + case 29: + case 21: + case 19: { // java.sql.Timestamp format + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(dateAsBytes, offset + 5, offset + 7); + day = StringUtils.getInt(dateAsBytes, offset + 8, offset + 10); + + return rs.fastDateCreate(targetCalendar, year, month, day); + } + + case 14: + case 8: { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(dateAsBytes, offset + 4, offset + 6); + day = StringUtils.getInt(dateAsBytes, offset + 6, offset + 8); + + return rs.fastDateCreate(targetCalendar, year, month, day); + } + + case 12: + case 10: + case 6: { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = year + 100; + } + + month = StringUtils.getInt(dateAsBytes, offset + 2, offset + 4); + day = StringUtils.getInt(dateAsBytes, offset + 4, offset + 6); + + return rs.fastDateCreate(targetCalendar, year + 1900, month, day); + } + + case 4: { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); + + if (year <= 69) { + year = year + 100; + } + + month = StringUtils.getInt(dateAsBytes, offset + 2, offset + 4); + + return rs.fastDateCreate(targetCalendar, year + 1900, month, 1); + } + + case 2: { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = year + 100; + } + + return rs.fastDateCreate(targetCalendar, year + 1900, 1, 1); + } + + default: + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", + new Object[] { StringUtils.toString(dateAsBytes), Integer.valueOf(columnIndex + 1) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + if (length == 2 || length == 1) { + year = StringUtils.getInt(dateAsBytes, offset, offset + length); + + if (year <= 69) { + year = year + 100; + } + + year += 1900; + } else { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); + } + + return rs.fastDateCreate(targetCalendar, year, 1, 1); + } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) { + return rs.fastDateCreate(targetCalendar, 1970, 1, 1); // Return EPOCH + } else { + if (length < 10) { + if (length == 8) { + return rs.fastDateCreate(targetCalendar, 1970, 1, 1); // Return + // EPOCH for TIME + } + + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", + new Object[] { StringUtils.toString(dateAsBytes), Integer.valueOf(columnIndex + 1) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (length != 18) { + year = StringUtils.getInt(dateAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(dateAsBytes, offset + 5, offset + 7); + day = StringUtils.getInt(dateAsBytes, offset + 8, offset + 10); + } else { + // JDK-1.3 timestamp format, not real easy to parse positionally :p + StringTokenizer st = new StringTokenizer(StringUtils.toString(dateAsBytes, offset, length, "ISO8859_1"), "- "); + + year = Integer.parseInt(st.nextToken()); + month = Integer.parseInt(st.nextToken()); + day = Integer.parseInt(st.nextToken()); + } + } + + return rs.fastDateCreate(targetCalendar, year, month, day); + } catch (SQLException sqlEx) { + throw sqlEx; // don't re-wrap + } catch (Exception e) { + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Date", new Object[] { StringUtils.toString(dateAsBytes), Integer.valueOf(columnIndex + 1) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(e); + + throw sqlEx; + } + } + + public abstract java.sql.Date getDateFast(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar targetCalendar) throws SQLException; + + /** + * Returns the value at the given column (index starts at 0) as an int. * + * + * @param index + * of the column value (starting at 0) to return. + * @return the value for the given column (returns 0 if NULL, use isNull() + * to determine if the value was actually NULL) + * @throws SQLException + * if an error occurs while retrieving the value. + */ + public abstract int getInt(int columnIndex) throws SQLException; + + /** + * Returns the value at the given column (index starts at 0) as a long. * + * + * @param index + * of the column value (starting at 0) to return. + * @return the value for the given column (returns 0 if NULL, use isNull() + * to determine if the value was actually NULL) + * @throws SQLException + * if an error occurs while retrieving the value. + */ + public abstract long getLong(int columnIndex) throws SQLException; + + /** + * @param columnIndex + * @param bits + * @param offset + * @param length + * @param conn + * @param rs + * @param cal + * @throws SQLException + */ + protected java.sql.Date getNativeDate(int columnIndex, byte[] bits, int offset, int length, MySQLConnection conn, ResultSetImpl rs, Calendar cal) + throws SQLException { + + int year = 0; + int month = 0; + int day = 0; + + if (length != 0) { + year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); + + month = bits[offset + 2]; + day = bits[offset + 3]; + } + + if (length == 0 || ((year == 0) && (month == 0) && (day == 0))) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + year = 1; + month = 1; + day = 1; + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastDateCreate(year, month, day, cal); + } + + return rs.fastDateCreate(cal == null ? rs.getCalendarInstanceForSessionOrNew() : cal, year, month, day); + } + + public abstract Date getNativeDate(int columnIndex, MySQLConnection conn, ResultSetImpl rs, Calendar cal) throws SQLException; + + protected Object getNativeDateTimeValue(int columnIndex, byte[] bits, int offset, int length, Calendar targetCalendar, int jdbcType, int mysqlType, + TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { + + int year = 0; + int month = 0; + int day = 0; + + int hour = 0; + int minute = 0; + int seconds = 0; + + int nanos = 0; + + if (bits == null) { + + return null; + } + + Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn.getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); + + boolean populatedFromDateTimeValue = false; + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + populatedFromDateTimeValue = true; + + if (length != 0) { + year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); + month = bits[offset + 2]; + day = bits[offset + 3]; + + if (length > 4) { + hour = bits[offset + 4]; + minute = bits[offset + 5]; + seconds = bits[offset + 6]; + } + + if (length > 7) { + // MySQL uses microseconds + nanos = ((bits[offset + 7] & 0xff) | ((bits[offset + 8] & 0xff) << 8) | ((bits[offset + 9] & 0xff) << 16) + | ((bits[offset + 10] & 0xff) << 24)) * 1000; + } + } + + break; + case MysqlDefs.FIELD_TYPE_DATE: + populatedFromDateTimeValue = true; + + if (length != 0) { + year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); + month = bits[offset + 2]; + day = bits[offset + 3]; + } + + break; + case MysqlDefs.FIELD_TYPE_TIME: + populatedFromDateTimeValue = true; + + if (length != 0) { + // bits[0] // skip tm->neg + // binaryData.readLong(); // skip daysPart + hour = bits[offset + 5]; + minute = bits[offset + 6]; + seconds = bits[offset + 7]; + } + + year = 1970; + month = 1; + day = 1; + + break; + default: + populatedFromDateTimeValue = false; + } + + switch (jdbcType) { + case Types.TIME: + if (populatedFromDateTimeValue) { + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimeCreate(hour, minute, seconds, targetCalendar, this.exceptionInterceptor); + } + + Time time = TimeUtil.fastTimeCreate(rs.getCalendarInstanceForSessionOrNew(), hour, minute, seconds, this.exceptionInterceptor); + + Time adjustedTime = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, time, conn.getServerTimezoneTZ(), tz, rollForward); + + return adjustedTime; + } + + return rs.getNativeTimeViaParseConversion(columnIndex + 1, targetCalendar, tz, rollForward); + + case Types.DATE: + if (populatedFromDateTimeValue) { + if ((year == 0) && (month == 0) && (day == 0)) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw new SQLException("Value '0000-00-00' can not be represented as java.sql.Date", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + year = 1; + month = 1; + day = 1; + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastDateCreate(year, month, day, targetCalendar); + } + + return rs.fastDateCreate(rs.getCalendarInstanceForSessionOrNew(), year, month, day); + } + + return rs.getNativeDateViaParseConversion(columnIndex + 1); + case Types.TIMESTAMP: + if (populatedFromDateTimeValue) { + if ((year == 0) && (month == 0) && (day == 0)) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw new SQLException("Value '0000-00-00' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + year = 1; + month = 1; + day = 1; + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minute, seconds, nanos); + } + + Timestamp ts = rs.fastTimestampCreate(rs.getCalendarInstanceForSessionOrNew(), year, month, day, hour, minute, seconds, nanos); + + Timestamp adjustedTs = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, ts, conn.getServerTimezoneTZ(), tz, rollForward); + + return adjustedTs; + } + + return rs.getNativeTimestampViaParseConversion(columnIndex + 1, targetCalendar, tz, rollForward); + + default: + throw new SQLException("Internal error - conversion method doesn't support this type", SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + public abstract Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar, int jdbcType, int mysqlType, TimeZone tz, boolean rollForward, + MySQLConnection conn, ResultSetImpl rs) throws SQLException; + + protected double getNativeDouble(byte[] bits, int offset) { + long valueAsLong = (bits[offset + 0] & 0xff) | ((long) (bits[offset + 1] & 0xff) << 8) | ((long) (bits[offset + 2] & 0xff) << 16) + | ((long) (bits[offset + 3] & 0xff) << 24) | ((long) (bits[offset + 4] & 0xff) << 32) | ((long) (bits[offset + 5] & 0xff) << 40) + | ((long) (bits[offset + 6] & 0xff) << 48) | ((long) (bits[offset + 7] & 0xff) << 56); + + return Double.longBitsToDouble(valueAsLong); + } + + public abstract double getNativeDouble(int columnIndex) throws SQLException; + + protected float getNativeFloat(byte[] bits, int offset) { + int asInt = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8) | ((bits[offset + 2] & 0xff) << 16) | ((bits[offset + 3] & 0xff) << 24); + + return Float.intBitsToFloat(asInt); + } + + public abstract float getNativeFloat(int columnIndex) throws SQLException; + + protected int getNativeInt(byte[] bits, int offset) { + + int valueAsInt = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8) | ((bits[offset + 2] & 0xff) << 16) | ((bits[offset + 3] & 0xff) << 24); + + return valueAsInt; + } + + public abstract int getNativeInt(int columnIndex) throws SQLException; + + protected long getNativeLong(byte[] bits, int offset) { + long valueAsLong = (bits[offset + 0] & 0xff) | ((long) (bits[offset + 1] & 0xff) << 8) | ((long) (bits[offset + 2] & 0xff) << 16) + | ((long) (bits[offset + 3] & 0xff) << 24) | ((long) (bits[offset + 4] & 0xff) << 32) | ((long) (bits[offset + 5] & 0xff) << 40) + | ((long) (bits[offset + 6] & 0xff) << 48) | ((long) (bits[offset + 7] & 0xff) << 56); + + return valueAsLong; + } + + public abstract long getNativeLong(int columnIndex) throws SQLException; + + protected short getNativeShort(byte[] bits, int offset) { + short asShort = (short) ((bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8)); + + return asShort; + } + + public abstract short getNativeShort(int columnIndex) throws SQLException; + + /** + * @param columnIndex + * @param bits + * @param offset + * @param length + * @param targetCalendar + * @param tz + * @param rollForward + * @param conn + * @param rs + * @throws SQLException + */ + protected Time getNativeTime(int columnIndex, byte[] bits, int offset, int length, Calendar targetCalendar, TimeZone tz, boolean rollForward, + MySQLConnection conn, ResultSetImpl rs) throws SQLException { + + int hour = 0; + int minute = 0; + int seconds = 0; + + if (length != 0) { + // bits[0] // skip tm->neg + // binaryData.readLong(); // skip daysPart + hour = bits[offset + 5]; + minute = bits[offset + 6]; + seconds = bits[offset + 7]; + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimeCreate(hour, minute, seconds, targetCalendar, this.exceptionInterceptor); + } + + Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew(); + + Time time = TimeUtil.fastTimeCreate(sessionCalendar, hour, minute, seconds, this.exceptionInterceptor); + + Time adjustedTime = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, time, conn.getServerTimezoneTZ(), tz, rollForward); + + return adjustedTime; + } + + public abstract Time getNativeTime(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException; + + protected Timestamp getNativeTimestamp(byte[] bits, int offset, int length, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, + ResultSetImpl rs) throws SQLException { + int year = 0; + int month = 0; + int day = 0; + + int hour = 0; + int minute = 0; + int seconds = 0; + + int nanos = 0; + + if (length != 0) { + year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8); + month = bits[offset + 2]; + day = bits[offset + 3]; + + if (length > 4) { + hour = bits[offset + 4]; + minute = bits[offset + 5]; + seconds = bits[offset + 6]; + } + + if (length > 7) { + // MySQL uses microseconds + nanos = ((bits[offset + 7] & 0xff) | ((bits[offset + 8] & 0xff) << 8) | ((bits[offset + 9] & 0xff) << 16) | ((bits[offset + 10] & 0xff) << 24)) + * 1000; + } + } + + if (length == 0 || ((year == 0) && (month == 0) && (day == 0))) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Timestamp", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + year = 1; + month = 1; + day = 1; + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minute, seconds, nanos); + } + + Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn.getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); + + Timestamp ts = rs.fastTimestampCreate(sessionCalendar, year, month, day, hour, minute, seconds, nanos); + + Timestamp adjustedTs = TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, ts, conn.getServerTimezoneTZ(), tz, rollForward); + + return adjustedTs; + } + + public abstract Timestamp getNativeTimestamp(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, + ResultSetImpl rs) throws SQLException; + + public abstract Reader getReader(int columnIndex) throws SQLException; + + /** + * Returns the value at the given column (index starts at 0) as a + * java.lang.String with the requested encoding, using the given + * MySQLConnection to find character converters. + * + * @param index + * of the column value (starting at 0) to return. + * @param encoding + * the Java name for the character encoding + * @param conn + * the connection that created this result set row + * + * @return the value for the given column (including NULL if it is) as a + * String + * + * @throws SQLException + * if an error occurs while retrieving the value. + */ + public abstract String getString(int index, String encoding, MySQLConnection conn) throws SQLException; + + /** + * Convenience method for turning a byte[] into a string with the given + * encoding. + * + * @param encoding + * the Java encoding name for the byte[] -> char conversion + * @param conn + * the MySQLConnection that created the result set + * @param value + * the String value as a series of bytes, encoded using + * "encoding" + * @param offset + * where to start the decoding + * @param length + * how many bytes to decode + * + * @return the String as decoded from bytes with the given encoding + * + * @throws SQLException + * if an error occurs + */ + protected String getString(String encoding, MySQLConnection conn, byte[] value, int offset, int length) throws SQLException { + String stringVal = null; + + if ((conn != null) && conn.getUseUnicode()) { + try { + if (encoding == null) { + stringVal = StringUtils.toString(value); + } else { + SingleByteCharsetConverter converter = conn.getCharsetConverter(encoding); + + if (converter != null) { + stringVal = converter.toString(value, offset, length); + } else { + stringVal = StringUtils.toString(value, offset, length, encoding); + } + } + } catch (java.io.UnsupportedEncodingException E) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Unsupported_character_encoding____101") + encoding + "'.", "0S100", + this.exceptionInterceptor); + } + } else { + stringVal = StringUtils.toAsciiString(value, offset, length); + } + + return stringVal; + } + + protected Time getTimeFast(int columnIndex, byte[] timeAsBytes, int offset, int fullLength, Calendar targetCalendar, TimeZone tz, boolean rollForward, + MySQLConnection conn, ResultSetImpl rs) throws SQLException { + + int hr = 0; + int min = 0; + int sec = 0; + int nanos = 0; + + int decimalIndex = -1; + + try { + + if (timeAsBytes == null) { + return null; + } + + boolean allZeroTime = true; + boolean onlyTimePresent = false; + + for (int i = 0; i < fullLength; i++) { + if (timeAsBytes[offset + i] == ':') { + onlyTimePresent = true; + break; + } + } + + for (int i = 0; i < fullLength; i++) { + if (timeAsBytes[offset + i] == '.') { + decimalIndex = i; + break; + } + } + + for (int i = 0; i < fullLength; i++) { + byte b = timeAsBytes[offset + i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { + allZeroTime = false; + + break; + } + } + + if (!onlyTimePresent && allZeroTime) { + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + StringUtils.toString(timeAsBytes) + "' can not be represented as java.sql.Time", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + // We're left with the case of 'round' to a time Java _can_ represent, which is '00:00:00' + return rs.fastTimeCreate(targetCalendar, 0, 0, 0); + } + + Field timeColField = this.metadata[columnIndex]; + + int length = fullLength; + + if (decimalIndex != -1) { + + length = decimalIndex; + + if ((decimalIndex + 2) <= fullLength) { + nanos = StringUtils.getInt(timeAsBytes, offset + decimalIndex + 1, offset + fullLength); + + int numDigits = (fullLength) - (decimalIndex + 1); + + if (numDigits < 9) { + int factor = (int) (Math.pow(10, 9 - numDigits)); + nanos = nanos * factor; + } + } else { + throw new IllegalArgumentException(); // re-thrown + // further + // down + // with + // a + // much better error message + } + } + + if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + + switch (length) { + case 19: { // YYYY-MM-DD hh:mm:ss + + hr = StringUtils.getInt(timeAsBytes, offset + length - 8, offset + length - 6); + min = StringUtils.getInt(timeAsBytes, offset + length - 5, offset + length - 3); + sec = StringUtils.getInt(timeAsBytes, offset + length - 2, offset + length); + } + + break; + case 14: + case 12: { + hr = StringUtils.getInt(timeAsBytes, offset + length - 6, offset + length - 4); + min = StringUtils.getInt(timeAsBytes, offset + length - 4, offset + length - 2); + sec = StringUtils.getInt(timeAsBytes, offset + length - 2, offset + length); + } + + break; + + case 10: { + hr = StringUtils.getInt(timeAsBytes, offset + 6, offset + 8); + min = StringUtils.getInt(timeAsBytes, offset + 8, offset + 10); + sec = 0; + } + + break; + + default: + throw SQLError.createSQLException(Messages.getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") + + (columnIndex + 1) + "(" + timeColField + ").", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } /* endswitch */ + + @SuppressWarnings("unused") + SQLWarning precisionLost = new SQLWarning( + Messages.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") + columnIndex + "(" + + timeColField + ")."); + /* + * if (this.warningChain == null) { this.warningChain = + * precisionLost; } else { + * this.warningChain.setNextWarning(precisionLost); } + */ + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { + hr = StringUtils.getInt(timeAsBytes, offset + 11, offset + 13); + min = StringUtils.getInt(timeAsBytes, offset + 14, offset + 16); + sec = StringUtils.getInt(timeAsBytes, offset + 17, offset + 19); + + @SuppressWarnings("unused") + SQLWarning precisionLost = new SQLWarning( + Messages.getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") + (columnIndex + 1) + "(" + + timeColField + ")."); + + /* + * if (this.warningChain == null) { this.warningChain = + * precisionLost; } else { + * this.warningChain.setNextWarning(precisionLost); } + */ + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) { + return rs.fastTimeCreate(null, 0, 0, 0); // midnight on the + // given date + } else { + // convert a String to a Time + if ((length != 5) && (length != 8)) { + throw SQLError.createSQLException( + Messages.getString("ResultSet.Bad_format_for_Time____267") + StringUtils.toString(timeAsBytes) + + Messages.getString("ResultSet.___in_column__268") + (columnIndex + 1), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + hr = StringUtils.getInt(timeAsBytes, offset + 0, offset + 2); + min = StringUtils.getInt(timeAsBytes, offset + 3, offset + 5); + sec = (length == 5) ? 0 : StringUtils.getInt(timeAsBytes, offset + 6, offset + 8); + } + + Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew(); + + if (!rs.useLegacyDatetimeCode) { + // TODO: return rs.fastTimeCreate(targetCalendar, hr, min, sec, nanos); + // java.sql.Time doesn't contain fractional part, so PreparedStatement.setTime/getTime can't deal with TIME(n) fractional part. + // There may be better mappings to high-precision time coming in JDBC-5 with the adoption of JSR-310. + return rs.fastTimeCreate(targetCalendar, hr, min, sec); + } + + return TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, rs.fastTimeCreate(sessionCalendar, hr, min, sec), conn.getServerTimezoneTZ(), + tz, rollForward); + // TODO: min, sec, nanos), conn.getServerTimezoneTZ(), tz, + // java.sql.Time doesn't contain fractional part, so PreparedStatement.setTime/getTime can't deal with TIME(n) fractional part. + // There may be better mappings to high-precision time coming in JDBC-5 with the adoption of JSR-310. + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + public abstract Time getTimeFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, ResultSetImpl rs) + throws SQLException; + + protected Timestamp getTimestampFast(int columnIndex, byte[] timestampAsBytes, int offset, int length, Calendar targetCalendar, TimeZone tz, + boolean rollForward, MySQLConnection conn, ResultSetImpl rs) throws SQLException { + + try { + Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn.getUtcCalendar() : rs.getCalendarInstanceForSessionOrNew(); + + boolean allZeroTimestamp = true; + + boolean onlyTimePresent = false; + + for (int i = 0; i < length; i++) { + if (timestampAsBytes[offset + i] == ':') { + onlyTimePresent = true; + break; + } + } + + for (int i = 0; i < length; i++) { + byte b = timestampAsBytes[offset + i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') { + allZeroTimestamp = false; + + break; + } + } + + if (!onlyTimePresent && allZeroTimestamp) { + + if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) { + + return null; + } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) { + throw SQLError.createSQLException("Value '" + StringUtils.toString(timestampAsBytes) + "' can not be represented as java.sql.Timestamp", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, 1, 1, 1, 0, 0, 0, 0); + } + // We're left with the case of 'round' to a date Java _can_ represent, which is '0001-01-01'. + return rs.fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0); + + } else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, StringUtils.getInt(timestampAsBytes, offset, 4), 1, 1, 0, 0, 0, 0); + } + + return TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, + rs.fastTimestampCreate(sessionCalendar, StringUtils.getInt(timestampAsBytes, offset, 4), 1, 1, 0, 0, 0, 0), conn.getServerTimezoneTZ(), + tz, rollForward); + } else { + // Convert from TIMESTAMP, TIME or DATE + + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minutes = 0; + int seconds = 0; + int nanos = 0; + + // check for the fractional part + int decimalIndex = -1; + for (int i = 0; i < length; i++) { + if (timestampAsBytes[offset + i] == '.') { + decimalIndex = i; + break; + } + } + + if (decimalIndex == offset + length - 1) { + // if the dot is in last position + length--; + + } else if (decimalIndex != -1) { + if ((decimalIndex + 2) <= length) { + nanos = StringUtils.getInt(timestampAsBytes, offset + decimalIndex + 1, offset + length); + + int numDigits = (length) - (decimalIndex + 1); + + if (numDigits < 9) { + int factor = (int) (Math.pow(10, 9 - numDigits)); + nanos = nanos * factor; + } + } else { + throw new IllegalArgumentException(); // re-thrown + // further down with a much better error message + } + + length = decimalIndex; + } + + switch (length) { + case 29: + case 26: + case 25: + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(timestampAsBytes, offset + 5, offset + 7); + day = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); + hour = StringUtils.getInt(timestampAsBytes, offset + 11, offset + 13); + minutes = StringUtils.getInt(timestampAsBytes, offset + 14, offset + 16); + seconds = StringUtils.getInt(timestampAsBytes, offset + 17, offset + 19); + + break; + } + + case 14: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); + day = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); + hour = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); + minutes = StringUtils.getInt(timestampAsBytes, offset + 10, offset + 12); + seconds = StringUtils.getInt(timestampAsBytes, offset + 12, offset + 14); + + break; + } + + case 12: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + + month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); + day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); + hour = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); + minutes = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); + seconds = StringUtils.getInt(timestampAsBytes, offset + 10, offset + 12); + + break; + } + + case 10: { + boolean hasDash = false; + + for (int i = 0; i < length; i++) { + if (timestampAsBytes[offset + i] == '-') { + hasDash = true; + break; + } + } + + if ((this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) || hasDash) { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(timestampAsBytes, offset + 5, offset + 7); + day = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); + hour = 0; + minutes = 0; + } else { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = (year + 100); + } + + month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); + day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); + hour = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); + minutes = StringUtils.getInt(timestampAsBytes, offset + 8, offset + 10); + + year += 1900; // two-digit year + } + + break; + } + + case 8: { + boolean hasColon = false; + + for (int i = 0; i < length; i++) { + if (timestampAsBytes[offset + i] == ':') { + hasColon = true; + break; + } + } + + if (hasColon) { + hour = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + minutes = StringUtils.getInt(timestampAsBytes, offset + 3, offset + 5); + seconds = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); + + year = 1970; + month = 1; + day = 1; + + break; + } + + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 4); + month = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); + day = StringUtils.getInt(timestampAsBytes, offset + 6, offset + 8); + + year -= 1900; + month--; + + break; + } + + case 6: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + + month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); + day = StringUtils.getInt(timestampAsBytes, offset + 4, offset + 6); + + break; + } + + case 4: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = (year + 100); + } + + month = StringUtils.getInt(timestampAsBytes, offset + 2, offset + 4); + + day = 1; + + break; + } + + case 2: { + year = StringUtils.getInt(timestampAsBytes, offset + 0, offset + 2); + + if (year <= 69) { + year = (year + 100); + } + + year += 1900; + month = 1; + day = 1; + + break; + } + + default: + throw new java.sql.SQLException( + "Bad format for Timestamp '" + StringUtils.toString(timestampAsBytes) + "' in column " + (columnIndex + 1) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + if (!rs.useLegacyDatetimeCode) { + return TimeUtil.fastTimestampCreate(tz, year, month, day, hour, minutes, seconds, nanos); + } + + return TimeUtil.changeTimezone(conn, sessionCalendar, targetCalendar, + rs.fastTimestampCreate(sessionCalendar, year, month, day, hour, minutes, seconds, nanos), conn.getServerTimezoneTZ(), tz, rollForward); + } + } catch (RuntimeException e) { + SQLException sqlEx = SQLError.createSQLException( + "Cannot convert value '" + getString(columnIndex, "ISO8859_1", conn) + "' from column " + (columnIndex + 1) + " to TIMESTAMP.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + sqlEx.initCause(e); + + throw sqlEx; + } + } + + public abstract Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar, TimeZone tz, boolean rollForward, MySQLConnection conn, + ResultSetImpl rs) throws SQLException; + + /** + * Could the column value at the given index (which starts at 0) be + * interpreted as a floating-point number (has +/-/E/e in it)? + * + * @param index + * of the column value (starting at 0) to check. + * + * @return true if the column value at the given index looks like it might + * be a floating-point number, false if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean isFloatingPointNumber(int index) throws SQLException; + + /** + * Is the column value at the given index (which starts at 0) NULL? + * + * @param index + * of the column value (starting at 0) to check. + * + * @return true if the column value is NULL, false if not. + * + * @throws SQLException + * if an error occurs + */ + public abstract boolean isNull(int index) throws SQLException; + + /** + * Returns the length of the column at the given index (which starts at 0). + * + * @param index + * of the column value (starting at 0) for which to return the + * length. + * @return the length of the requested column, 0 if null (clients of this + * interface should use isNull() beforehand to determine status of + * NULL values in the column). + * + * @throws SQLException + */ + public abstract long length(int index) throws SQLException; + + /** + * Sets the given column value (only works currently with + * ByteArrayRowHolder). + * + * @param index + * index of the column value (starting at 0) to set. + * @param value + * the (raw) value to set + * + * @throws SQLException + * if an error occurs, or the concrete RowHolder doesn't support + * this operation. + */ + public abstract void setColumnValue(int index, byte[] value) throws SQLException; + + public ResultSetRow setMetadata(Field[] f) throws SQLException { + this.metadata = f; + + return this; + } + + public abstract int getBytesSize(); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowData.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowData.java new file mode 100644 index 0000000..93f1fc9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowData.java @@ -0,0 +1,243 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * This interface abstracts away how row data is accessed by the result set. It is meant to allow a static implementation (Current version), and a streaming + * one. + */ +public interface RowData { + + /** + * What's returned for the size of a result set when its size can not be + * determined. + */ + public static final int RESULT_SET_SIZE_UNKNOWN = -1; + + /** + * Adds a row to this row data. + * + * @param row + * the row to add + * @throws SQLException + * if a database error occurs + */ + void addRow(ResultSetRow row) throws SQLException; + + /** + * Moves to after last. + * + * @throws SQLException + * if a database error occurs + */ + void afterLast() throws SQLException; + + /** + * Moves to before first. + * + * @throws SQLException + * if a database error occurs + */ + void beforeFirst() throws SQLException; + + /** + * Moves to before last so next el is the last el. + * + * @throws SQLException + * if a database error occurs + */ + void beforeLast() throws SQLException; + + /** + * We're done. + * + * @throws SQLException + * if a database error occurs + */ + void close() throws SQLException; + + /** + * Only works on non dynamic result sets. + * + * @param index + * row number to get at + * @return row data at index + * @throws SQLException + * if a database error occurs + */ + ResultSetRow getAt(int index) throws SQLException; + + /** + * Returns the current position in the result set as a row number. + * + * @return the current row number + * @throws SQLException + * if a database error occurs + */ + int getCurrentRowNumber() throws SQLException; + + /** + * Returns the result set that 'owns' this RowData + */ + ResultSetInternalMethods getOwner(); + + /** + * Returns true if another row exsists. + * + * @return true if more rows + * @throws SQLException + * if a database error occurs + */ + boolean hasNext() throws SQLException; + + /** + * Returns true if we got the last element. + * + * @return true if after last row + * @throws SQLException + * if a database error occurs + */ + boolean isAfterLast() throws SQLException; + + /** + * Returns if iteration has not occured yet. + * + * @return true if before first row + * @throws SQLException + * if a database error occurs + */ + boolean isBeforeFirst() throws SQLException; + + /** + * Returns true if the result set is dynamic. + * + * This means that move back and move forward won't work because we do not + * hold on to the records. + * + * @return true if this result set is streaming from the server + * @throws SQLException + * if a database error occurs + */ + boolean isDynamic() throws SQLException; + + /** + * Has no records. + * + * @return true if no records + * @throws SQLException + * if a database error occurs + */ + boolean isEmpty() throws SQLException; + + /** + * Are we on the first row of the result set? + * + * @return true if on first row + * @throws SQLException + * if a database error occurs + */ + boolean isFirst() throws SQLException; + + /** + * Are we on the last row of the result set? + * + * @return true if on last row + * @throws SQLException + * if a database error occurs + */ + boolean isLast() throws SQLException; + + /** + * Moves the current position relative 'rows' from the current position. + * + * @param rows + * the relative number of rows to move + * @throws SQLException + * if a database error occurs + */ + void moveRowRelative(int rows) throws SQLException; + + /** + * Returns the next row. + * + * @return the next row value + * @throws SQLException + * if a database error occurs + */ + ResultSetRow next() throws SQLException; + + /** + * Removes the row at the given index. + * + * @param index + * the row to move to + * @throws SQLException + * if a database error occurs + */ + void removeRow(int index) throws SQLException; + + /** + * Moves the current position in the result set to the given row number. + * + * @param rowNumber + * row to move to + * @throws SQLException + * if a database error occurs + */ + void setCurrentRow(int rowNumber) throws SQLException; + + /** + * Set the result set that 'owns' this RowData + * + * @param rs + * the result set that 'owns' this RowData + */ + void setOwner(ResultSetImpl rs); + + /** + * Only works on non dynamic result sets. + * + * @return the size of this row data + * @throws SQLException + * if a database error occurs + */ + int size() throws SQLException; + + /** + * Did this result set have no rows? + */ + boolean wasEmpty(); + + /** + * Sometimes the driver doesn't have metadata until after + * the statement has the result set in-hand (because it's cached), + * so it can call this to set it after the fact. + * + * @param metadata + * field-level metadata for the result set + */ + void setMetadata(Field[] metadata); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataCursor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataCursor.java new file mode 100644 index 0000000..e15673d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataCursor.java @@ -0,0 +1,463 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Model for result set data backed by a cursor. Only works for forward-only result sets (but still works with updatable concurrency). + */ +public class RowDataCursor implements RowData { + + private final static int BEFORE_START_OF_ROWS = -1; + + /** + * The cache of rows we have retrieved from the server. + */ + private List fetchedRows; + + /** + * Where we are positionaly in the entire result set, used mostly to + * facilitate easy 'isBeforeFirst()' and 'isFirst()' methods. + */ + private int currentPositionInEntireResult = BEFORE_START_OF_ROWS; + + /** + * Position in cache of rows, used to determine if we need to fetch more + * rows from the server to satisfy a request for the next row. + */ + private int currentPositionInFetchedRows = BEFORE_START_OF_ROWS; + + /** + * The result set that we 'belong' to. + */ + private ResultSetImpl owner; + + /** + * Have we been told from the server that we have seen the last row? + */ + private boolean lastRowFetched = false; + + /** + * Field-level metadata from the server. We need this, because it is not + * sent for each batch of rows, but we need the metadata to unpack the + * results for each field. + */ + private Field[] metadata; + + /** + * Communications channel to the server + */ + private MysqlIO mysql; + + /** + * Identifier for the statement that created this cursor. + */ + private long statementIdOnServer; + + /** + * The prepared statement that created this cursor. + */ + private ServerPreparedStatement prepStmt; + + /** + * The server status for 'last-row-sent'...This might belong in mysqldefs, + * but it it only ever referenced from here. + */ + private static final int SERVER_STATUS_LAST_ROW_SENT = 128; + + /** + * Have we attempted to fetch any rows yet? + */ + private boolean firstFetchCompleted = false; + + private boolean wasEmpty = false; + + private boolean useBufferRowExplicit = false; + + /** + * Creates a new cursor-backed row provider. + * + * @param ioChannel + * connection to the server. + * @param creatingStatement + * statement that opened the cursor. + * @param metadata + * field-level metadata for the results that this cursor covers. + */ + public RowDataCursor(MysqlIO ioChannel, ServerPreparedStatement creatingStatement, Field[] metadata) { + this.currentPositionInEntireResult = BEFORE_START_OF_ROWS; + this.metadata = metadata; + this.mysql = ioChannel; + this.statementIdOnServer = creatingStatement.getServerStatementId(); + this.prepStmt = creatingStatement; + this.useBufferRowExplicit = MysqlIO.useBufferRowExplicit(this.metadata); + + } + + /** + * Returns true if we got the last element. + */ + public boolean isAfterLast() { + return this.lastRowFetched && this.currentPositionInFetchedRows > this.fetchedRows.size(); + } + + /** + * Only works on non dynamic result sets. + * + * @param index + * row number to get at + * @return row data at index + * @throws SQLException + * if a database error occurs + */ + public ResultSetRow getAt(int ind) throws SQLException { + notSupported(); + + return null; + } + + /** + * Returns if iteration has not occured yet. + * + * @return true if before first row + * @throws SQLException + * if a database error occurs + */ + public boolean isBeforeFirst() throws SQLException { + return this.currentPositionInEntireResult < 0; + } + + /** + * Moves the current position in the result set to the given row number. + * + * @param rowNumber + * row to move to + * @throws SQLException + * if a database error occurs + */ + public void setCurrentRow(int rowNumber) throws SQLException { + notSupported(); + } + + /** + * Returns the current position in the result set as a row number. + * + * @return the current row number + * @throws SQLException + * if a database error occurs + */ + public int getCurrentRowNumber() throws SQLException { + return this.currentPositionInEntireResult + 1; + } + + /** + * Returns true if the result set is dynamic. + * + * This means that move back and move forward won't work because we do not + * hold on to the records. + * + * @return true if this result set is streaming from the server + */ + public boolean isDynamic() { + return true; + } + + /** + * Has no records. + * + * @return true if no records + * @throws SQLException + * if a database error occurs + */ + public boolean isEmpty() throws SQLException { + return this.isBeforeFirst() && this.isAfterLast(); + } + + /** + * Are we on the first row of the result set? + * + * @return true if on first row + * @throws SQLException + * if a database error occurs + */ + public boolean isFirst() throws SQLException { + return this.currentPositionInEntireResult == 0; + } + + /** + * Are we on the last row of the result set? + * + * @return true if on last row + * @throws SQLException + * if a database error occurs + */ + public boolean isLast() throws SQLException { + return this.lastRowFetched && this.currentPositionInFetchedRows == (this.fetchedRows.size() - 1); + } + + /** + * Adds a row to this row data. + * + * @param row + * the row to add + * @throws SQLException + * if a database error occurs + */ + public void addRow(ResultSetRow row) throws SQLException { + notSupported(); + } + + /** + * Moves to after last. + * + * @throws SQLException + * if a database error occurs + */ + public void afterLast() throws SQLException { + notSupported(); + } + + /** + * Moves to before first. + * + * @throws SQLException + * if a database error occurs + */ + public void beforeFirst() throws SQLException { + notSupported(); + } + + /** + * Moves to before last so next el is the last el. + * + * @throws SQLException + * if a database error occurs + */ + public void beforeLast() throws SQLException { + notSupported(); + } + + /** + * We're done. + * + * @throws SQLException + * if a database error occurs + */ + public void close() throws SQLException { + + this.metadata = null; + this.owner = null; + } + + /** + * Returns true if another row exists. + * + * @return true if more rows + * @throws SQLException + * if a database error occurs + */ + public boolean hasNext() throws SQLException { + + if (this.fetchedRows != null && this.fetchedRows.size() == 0) { + return false; + } + + if (this.owner != null && this.owner.owningStatement != null) { + int maxRows = this.owner.owningStatement.maxRows; + + if (maxRows != -1 && this.currentPositionInEntireResult + 1 > maxRows) { + return false; + } + } + + if (this.currentPositionInEntireResult != BEFORE_START_OF_ROWS) { + // Case, we've fetched some rows, but are not at end of fetched block + if (this.currentPositionInFetchedRows < (this.fetchedRows.size() - 1)) { + return true; + } else if (this.currentPositionInFetchedRows == this.fetchedRows.size() && this.lastRowFetched) { + return false; + } else { + // need to fetch to determine + fetchMoreRows(); + + return (this.fetchedRows.size() > 0); + } + } + + // Okay, no rows _yet_, so fetch 'em + + fetchMoreRows(); + + return this.fetchedRows.size() > 0; + } + + /** + * Moves the current position relative 'rows' from the current position. + * + * @param rows + * the relative number of rows to move + * @throws SQLException + * if a database error occurs + */ + public void moveRowRelative(int rows) throws SQLException { + notSupported(); + } + + /** + * Returns the next row. + * + * @return the next row value + * @throws SQLException + * if a database error occurs + */ + public ResultSetRow next() throws SQLException { + if (this.fetchedRows == null && this.currentPositionInEntireResult != BEFORE_START_OF_ROWS) { + throw SQLError.createSQLException(Messages.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), + SQLError.SQL_STATE_GENERAL_ERROR, this.mysql.getExceptionInterceptor()); + } + + if (!hasNext()) { + return null; + } + + this.currentPositionInEntireResult++; + this.currentPositionInFetchedRows++; + + // Catch the forced scroll-passed-end + if (this.fetchedRows != null && this.fetchedRows.size() == 0) { + return null; + } + + if ((this.fetchedRows == null) || (this.currentPositionInFetchedRows > (this.fetchedRows.size() - 1))) { + fetchMoreRows(); + this.currentPositionInFetchedRows = 0; + } + + ResultSetRow row = this.fetchedRows.get(this.currentPositionInFetchedRows); + + row.setMetadata(this.metadata); + + return row; + } + + /** + */ + private void fetchMoreRows() throws SQLException { + if (this.lastRowFetched) { + this.fetchedRows = new ArrayList(0); + return; + } + + synchronized (this.owner.connection.getConnectionMutex()) { + boolean oldFirstFetchCompleted = this.firstFetchCompleted; + + if (!this.firstFetchCompleted) { + this.firstFetchCompleted = true; + } + + int numRowsToFetch = this.owner.getFetchSize(); + + if (numRowsToFetch == 0) { + numRowsToFetch = this.prepStmt.getFetchSize(); + } + + if (numRowsToFetch == Integer.MIN_VALUE) { + // Handle the case where the user used 'old' streaming result sets + + numRowsToFetch = 1; + } + + this.fetchedRows = this.mysql.fetchRowsViaCursor(this.fetchedRows, this.statementIdOnServer, this.metadata, numRowsToFetch, + this.useBufferRowExplicit); + this.currentPositionInFetchedRows = BEFORE_START_OF_ROWS; + + if ((this.mysql.getServerStatus() & SERVER_STATUS_LAST_ROW_SENT) != 0) { + this.lastRowFetched = true; + + if (!oldFirstFetchCompleted && this.fetchedRows.size() == 0) { + this.wasEmpty = true; + } + } + } + } + + /** + * Removes the row at the given index. + * + * @param index + * the row to move to + * @throws SQLException + * if a database error occurs + */ + public void removeRow(int ind) throws SQLException { + notSupported(); + } + + /** + * Only works on non dynamic result sets. + * + * @return the size of this row data + */ + public int size() { + return RESULT_SET_SIZE_UNKNOWN; + } + + protected void nextRecord() throws SQLException { + + } + + private void notSupported() throws SQLException { + throw new OperationNotSupportedException(); + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.RowProvider#setOwner(com.mysql.jdbc.ResultSet) + */ + public void setOwner(ResultSetImpl rs) { + this.owner = rs; + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.RowProvider#getOwner() + */ + public ResultSetInternalMethods getOwner() { + return this.owner; + } + + public boolean wasEmpty() { + return this.wasEmpty; + } + + public void setMetadata(Field[] metadata) { + this.metadata = metadata; + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataDynamic.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataDynamic.java new file mode 100644 index 0000000..8450435 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataDynamic.java @@ -0,0 +1,467 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; + +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +/** + * Allows streaming of MySQL data. + */ +public class RowDataDynamic implements RowData { + + private int columnCount; + + private Field[] metadata; + + private int index = -1; + + private MysqlIO io; + + private boolean isAfterEnd = false; + + private boolean noMoreRows = false; + + private boolean isBinaryEncoded = false; + + private ResultSetRow nextRow; + + private ResultSetImpl owner; + + private boolean streamerClosed = false; + + private boolean wasEmpty = false; // we don't know until we attempt to traverse + + private boolean useBufferRowExplicit; + + private boolean moreResultsExisted; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates a new RowDataDynamic object. + * + * @param io + * the connection to MySQL that this data is coming from + * @param metadata + * the metadata that describe this data + * @param isBinaryEncoded + * is this data in native format? + * @param colCount + * the number of columns + * @throws SQLException + * if the next record can not be found + */ + public RowDataDynamic(MysqlIO io, int colCount, Field[] fields, boolean isBinaryEncoded) throws SQLException { + this.io = io; + this.columnCount = colCount; + this.isBinaryEncoded = isBinaryEncoded; + this.metadata = fields; + this.exceptionInterceptor = this.io.getExceptionInterceptor(); + this.useBufferRowExplicit = MysqlIO.useBufferRowExplicit(this.metadata); + } + + /** + * Adds a row to this row data. + * + * @param row + * the row to add + * @throws SQLException + * if a database error occurs + */ + public void addRow(ResultSetRow row) throws SQLException { + notSupported(); + } + + /** + * Moves to after last. + * + * @throws SQLException + * if a database error occurs + */ + public void afterLast() throws SQLException { + notSupported(); + } + + /** + * Moves to before first. + * + * @throws SQLException + * if a database error occurs + */ + public void beforeFirst() throws SQLException { + notSupported(); + } + + /** + * Moves to before last so next el is the last el. + * + * @throws SQLException + * if a database error occurs + */ + public void beforeLast() throws SQLException { + notSupported(); + } + + /** + * We're done. + * + * @throws SQLException + * if a database error occurs + */ + public void close() throws SQLException { + // Belt and suspenders here - if we don't have a reference to the connection it's more than likely dead/gone and we won't be able to consume rows anyway + + Object mutex = this; + + MySQLConnection conn = null; + + if (this.owner != null) { + conn = this.owner.connection; + + if (conn != null) { + mutex = conn.getConnectionMutex(); + } + } + + boolean hadMore = false; + int howMuchMore = 0; + + synchronized (mutex) { + // drain the rest of the records. + while (next() != null) { + hadMore = true; + howMuchMore++; + + if (howMuchMore % 100 == 0) { + Thread.yield(); + } + } + + if (conn != null) { + if (!conn.getClobberStreamingResults() && conn.getNetTimeoutForStreamingResults() > 0) { + String oldValue = conn.getServerVariable("net_write_timeout"); + + if (oldValue == null || oldValue.length() == 0) { + oldValue = "60"; // the current default + } + + this.io.clearInputStream(); + + java.sql.Statement stmt = null; + + try { + stmt = conn.createStatement(); + ((com.mysql.jdbc.StatementImpl) stmt).executeSimpleNonQuery(conn, "SET net_write_timeout=" + oldValue); + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + if (conn.getUseUsageAdvisor()) { + if (hadMore) { + + ProfilerEventHandler eventSink = ProfilerEventHandlerFactory.getInstance(conn); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", + this.owner.owningStatement == null ? "N/A" : this.owner.owningStatement.currentCatalog, this.owner.connectionId, + this.owner.owningStatement == null ? -1 : this.owner.owningStatement.getId(), -1, System.currentTimeMillis(), 0, + Constants.MILLIS_I18N, null, null, + Messages.getString("RowDataDynamic.2") + howMuchMore + Messages.getString("RowDataDynamic.3") + + Messages.getString("RowDataDynamic.4") + Messages.getString("RowDataDynamic.5") + + Messages.getString("RowDataDynamic.6") + this.owner.pointOfOrigin)); + } + } + } + } + + this.metadata = null; + this.owner = null; + } + + /** + * Only works on non dynamic result sets. + * + * @param index + * row number to get at + * @return row data at index + * @throws SQLException + * if a database error occurs + */ + public ResultSetRow getAt(int ind) throws SQLException { + notSupported(); + + return null; + } + + /** + * Returns the current position in the result set as a row number. + * + * @return the current row number + * @throws SQLException + * if a database error occurs + */ + public int getCurrentRowNumber() throws SQLException { + notSupported(); + + return -1; + } + + /** + * @see com.mysql.jdbc.RowData#getOwner() + */ + public ResultSetInternalMethods getOwner() { + return this.owner; + } + + /** + * Returns true if another row exsists. + * + * @return true if more rows + * @throws SQLException + * if a database error occurs + */ + public boolean hasNext() throws SQLException { + boolean hasNext = (this.nextRow != null); + + if (!hasNext && !this.streamerClosed) { + this.io.closeStreamer(this); + this.streamerClosed = true; + } + + return hasNext; + } + + /** + * Returns true if we got the last element. + * + * @return true if after last row + * @throws SQLException + * if a database error occurs + */ + public boolean isAfterLast() throws SQLException { + return this.isAfterEnd; + } + + /** + * Returns if iteration has not occured yet. + * + * @return true if before first row + * @throws SQLException + * if a database error occurs + */ + public boolean isBeforeFirst() throws SQLException { + return this.index < 0; + } + + /** + * Returns true if the result set is dynamic. + * + * This means that move back and move forward won't work because we do not + * hold on to the records. + * + * @return true if this result set is streaming from the server + */ + public boolean isDynamic() { + return true; + } + + /** + * Has no records. + * + * @return true if no records + * @throws SQLException + * if a database error occurs + */ + public boolean isEmpty() throws SQLException { + notSupported(); + + return false; + } + + /** + * Are we on the first row of the result set? + * + * @return true if on first row + * @throws SQLException + * if a database error occurs + */ + public boolean isFirst() throws SQLException { + notSupported(); + + return false; + } + + /** + * Are we on the last row of the result set? + * + * @return true if on last row + * @throws SQLException + * if a database error occurs + */ + public boolean isLast() throws SQLException { + notSupported(); + + return false; + } + + /** + * Moves the current position relative 'rows' from the current position. + * + * @param rows + * the relative number of rows to move + * @throws SQLException + * if a database error occurs + */ + public void moveRowRelative(int rows) throws SQLException { + notSupported(); + } + + /** + * Returns the next row. + * + * @return the next row value + * @throws SQLException + * if a database error occurs + */ + public ResultSetRow next() throws SQLException { + + nextRecord(); + + if (this.nextRow == null && !this.streamerClosed && !this.moreResultsExisted) { + this.io.closeStreamer(this); + this.streamerClosed = true; + } + + if (this.nextRow != null) { + if (this.index != Integer.MAX_VALUE) { + this.index++; + } + } + + return this.nextRow; + } + + private void nextRecord() throws SQLException { + + try { + if (!this.noMoreRows) { + this.nextRow = this.io.nextRow(this.metadata, this.columnCount, this.isBinaryEncoded, java.sql.ResultSet.CONCUR_READ_ONLY, true, + this.useBufferRowExplicit, true, null); + + if (this.nextRow == null) { + this.noMoreRows = true; + this.isAfterEnd = true; + this.moreResultsExisted = this.io.tackOnMoreStreamingResults(this.owner); + + if (this.index == -1) { + this.wasEmpty = true; + } + } + } else { + this.nextRow = null; + this.isAfterEnd = true; + } + } catch (SQLException sqlEx) { + if (sqlEx instanceof StreamingNotifiable) { + ((StreamingNotifiable) sqlEx).setWasStreamingResults(); + } + + // There won't be any more rows + this.noMoreRows = true; + + // don't wrap SQLExceptions + throw sqlEx; + } catch (Exception ex) { + String exceptionType = ex.getClass().getName(); + String exceptionMessage = ex.getMessage(); + + exceptionMessage += Messages.getString("RowDataDynamic.7"); + exceptionMessage += Util.stackTraceToString(ex); + + SQLException sqlEx = SQLError.createSQLException( + Messages.getString("RowDataDynamic.8") + exceptionType + Messages.getString("RowDataDynamic.9") + exceptionMessage, + SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + private void notSupported() throws SQLException { + throw new OperationNotSupportedException(); + } + + /** + * Removes the row at the given index. + * + * @param index + * the row to move to + * @throws SQLException + * if a database error occurs + */ + public void removeRow(int ind) throws SQLException { + notSupported(); + } + + /** + * Moves the current position in the result set to the given row number. + * + * @param rowNumber + * row to move to + * @throws SQLException + * if a database error occurs + */ + public void setCurrentRow(int rowNumber) throws SQLException { + notSupported(); + } + + /** + * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSetInternalMethods) + */ + public void setOwner(ResultSetImpl rs) { + this.owner = rs; + } + + /** + * Only works on non dynamic result sets. + * + * @return the size of this row data + */ + public int size() { + return RESULT_SET_SIZE_UNKNOWN; + } + + public boolean wasEmpty() { + return this.wasEmpty; + } + + public void setMetadata(Field[] metadata) { + this.metadata = metadata; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataStatic.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataStatic.java new file mode 100644 index 0000000..c559bef --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/RowDataStatic.java @@ -0,0 +1,195 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.List; + +/** + * Represents an in-memory result set + */ +public class RowDataStatic implements RowData { + private Field[] metadata; + + private int index; + + ResultSetImpl owner; + + private List rows; + + /** + * Creates a new RowDataStatic object. + * + * @param rows + */ + public RowDataStatic(List rows) { + this.index = -1; + this.rows = rows; + } + + public void addRow(ResultSetRow row) { + this.rows.add(row); + } + + /** + * Moves to after last. + */ + public void afterLast() { + if (this.rows.size() > 0) { + this.index = this.rows.size(); + } + } + + /** + * Moves to before first. + */ + public void beforeFirst() { + if (this.rows.size() > 0) { + this.index = -1; + } + } + + public void beforeLast() { + if (this.rows.size() > 0) { + this.index = this.rows.size() - 2; + } + } + + public void close() { + } + + public ResultSetRow getAt(int atIndex) throws SQLException { + if ((atIndex < 0) || (atIndex >= this.rows.size())) { + return null; + } + + return (this.rows.get(atIndex)).setMetadata(this.metadata); + } + + public int getCurrentRowNumber() { + return this.index; + } + + /** + * @see com.mysql.jdbc.RowData#getOwner() + */ + public ResultSetInternalMethods getOwner() { + return this.owner; + } + + public boolean hasNext() { + boolean hasMore = (this.index + 1) < this.rows.size(); + + return hasMore; + } + + /** + * Returns true if we got the last element. + */ + public boolean isAfterLast() { + return this.index >= this.rows.size() && this.rows.size() != 0; + } + + /** + * Returns if iteration has not occurred yet. + */ + public boolean isBeforeFirst() { + return this.index == -1 && this.rows.size() != 0; + } + + public boolean isDynamic() { + return false; + } + + public boolean isEmpty() { + return this.rows.size() == 0; + } + + public boolean isFirst() { + return this.index == 0; + } + + public boolean isLast() { + // + // You can never be on the 'last' row of an empty result set + // + if (this.rows.size() == 0) { + return false; + } + + return (this.index == (this.rows.size() - 1)); + } + + public void moveRowRelative(int rowsToMove) { + if (this.rows.size() > 0) { + this.index += rowsToMove; + if (this.index < -1) { + beforeFirst(); + } else if (this.index > this.rows.size()) { + afterLast(); + } + } + } + + public ResultSetRow next() throws SQLException { + this.index++; + + if (this.index > this.rows.size()) { + afterLast(); + } else if (this.index < this.rows.size()) { + ResultSetRow row = this.rows.get(this.index); + + return row.setMetadata(this.metadata); + } + + return null; + } + + public void removeRow(int atIndex) { + this.rows.remove(atIndex); + } + + public void setCurrentRow(int newIndex) { + this.index = newIndex; + } + + /** + * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSetInternalMethods) + */ + public void setOwner(ResultSetImpl rs) { + this.owner = rs; + } + + public int size() { + return this.rows.size(); + } + + public boolean wasEmpty() { + return (this.rows != null && this.rows.size() == 0); + } + + public void setMetadata(Field[] metadata) { + this.metadata = metadata; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SQLError.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SQLError.java new file mode 100644 index 0000000..ab20469 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SQLError.java @@ -0,0 +1,1208 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.Constructor; +import java.net.BindException; +import java.sql.BatchUpdateException; +import java.sql.DataTruncation; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.TreeMap; + +import com.mysql.jdbc.exceptions.MySQLDataException; +import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException; +import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException; +import com.mysql.jdbc.exceptions.MySQLQueryInterruptedException; +import com.mysql.jdbc.exceptions.MySQLSyntaxErrorException; +import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException; +import com.mysql.jdbc.exceptions.MySQLTransientConnectionException; + +/** + * SQLError is a utility class that maps MySQL error codes to X/Open error codes as is required by the JDBC spec. + */ +public class SQLError { + static final int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196; + + private static Map mysqlToSql99State; + + private static Map mysqlToSqlState; + + // SQL-92 + public static final String SQL_STATE_WARNING = "01000"; + public static final String SQL_STATE_DISCONNECT_ERROR = "01002"; + public static final String SQL_STATE_DATE_TRUNCATED = "01004"; + public static final String SQL_STATE_PRIVILEGE_NOT_REVOKED = "01006"; + public static final String SQL_STATE_NO_DATA = "02000"; + public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; + public static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = "08001"; + public static final String SQL_STATE_CONNECTION_IN_USE = "08002"; + public static final String SQL_STATE_CONNECTION_NOT_OPEN = "08003"; + public static final String SQL_STATE_CONNECTION_REJECTED = "08004"; + public static final String SQL_STATE_CONNECTION_FAILURE = "08006"; + public static final String SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN = "08007"; + public static final String SQL_STATE_COMMUNICATION_LINK_FAILURE = "08S01"; + public static final String SQL_STATE_FEATURE_NOT_SUPPORTED = "0A000"; + public static final String SQL_STATE_CARDINALITY_VIOLATION = "21000"; + public static final String SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST = "21S01"; + public static final String SQL_STATE_STRING_DATA_RIGHT_TRUNCATION = "22001"; + public static final String SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE = "22003"; + public static final String SQL_STATE_INVALID_DATETIME_FORMAT = "22007"; + public static final String SQL_STATE_DATETIME_FIELD_OVERFLOW = "22008"; + public static final String SQL_STATE_DIVISION_BY_ZERO = "22012"; + public static final String SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST = "22018"; + public static final String SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION = "23000"; + public static final String SQL_STATE_INVALID_CURSOR_STATE = "24000"; + public static final String SQL_STATE_INVALID_TRANSACTION_STATE = "25000"; + public static final String SQL_STATE_INVALID_AUTH_SPEC = "28000"; + public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; + public static final String SQL_STATE_INVALID_CONDITION_NUMBER = "35000"; + public static final String SQL_STATE_INVALID_CATALOG_NAME = "3D000"; + public static final String SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE = "40001"; + public static final String SQL_STATE_SYNTAX_ERROR = "42000"; + public static final String SQL_STATE_ER_TABLE_EXISTS_ERROR = "42S01"; + public static final String SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND = "42S02"; + public static final String SQL_STATE_ER_NO_SUCH_INDEX = "42S12"; + public static final String SQL_STATE_ER_DUP_FIELDNAME = "42S21"; + public static final String SQL_STATE_ER_BAD_FIELD_ERROR = "42S22"; + + // SQL-99 + public static final String SQL_STATE_INVALID_CONNECTION_ATTRIBUTE = "01S00"; + public static final String SQL_STATE_ERROR_IN_ROW = "01S01"; + public static final String SQL_STATE_NO_ROWS_UPDATED_OR_DELETED = "01S03"; + public static final String SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED = "01S04"; + public static final String SQL_STATE_RESIGNAL_WHEN_HANDLER_NOT_ACTIVE = "0K000"; + public static final String SQL_STATE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = "0Z002"; + public static final String SQL_STATE_CASE_NOT_FOUND_FOR_CASE_STATEMENT = "20000"; + public static final String SQL_STATE_NULL_VALUE_NOT_ALLOWED = "22004"; + public static final String SQL_STATE_INVALID_LOGARITHM_ARGUMENT = "2201E"; + public static final String SQL_STATE_ACTIVE_SQL_TRANSACTION = "25001"; + public static final String SQL_STATE_READ_ONLY_SQL_TRANSACTION = "25006"; + public static final String SQL_STATE_SRE_PROHIBITED_SQL_STATEMENT_ATTEMPTED = "2F003"; + public static final String SQL_STATE_SRE_FUNCTION_EXECUTED_NO_RETURN_STATEMENT = "2F005"; + public static final String SQL_STATE_ER_QUERY_INTERRUPTED = "70100"; // non-standard ? + public static final String SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS = "S0001"; + public static final String SQL_STATE_BASE_TABLE_NOT_FOUND = "S0002"; + public static final String SQL_STATE_INDEX_ALREADY_EXISTS = "S0011"; + public static final String SQL_STATE_INDEX_NOT_FOUND = "S0012"; + public static final String SQL_STATE_COLUMN_ALREADY_EXISTS = "S0021"; + public static final String SQL_STATE_COLUMN_NOT_FOUND = "S0022"; + public static final String SQL_STATE_NO_DEFAULT_FOR_COLUMN = "S0023"; + public static final String SQL_STATE_GENERAL_ERROR = "S1000"; + public static final String SQL_STATE_MEMORY_ALLOCATION_FAILURE = "S1001"; + public static final String SQL_STATE_INVALID_COLUMN_NUMBER = "S1002"; + public static final String SQL_STATE_ILLEGAL_ARGUMENT = "S1009"; + public static final String SQL_STATE_DRIVER_NOT_CAPABLE = "S1C00"; + public static final String SQL_STATE_TIMEOUT_EXPIRED = "S1T00"; + public static final String SQL_STATE_CLI_SPECIFIC_CONDITION = "HY000"; + public static final String SQL_STATE_MEMORY_ALLOCATION_ERROR = "HY001"; + public static final String SQL_STATE_XA_RBROLLBACK = "XA100"; + public static final String SQL_STATE_XA_RBDEADLOCK = "XA102"; + public static final String SQL_STATE_XA_RBTIMEOUT = "XA106"; + public static final String SQL_STATE_XA_RMERR = "XAE03"; + public static final String SQL_STATE_XAER_NOTA = "XAE04"; + public static final String SQL_STATE_XAER_INVAL = "XAE05"; + public static final String SQL_STATE_XAER_RMFAIL = "XAE07"; + public static final String SQL_STATE_XAER_DUPID = "XAE08"; + public static final String SQL_STATE_XAER_OUTSIDE = "XAE09"; + + private static Map sqlStateMessages; + + private static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 28800; + + private static final int DUE_TO_TIMEOUT_FALSE = 0; + + private static final int DUE_TO_TIMEOUT_MAYBE = 2; + + private static final int DUE_TO_TIMEOUT_TRUE = 1; + + private static final Constructor JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = Class.forName("com.mysql.jdbc.exceptions.jdbc4.CommunicationsException") + .getConstructor(new Class[] { MySQLConnection.class, Long.TYPE, Long.TYPE, Exception.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = null; + } + + sqlStateMessages = new HashMap(); + sqlStateMessages.put(SQL_STATE_DISCONNECT_ERROR, Messages.getString("SQLError.35")); + sqlStateMessages.put(SQL_STATE_DATE_TRUNCATED, Messages.getString("SQLError.36")); + sqlStateMessages.put(SQL_STATE_PRIVILEGE_NOT_REVOKED, Messages.getString("SQLError.37")); + sqlStateMessages.put(SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, Messages.getString("SQLError.38")); + sqlStateMessages.put(SQL_STATE_ERROR_IN_ROW, Messages.getString("SQLError.39")); + sqlStateMessages.put(SQL_STATE_NO_ROWS_UPDATED_OR_DELETED, Messages.getString("SQLError.40")); + sqlStateMessages.put(SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED, Messages.getString("SQLError.41")); + sqlStateMessages.put(SQL_STATE_WRONG_NO_OF_PARAMETERS, Messages.getString("SQLError.42")); + sqlStateMessages.put(SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, Messages.getString("SQLError.43")); + sqlStateMessages.put(SQL_STATE_CONNECTION_IN_USE, Messages.getString("SQLError.44")); + sqlStateMessages.put(SQL_STATE_CONNECTION_NOT_OPEN, Messages.getString("SQLError.45")); + sqlStateMessages.put(SQL_STATE_CONNECTION_REJECTED, Messages.getString("SQLError.46")); + sqlStateMessages.put(SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, Messages.getString("SQLError.47")); + sqlStateMessages.put(SQL_STATE_COMMUNICATION_LINK_FAILURE, Messages.getString("SQLError.48")); + sqlStateMessages.put(SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST, Messages.getString("SQLError.49")); + sqlStateMessages.put(SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, Messages.getString("SQLError.50")); + sqlStateMessages.put(SQL_STATE_DATETIME_FIELD_OVERFLOW, Messages.getString("SQLError.51")); + sqlStateMessages.put(SQL_STATE_DIVISION_BY_ZERO, Messages.getString("SQLError.52")); + sqlStateMessages.put(SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE, Messages.getString("SQLError.53")); + sqlStateMessages.put(SQL_STATE_INVALID_AUTH_SPEC, Messages.getString("SQLError.54")); + sqlStateMessages.put(SQL_STATE_SYNTAX_ERROR, Messages.getString("SQLError.55")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND, Messages.getString("SQLError.56")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS, Messages.getString("SQLError.57")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_NOT_FOUND, Messages.getString("SQLError.58")); + sqlStateMessages.put(SQL_STATE_INDEX_ALREADY_EXISTS, Messages.getString("SQLError.59")); + sqlStateMessages.put(SQL_STATE_INDEX_NOT_FOUND, Messages.getString("SQLError.60")); + sqlStateMessages.put(SQL_STATE_COLUMN_ALREADY_EXISTS, Messages.getString("SQLError.61")); + sqlStateMessages.put(SQL_STATE_COLUMN_NOT_FOUND, Messages.getString("SQLError.62")); + sqlStateMessages.put(SQL_STATE_NO_DEFAULT_FOR_COLUMN, Messages.getString("SQLError.63")); + sqlStateMessages.put(SQL_STATE_GENERAL_ERROR, Messages.getString("SQLError.64")); + sqlStateMessages.put(SQL_STATE_MEMORY_ALLOCATION_FAILURE, Messages.getString("SQLError.65")); + sqlStateMessages.put(SQL_STATE_INVALID_COLUMN_NUMBER, Messages.getString("SQLError.66")); + sqlStateMessages.put(SQL_STATE_ILLEGAL_ARGUMENT, Messages.getString("SQLError.67")); + sqlStateMessages.put(SQL_STATE_DRIVER_NOT_CAPABLE, Messages.getString("SQLError.68")); + sqlStateMessages.put(SQL_STATE_TIMEOUT_EXPIRED, Messages.getString("SQLError.69")); + + mysqlToSqlState = new Hashtable(); + + mysqlToSqlState.put(MysqlErrorNumbers.ER_SELECT_REDUCED, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_UNINIT_VAR, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SIGNAL_WARN, SQL_STATE_WARNING); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CON_COUNT_ERROR, SQL_STATE_CONNECTION_REJECTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE, SQL_STATE_CONNECTION_REJECTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BAD_HOST_ERROR, SQL_STATE_CONNECTION_REJECTED); // legacy, should be SQL_STATE_COMMUNICATION_LINK_FAILURE + mysqlToSqlState.put(MysqlErrorNumbers.ER_HANDSHAKE_ERROR, SQL_STATE_CONNECTION_REJECTED); // legacy, should be SQL_STATE_COMMUNICATION_LINK_FAILURE + mysqlToSqlState.put(MysqlErrorNumbers.ER_HOST_IS_BLOCKED, SQL_STATE_CONNECTION_REJECTED); // overrides HY000, why? + mysqlToSqlState.put(MysqlErrorNumbers.ER_HOST_NOT_PRIVILEGED, SQL_STATE_CONNECTION_REJECTED); // overrides HY000, why? + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNKNOWN_COM_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SERVER_SHUTDOWN, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FORCING_CLOSE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_IPSOCK_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_FCNTL_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_READ_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_READ_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_MASTER_NET_READ, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_MASTER_NET_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CONNECT_TO_MASTER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BADSELECT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BADSTATEMENT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_SUBSELECT_NYI, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_NO_RETSET, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BAD_DB_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_GROUP_FIELD, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_SUM_SELECT, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_LONG_IDENT, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_FIELDNAME, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_ER_DUP_FIELDNAME + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_KEYNAME, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_ENTRY, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_PARSE_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_EMPTY_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NONUNIQ_TABLE, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_INVALID_DEFAULT, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_MANY_KEYS, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_LONG_KEY, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_AUTO_KEY, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_SUCH_INDEX, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_ER_NO_SUCH_INDEX + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED, SQL_STATE_ILLEGAL_ARGUMENT); // legacy, should be SQL_STATE_SYNTAX_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_DB_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_TABLE_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_PASSWORD_NO_MATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_REGEXP_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NONEXISTING_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SYNTAX_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_LONG_STRING, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_COLUMN_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_MANY_ROWS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_KEY_DOES_NOT_EXITS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_USER_LIMIT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SPECIFIC_ACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_YET, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_FK_DEF, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_ALREADY_EXISTS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DOES_NOT_EXIST, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_LILABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_LABEL_REDEFINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_LABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BADRETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_IGNORED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_TRANSLATED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_WRONG_NO_OF_ARGS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_COND_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_NORETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_CURSOR_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_UNDECLARED_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DUP_PARAM, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DUP_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DUP_COND, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DUP_CURS, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_VARCOND_AFTER_CURSHNDLR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_CURSOR_AFTER_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_PROCACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NONEXISTING_PROC_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BAD_SQLSTATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_CREATE_USER_WITH_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_DUP_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_NOT_VAR_ARG, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_SCALE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_PRECISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_M_BIGGER_THAN_D, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_LONG_BODY, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TOO_BIG_DISPLAYWIDTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_BAD_VAR_SHADOW, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_WRONG_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_NO_AGGREGATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_MAX_PREPARED_STMT_COUNT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NON_GROUPING_FIELD_USED, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_STORED_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FUNC_INEXISTENT_NAME_COLLISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_SIGNAL_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SPATIAL_MUST_HAVE_GEOM_COL, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TRUNCATE_ILLEGAL_FK, SQL_STATE_SYNTAX_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_OPERAND_COLUMNS, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BAD_NULL_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NON_UNIQ_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_UNIQUE, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_ENTRY_WITH_KEY_NAME, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DUP_UNKNOWN_IN_INDEX, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DATA_TOO_LONG, SQL_STATE_STRING_DATA_RIGHT_TRUNCATION); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE, SQL_STATE_WARNING); // legacy, should be SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_CREATE_GEOMETRY_OBJECT, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DATA_OUT_OF_RANGE, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TRUNCATED_WRONG_VALUE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ILLEGAL_VALUE_FOR_TYPE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DATETIME_FUNCTION_OVERFLOW, SQL_STATE_DATETIME_FIELD_OVERFLOW); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DIVISION_BY_ZERO, SQL_STATE_DIVISION_BY_ZERO); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_CURSOR_ALREADY_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_SP_CURSOR_NOT_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ACCESS_DENIED_NO_PASSWORD_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSqlState.put(MysqlErrorNumbers.ER_ACCESS_DENIED_CHANGE_USER_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSqlState.put(MysqlErrorNumbers.ER_DA_INVALID_CONDITION_NUMBER, SQL_STATE_INVALID_CONDITION_NUMBER); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_DB_ERROR, SQL_STATE_INVALID_CATALOG_NAME); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSqlState.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSqlState.put(MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR, SQL_STATE_ER_TABLE_EXISTS_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BAD_TABLE_ERROR, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSqlState.put(MysqlErrorNumbers.ER_UNKNOWN_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSqlState.put(MysqlErrorNumbers.ER_NO_SUCH_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSqlState.put(MysqlErrorNumbers.ER_BAD_FIELD_ERROR, SQL_STATE_COLUMN_NOT_FOUND); // legacy, should be SQL_STATE_ER_BAD_FIELD_ERROR + mysqlToSqlState.put(MysqlErrorNumbers.ER_ILLEGAL_REFERENCE, SQL_STATE_ER_BAD_FIELD_ERROR); + mysqlToSqlState.put(MysqlErrorNumbers.ER_OUTOFMEMORY, SQL_STATE_MEMORY_ALLOCATION_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_OUT_OF_SORTMEMORY, SQL_STATE_MEMORY_ALLOCATION_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + mysqlToSqlState.put(MysqlErrorNumbers.ER_LOCK_DEADLOCK, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + + mysqlToSql99State = new HashMap(); + + mysqlToSql99State.put(MysqlErrorNumbers.ER_SELECT_REDUCED, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_NULL_TO_NOTNULL, SQL_STATE_WARNING); // legacy, should be SQL_STATE_NULL_VALUE_NOT_ALLOWED + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE, SQL_STATE_WARNING); // legacy, should be SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_UNINIT_VAR, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SIGNAL_WARN, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_FETCH_NO_DATA, SQL_STATE_NO_DATA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SIGNAL_NOT_FOUND, SQL_STATE_NO_DATA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CON_COUNT_ERROR, SQL_STATE_CONNECTION_REJECTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE, SQL_STATE_CONNECTION_REJECTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_HOST_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_HANDSHAKE_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_COM_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SERVER_SHUTDOWN, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FORCING_CLOSE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_IPSOCK_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_FCNTL_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MASTER_NET_READ, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MASTER_NET_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CONNECT_TO_MASTER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADSELECT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADSTATEMENT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_SUBSELECT_NYI, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_RETSET, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_DB_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_GROUP_FIELD, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_SUM_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_IDENT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_KEYNAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PARSE_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_EMPTY_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONUNIQ_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_KEYS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_AUTO_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_DB_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_TABLE_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_NO_MATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_REGEXP_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SYNTAX_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_STRING, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_COLUMN_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_ROWS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_KEY_DOES_NOT_EXITS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_USER_LIMIT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPECIFIC_ACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_YET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FK_DEF, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_ALREADY_EXISTS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DOES_NOT_EXIST, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LILABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LABEL_REDEFINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADRETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_IGNORED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_TRANSLATED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_WRONG_NO_OF_ARGS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_COND_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NORETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_UNDECLARED_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_PARAM, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_COND, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_CURS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_VARCOND_AFTER_CURSHNDLR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_AFTER_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PROCACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_PROC_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_SQLSTATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CREATE_USER_WITH_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NOT_VAR_ARG, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_SCALE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_PRECISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_M_BIGGER_THAN_D, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_BODY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_DISPLAYWIDTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_VAR_SHADOW, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_WRONG_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_AGGREGATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MAX_PREPARED_STMT_COUNT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NON_GROUPING_FIELD_USED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_STORED_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FUNC_INEXISTENT_NAME_COLLISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_SIGNAL_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPATIAL_MUST_HAVE_GEOM_COL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TRUNCATE_ILLEGAL_FK, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OPERAND_COLUMNS, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_NULL_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NON_UNIQ_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_ENTRY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_UNIQUE, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_ENTRY_WITH_KEY_NAME, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_UNKNOWN_IN_INDEX, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATA_TOO_LONG, SQL_STATE_STRING_DATA_RIGHT_TRUNCATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CREATE_GEOMETRY_OBJECT, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATA_OUT_OF_RANGE, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TRUNCATED_WRONG_VALUE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_VALUE_FOR_TYPE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATETIME_FUNCTION_OVERFLOW, SQL_STATE_DATETIME_FIELD_OVERFLOW); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DIVISION_BY_ZERO, SQL_STATE_DIVISION_BY_ZERO); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_ALREADY_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_NOT_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_NO_PASSWORD_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_CHANGE_USER_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DA_INVALID_CONDITION_NUMBER, SQL_STATE_INVALID_CONDITION_NUMBER); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_DB_ERROR, SQL_STATE_INVALID_CATALOG_NAME); + mysqlToSql99State.put(MysqlErrorNumbers.ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER, SQL_STATE_RESIGNAL_WHEN_HANDLER_NOT_ACTIVE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER, SQL_STATE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CASE_NOT_FOUND, SQL_STATE_CASE_NOT_FOUND_FOR_CASE_STATEMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_USE_OF_NULL, SQL_STATE_SYNTAX_ERROR); // legacy, must be SQL_STATE_NULL_VALUE_NOT_ALLOWED + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_ARGUMENT_FOR_LOGARITHM, SQL_STATE_INVALID_LOGARITHM_ARGUMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CHANGE_TX_ISOLATION, SQL_STATE_ACTIVE_SQL_TRANSACTION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, SQL_STATE_READ_ONLY_SQL_TRANSACTION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_RECURSIVE_CREATE, SQL_STATE_SRE_PROHIBITED_SQL_STATEMENT_ATTEMPTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NORETURNEND, SQL_STATE_SRE_FUNCTION_EXECUTED_NO_RETURN_STATEMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR, SQL_STATE_ER_TABLE_EXISTS_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_TABLE_ERROR, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_SUCH_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_SUCH_INDEX, SQL_STATE_ER_NO_SUCH_INDEX); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_FIELDNAME, SQL_STATE_ER_DUP_FIELDNAME); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_FIELD_ERROR, SQL_STATE_ER_BAD_FIELD_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_REFERENCE, SQL_STATE_ER_BAD_FIELD_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_QUERY_INTERRUPTED, SQL_STATE_ER_QUERY_INTERRUPTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OUTOFMEMORY, SQL_STATE_MEMORY_ALLOCATION_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OUT_OF_SORTMEMORY, SQL_STATE_MEMORY_ALLOCATION_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBROLLBACK, SQL_STATE_XA_RBROLLBACK); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBDEADLOCK, SQL_STATE_XA_RBDEADLOCK); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBTIMEOUT, SQL_STATE_XA_RBTIMEOUT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RMERR, SQL_STATE_XA_RMERR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_NOTA, SQL_STATE_XAER_NOTA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_INVAL, SQL_STATE_XAER_INVAL); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_RMFAIL, SQL_STATE_XAER_RMFAIL); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_DUPID, SQL_STATE_XAER_DUPID); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_OUTSIDE, SQL_STATE_XAER_OUTSIDE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_LOCK_DEADLOCK, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + } + + /** + * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances. + * + * If 'forTruncationOnly' is true, only looks for truncation warnings, and + * actually throws DataTruncation as an exception. + * + * @param connection + * the connection to use for getting warnings. + * + * @return the SQLWarning chain (or null if no warnings) + * + * @throws SQLException + * if the warnings could not be retrieved + */ + static SQLWarning convertShowWarningsToSQLWarnings(Connection connection) throws SQLException { + return convertShowWarningsToSQLWarnings(connection, 0, false); + } + + /** + * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances. + * + * If 'forTruncationOnly' is true, only looks for truncation warnings, and + * actually throws DataTruncation as an exception. + * + * @param connection + * the connection to use for getting warnings. + * @param warningCountIfKnown + * the warning count (if known), otherwise set it to 0. + * @param forTruncationOnly + * if this method should only scan for data truncation warnings + * + * @return the SQLWarning chain (or null if no warnings) + * + * @throws SQLException + * if the warnings could not be retrieved, or if data truncation + * is being scanned for and truncations were found. + */ + static SQLWarning convertShowWarningsToSQLWarnings(Connection connection, int warningCountIfKnown, boolean forTruncationOnly) throws SQLException { + java.sql.Statement stmt = null; + java.sql.ResultSet warnRs = null; + + SQLWarning currentWarning = null; + + try { + if (warningCountIfKnown < 100) { + stmt = connection.createStatement(); + + if (stmt.getMaxRows() != 0) { + stmt.setMaxRows(0); + } + } else { + // stream large warning counts + stmt = connection.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); + stmt.setFetchSize(Integer.MIN_VALUE); + } + + /* + * +---------+------+---------------------------------------------+ | + * Level | Code | Message | + * +---------+------+---------------------------------------------+ | + * Warning | 1265 | Data truncated for column 'field1' at row 1 | + * +---------+------+---------------------------------------------+ + */ + warnRs = stmt.executeQuery("SHOW WARNINGS"); + + while (warnRs.next()) { + int code = warnRs.getInt("Code"); + + if (forTruncationOnly) { + if (code == MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED || code == MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE) { + DataTruncation newTruncation = new MysqlDataTruncation(warnRs.getString("Message"), 0, false, false, 0, 0, code); + + if (currentWarning == null) { + currentWarning = newTruncation; + } else { + currentWarning.setNextWarning(newTruncation); + } + } + } else { + //String level = warnRs.getString("Level"); + String message = warnRs.getString("Message"); + + SQLWarning newWarning = new SQLWarning(message, SQLError.mysqlToSqlState(code, connection.getUseSqlStateCodes()), code); + + if (currentWarning == null) { + currentWarning = newWarning; + } else { + currentWarning.setNextWarning(newWarning); + } + } + } + + if (forTruncationOnly && (currentWarning != null)) { + throw currentWarning; + } + + return currentWarning; + } finally { + SQLException reThrow = null; + + if (warnRs != null) { + try { + warnRs.close(); + } catch (SQLException sqlEx) { + reThrow = sqlEx; + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // ideally, we'd use chained exceptions here, but we still support JDK-1.2.x with this driver which doesn't have them.... + reThrow = sqlEx; + } + } + + if (reThrow != null) { + throw reThrow; + } + } + } + + public static void dumpSqlStatesMappingsAsXml() throws Exception { + TreeMap allErrorNumbers = new TreeMap(); + Map mysqlErrorNumbersToNames = new HashMap(); + + // Integer errorNumber = null; + + // + // First create a list of all 'known' error numbers that are mapped. + // + for (Integer errorNumber : mysqlToSql99State.keySet()) { + allErrorNumbers.put(errorNumber, errorNumber); + } + + for (Integer errorNumber : mysqlToSqlState.keySet()) { + allErrorNumbers.put(errorNumber, errorNumber); + } + + // + // Now create a list of the actual MySQL error numbers we know about + // + java.lang.reflect.Field[] possibleFields = MysqlErrorNumbers.class.getDeclaredFields(); + + for (int i = 0; i < possibleFields.length; i++) { + String fieldName = possibleFields[i].getName(); + + if (fieldName.startsWith("ER_")) { + mysqlErrorNumbersToNames.put(possibleFields[i].get(null), fieldName); + } + } + + System.out.println(""); + + for (Integer errorNumber : allErrorNumbers.keySet()) { + String sql92State = mysqlToSql99(errorNumber.intValue()); + String oldSqlState = mysqlToXOpen(errorNumber.intValue()); + + System.out.println(" "); + } + + System.out.println(""); + } + + static String get(String stateCode) { + return sqlStateMessages.get(stateCode); + } + + private static String mysqlToSql99(int errno) { + Integer err = Integer.valueOf(errno); + + if (mysqlToSql99State.containsKey(err)) { + return mysqlToSql99State.get(err); + } + + return SQL_STATE_CLI_SPECIFIC_CONDITION; + } + + /** + * Map MySQL error codes to X/Open or SQL-92 error codes + * + * @param errno + * the MySQL error code + * + * @return the corresponding X/Open or SQL-92 error code + */ + static String mysqlToSqlState(int errno, boolean useSql92States) { + if (useSql92States) { + return mysqlToSql99(errno); + } + + return mysqlToXOpen(errno); + } + + private static String mysqlToXOpen(int errno) { + Integer err = Integer.valueOf(errno); + + if (mysqlToSqlState.containsKey(err)) { + return mysqlToSqlState.get(err); + } + + return SQL_STATE_GENERAL_ERROR; + } + + /* + * SQL State Class SQLNonTransientException Subclass 08 + * SQLNonTransientConnectionException 22 SQLDataException 23 + * SQLIntegrityConstraintViolationException N/A + * SQLInvalidAuthorizationException 42 SQLSyntaxErrorException + * + * SQL State Class SQLTransientException Subclass 08 + * SQLTransientConnectionException 40 SQLTransactionRollbackException N/A + * SQLTimeoutException + */ + + public static SQLException createSQLException(String message, String sqlState, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, 0, interceptor); + } + + public static SQLException createSQLException(String message, ExceptionInterceptor interceptor) { + return createSQLException(message, interceptor, null); + } + + public static SQLException createSQLException(String message, ExceptionInterceptor interceptor, Connection conn) { + SQLException sqlEx = new SQLException(message); + return runThroughExceptionInterceptor(interceptor, sqlEx, conn); + } + + public static SQLException createSQLException(String message, String sqlState, Throwable cause, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, cause, interceptor, null); + } + + public static SQLException createSQLException(String message, String sqlState, Throwable cause, ExceptionInterceptor interceptor, Connection conn) { + SQLException sqlEx = createSQLException(message, sqlState, null); + if (sqlEx.getCause() == null) { + sqlEx.initCause(cause); + } + // Run through the exception interceptor after setting the init cause. + return runThroughExceptionInterceptor(interceptor, sqlEx, conn); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, vendorErrorCode, false, interceptor); + } + + /** + * @param message + * @param sqlState + * @param vendorErrorCode + * @param isTransient + * @param interceptor + */ + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, boolean isTransient, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, vendorErrorCode, isTransient, interceptor, null); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, boolean isTransient, ExceptionInterceptor interceptor, + Connection conn) { + try { + SQLException sqlEx = null; + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + if (isTransient) { + if (!Util.isJdbc4()) { + sqlEx = new MySQLTransientConnectionException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLTransientConnectionException", + new Class[] { String.class, String.class, Integer.TYPE }, + new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, interceptor); + } + } else if (!Util.isJdbc4()) { + sqlEx = new MySQLNonTransientConnectionException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else if (sqlState.startsWith("22")) { + if (!Util.isJdbc4()) { + sqlEx = new MySQLDataException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLDataException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else if (sqlState.startsWith("23")) { + + if (!Util.isJdbc4()) { + sqlEx = new MySQLIntegrityConstraintViolationException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else if (sqlState.startsWith("42")) { + if (!Util.isJdbc4()) { + sqlEx = new MySQLSyntaxErrorException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else if (sqlState.startsWith("40")) { + if (!Util.isJdbc4()) { + sqlEx = new MySQLTransactionRollbackException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else if (sqlState.startsWith("70100")) { + if (!Util.isJdbc4()) { + sqlEx = new MySQLQueryInterruptedException(message, sqlState, vendorErrorCode); + } else { + sqlEx = (SQLException) Util.getInstance("com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException", + new Class[] { String.class, String.class, Integer.TYPE }, new Object[] { message, sqlState, Integer.valueOf(vendorErrorCode) }, + interceptor); + } + } else { + sqlEx = new SQLException(message, sqlState, vendorErrorCode); + } + } else { + sqlEx = new SQLException(message, sqlState, vendorErrorCode); + } + + return runThroughExceptionInterceptor(interceptor, sqlEx, conn); + } catch (SQLException sqlEx) { + SQLException unexpectedEx = new SQLException( + "Unable to create correct SQLException class instance, error class/codes may be incorrect. Reason: " + Util.stackTraceToString(sqlEx), + SQL_STATE_GENERAL_ERROR); + + return runThroughExceptionInterceptor(interceptor, unexpectedEx, conn); + } + } + + public static SQLException createCommunicationsException(MySQLConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, + Exception underlyingException, ExceptionInterceptor interceptor) { + SQLException exToReturn = null; + + if (!Util.isJdbc4()) { + exToReturn = new CommunicationsException(conn, lastPacketSentTimeMs, lastPacketReceivedTimeMs, underlyingException); + } else { + + try { + exToReturn = (SQLException) Util.handleNewInstance(JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR, + new Object[] { conn, Long.valueOf(lastPacketSentTimeMs), Long.valueOf(lastPacketReceivedTimeMs), underlyingException }, interceptor); + } catch (SQLException sqlEx) { + // We should _never_ get this, but let's not swallow it either + + return sqlEx; + } + } + + return runThroughExceptionInterceptor(interceptor, exToReturn, conn); + } + + /** + * Creates a communications link failure message to be used + * in CommunicationsException that (hopefully) has some better + * information and suggestions based on heuristics. + * + * @param conn + * @param lastPacketSentTimeMs + * @param underlyingException + */ + public static String createLinkFailureMessageBasedOnHeuristics(MySQLConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, + Exception underlyingException) { + long serverTimeoutSeconds = 0; + boolean isInteractiveClient = false; + + if (conn != null) { + isInteractiveClient = conn.getInteractiveClient(); + + String serverTimeoutSecondsStr = null; + + if (isInteractiveClient) { + serverTimeoutSecondsStr = conn.getServerVariable("interactive_timeout"); + } else { + serverTimeoutSecondsStr = conn.getServerVariable("wait_timeout"); + } + + if (serverTimeoutSecondsStr != null) { + try { + serverTimeoutSeconds = Long.parseLong(serverTimeoutSecondsStr); + } catch (NumberFormatException nfe) { + serverTimeoutSeconds = 0; + } + } + } + + StringBuilder exceptionMessageBuf = new StringBuilder(); + + long nowMs = System.currentTimeMillis(); + + if (lastPacketSentTimeMs == 0) { + lastPacketSentTimeMs = nowMs; + } + + long timeSinceLastPacketSentMs = (nowMs - lastPacketSentTimeMs); + long timeSinceLastPacketSeconds = timeSinceLastPacketSentMs / 1000; + + long timeSinceLastPacketReceivedMs = (nowMs - lastPacketReceivedTimeMs); + + int dueToTimeout = DUE_TO_TIMEOUT_FALSE; + + StringBuilder timeoutMessageBuf = null; + + if (serverTimeoutSeconds != 0) { + if (timeSinceLastPacketSeconds > serverTimeoutSeconds) { + dueToTimeout = DUE_TO_TIMEOUT_TRUE; + + timeoutMessageBuf = new StringBuilder(); + + timeoutMessageBuf.append(Messages.getString("CommunicationsException.2")); + + if (!isInteractiveClient) { + timeoutMessageBuf.append(Messages.getString("CommunicationsException.3")); + } else { + timeoutMessageBuf.append(Messages.getString("CommunicationsException.4")); + } + + } + } else if (timeSinceLastPacketSeconds > DEFAULT_WAIT_TIMEOUT_SECONDS) { + dueToTimeout = DUE_TO_TIMEOUT_MAYBE; + + timeoutMessageBuf = new StringBuilder(); + + timeoutMessageBuf.append(Messages.getString("CommunicationsException.5")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.6")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.7")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.8")); + } + + if (dueToTimeout == DUE_TO_TIMEOUT_TRUE || dueToTimeout == DUE_TO_TIMEOUT_MAYBE) { + + if (lastPacketReceivedTimeMs != 0) { + Object[] timingInfo = { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }; + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfo", timingInfo)); + } else { + exceptionMessageBuf.append( + Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", new Object[] { Long.valueOf(timeSinceLastPacketSentMs) })); + } + + if (timeoutMessageBuf != null) { + exceptionMessageBuf.append(timeoutMessageBuf); + } + + exceptionMessageBuf.append(Messages.getString("CommunicationsException.11")); + exceptionMessageBuf.append(Messages.getString("CommunicationsException.12")); + exceptionMessageBuf.append(Messages.getString("CommunicationsException.13")); + + } else { + // + // Attempt to determine the reason for the underlying exception (we can only make a best-guess here) + // + + if (underlyingException instanceof BindException) { + if (conn.getLocalSocketAddress() != null && !Util.interfaceExists(conn.getLocalSocketAddress())) { + exceptionMessageBuf.append(Messages.getString("CommunicationsException.LocalSocketAddressNotAvailable")); + } else { + // too many client connections??? + exceptionMessageBuf.append(Messages.getString("CommunicationsException.TooManyClientConnections")); + } + } + } + + if (exceptionMessageBuf.length() == 0) { + // We haven't figured out a good reason, so copy it. + exceptionMessageBuf.append(Messages.getString("CommunicationsException.20")); + + if (conn != null && conn.getMaintainTimeStats() && !conn.getParanoid()) { + exceptionMessageBuf.append("\n\n"); + if (lastPacketReceivedTimeMs != 0) { + Object[] timingInfo = { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }; + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfo", timingInfo)); + } else { + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", + new Object[] { Long.valueOf(timeSinceLastPacketSentMs) })); + } + } + } + + return exceptionMessageBuf.toString(); + } + + /** + * Run exception through an ExceptionInterceptor chain. + * + * @param exInterceptor + * @param sqlEx + * @param conn + * @return + */ + private static SQLException runThroughExceptionInterceptor(ExceptionInterceptor exInterceptor, SQLException sqlEx, Connection conn) { + if (exInterceptor != null) { + SQLException interceptedEx = exInterceptor.interceptException(sqlEx, conn); + + if (interceptedEx != null) { + return interceptedEx; + } + } + return sqlEx; + } + + /** + * Create a BatchUpdateException taking in consideration the JDBC version in use. For JDBC version prior to 4.2 the updates count array has int elements + * while JDBC 4.2 and beyond uses long values. + * + * @param underlyingEx + * @param updateCounts + * @param interceptor + */ + public static SQLException createBatchUpdateException(SQLException underlyingEx, long[] updateCounts, ExceptionInterceptor interceptor) + throws SQLException { + SQLException newEx; + + if (Util.isJdbc42()) { + newEx = (SQLException) Util.getInstance("java.sql.BatchUpdateException", + new Class[] { String.class, String.class, int.class, long[].class, Throwable.class }, + new Object[] { underlyingEx.getMessage(), underlyingEx.getSQLState(), underlyingEx.getErrorCode(), updateCounts, underlyingEx }, + interceptor); + } else { // return pre-JDBC4.2 BatchUpdateException (updateCounts are limited to int[]) + newEx = new BatchUpdateException(underlyingEx.getMessage(), underlyingEx.getSQLState(), underlyingEx.getErrorCode(), + Util.truncateAndConvertToInt(updateCounts)); + newEx.initCause(underlyingEx); + } + return runThroughExceptionInterceptor(interceptor, newEx, null); + } + + /** + * Create a SQLFeatureNotSupportedException or a NotImplemented exception according to the JDBC version in use. + */ + public static SQLException createSQLFeatureNotSupportedException() throws SQLException { + SQLException newEx; + + if (Util.isJdbc4()) { + newEx = (SQLException) Util.getInstance("java.sql.SQLFeatureNotSupportedException", null, null, null); + } else { + newEx = new NotImplemented(); + } + + return newEx; + } + + /** + * Create a SQLFeatureNotSupportedException or a NotImplemented exception according to the JDBC version in use. + * + * @param message + * @param sqlState + * @param interceptor + */ + public static SQLException createSQLFeatureNotSupportedException(String message, String sqlState, ExceptionInterceptor interceptor) throws SQLException { + SQLException newEx; + + if (Util.isJdbc4()) { + newEx = (SQLException) Util.getInstance("java.sql.SQLFeatureNotSupportedException", new Class[] { String.class, String.class }, + new Object[] { message, sqlState }, interceptor); + } else { + newEx = new NotImplemented(); + } + + return runThroughExceptionInterceptor(interceptor, newEx, null); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Security.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Security.java new file mode 100644 index 0000000..d3b7c2b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Security.java @@ -0,0 +1,334 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Methods for doing secure authentication with MySQL-4.1 and newer. + */ +public class Security { + private static final char PVERSION41_CHAR = '*'; + + private static final int SHA1_HASH_SIZE = 20; + + /** + * Returns hex value for given char + */ + private static int charVal(char c) { + return ((c >= '0') && (c <= '9')) ? (c - '0') : (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 10) : (c - 'a' + 10)); + } + + /* + * Convert password in salted form to binary string password and hash-salt + * For old password this involes one more hashing + * + * SYNOPSIS get_hash_and_password() salt IN Salt to convert from pversion IN + * Password version to use hash OUT Store zero ended hash here bin_password + * OUT Store binary password here (no zero at the end) + * + * RETURN 0 for pre 4.1 passwords !0 password version char for newer + * passwords + */ + + /** + * Creates key from old password to decode scramble Used in 4.1 + * authentication with passwords stored pre-4.1 hashing. + * + * @param passwd + * the password to create the key from + * + * @return 20 byte generated key + * + * @throws NoSuchAlgorithmException + * if the message digest 'SHA-1' is not available. + */ + static byte[] createKeyFromOldPassword(String passwd) throws NoSuchAlgorithmException { + /* At first hash password to the string stored in password */ + passwd = makeScrambledPassword(passwd); + + /* Now convert it to the salt form */ + int[] salt = getSaltFromPassword(passwd); + + /* Finally get hash and bin password from salt */ + return getBinaryPassword(salt, false); + } + + /** + * @param salt + * @param usingNewPasswords + * + * @throws NoSuchAlgorithmException + * if the message digest 'SHA-1' is not available. + */ + static byte[] getBinaryPassword(int[] salt, boolean usingNewPasswords) throws NoSuchAlgorithmException { + int val = 0; + + byte[] binaryPassword = new byte[SHA1_HASH_SIZE]; /* Binary password loop pointer */ + + if (usingNewPasswords) /* New password version assumed */ { + int pos = 0; + + for (int i = 0; i < 4; i++) /* Iterate over these elements */ { + val = salt[i]; + + for (int t = 3; t >= 0; t--) { + binaryPassword[pos++] = (byte) (val & 255); + val >>= 8; /* Scroll 8 bits to get next part */ + } + } + + return binaryPassword; + } + + int offset = 0; + + for (int i = 0; i < 2; i++) /* Iterate over these elements */ { + val = salt[i]; + + for (int t = 3; t >= 0; t--) { + binaryPassword[t + offset] = (byte) (val % 256); + val >>= 8; /* Scroll 8 bits to get next part */ + } + + offset += 4; + } + + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + md.update(binaryPassword, 0, 8); + + return md.digest(); + } + + private static int[] getSaltFromPassword(String password) { + int[] result = new int[6]; + + if ((password == null) || (password.length() == 0)) { + return result; + } + + if (password.charAt(0) == PVERSION41_CHAR) { + // new password + String saltInHex = password.substring(1, 5); + + int val = 0; + + for (int i = 0; i < 4; i++) { + val = (val << 4) + charVal(saltInHex.charAt(i)); + } + + return result; + } + + int resultPos = 0; + int pos = 0; + int length = password.length(); + + while (pos < length) { + int val = 0; + + for (int i = 0; i < 8; i++) { + val = (val << 4) + charVal(password.charAt(pos++)); + } + + result[resultPos++] = val; + } + + return result; + } + + private static String longToHex(long val) { + String longHex = Long.toHexString(val); + + int length = longHex.length(); + + if (length < 8) { + int padding = 8 - length; + StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < padding; i++) { + buf.append("0"); + } + + buf.append(longHex); + + return buf.toString(); + } + + return longHex.substring(0, 8); + } + + /** + * Creates password to be stored in user database from raw string. + * + * Handles Pre-MySQL 4.1 passwords. + * + * @param password + * plaintext password + * + * @return scrambled password + * + * @throws NoSuchAlgorithmException + * if the message digest 'SHA-1' is not available. + */ + static String makeScrambledPassword(String password) throws NoSuchAlgorithmException { + long[] passwordHash = Util.hashPre41Password(password); + StringBuilder scramble = new StringBuilder(); + + scramble.append(longToHex(passwordHash[0])); + scramble.append(longToHex(passwordHash[1])); + + return scramble.toString(); + } + + /** + * Encrypt/Decrypt function used for password encryption in authentication + * + * Simple XOR is used here but it is OK as we crypt random strings + * + * @param from + * IN Data for encryption + * @param to + * OUT Encrypt data to the buffer (may be the same) + * @param scramble + * IN Scramble used for encryption + * @param length + * IN Length of data to encrypt + */ + public static void xorString(byte[] from, byte[] to, byte[] scramble, int length) { + int pos = 0; + int scrambleLength = scramble.length; + + while (pos < length) { + to[pos] = (byte) (from[pos] ^ scramble[pos % scrambleLength]); + pos++; + } + } + + /** + * Stage one password hashing, used in MySQL 4.1 password handling + * + * @param password + * plaintext password + * + * @return stage one hash of password + * + * @throws NoSuchAlgorithmException + * if the message digest 'SHA-1' is not available. + */ + static byte[] passwordHashStage1(String password) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + StringBuilder cleansedPassword = new StringBuilder(); + + int passwordLength = password.length(); + + for (int i = 0; i < passwordLength; i++) { + char c = password.charAt(i); + + if ((c == ' ') || (c == '\t')) { + continue; /* skip space in password */ + } + + cleansedPassword.append(c); + } + + return md.digest(StringUtils.getBytes(cleansedPassword.toString())); + } + + /** + * Stage two password hashing used in MySQL 4.1 password handling + * + * @param hash + * from passwordHashStage1 + * @param salt + * salt used for stage two hashing + * + * @return result of stage two password hash + * + * @throws NoSuchAlgorithmException + * if the message digest 'SHA-1' is not available. + */ + static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + // hash 4 bytes of salt + md.update(salt, 0, 4); + + md.update(hashedPassword, 0, SHA1_HASH_SIZE); + + return md.digest(); + } + + // SERVER: public_seed=create_random_string() + // send(public_seed) + // + // CLIENT: recv(public_seed) + // hash_stage1=sha1("password") + // hash_stage2=sha1(hash_stage1) + // reply=xor(hash_stage1, sha1(public_seed,hash_stage2) + // + // // this three steps are done in scramble() + // + // send(reply) + // + // + // SERVER: recv(reply) + // hash_stage1=xor(reply, sha1(public_seed,hash_stage2)) + // candidate_hash2=sha1(hash_stage1) + // check(candidate_hash2==hash_stage2) + public static byte[] scramble411(String password, String seed, String passwordEncoding) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + byte[] passwordHashStage1 = md.digest((passwordEncoding == null || passwordEncoding.length() == 0) ? StringUtils.getBytes(password) + : StringUtils.getBytes(password, passwordEncoding)); + md.reset(); + + byte[] passwordHashStage2 = md.digest(passwordHashStage1); + md.reset(); + + byte[] seedAsBytes = StringUtils.getBytes(seed, "ASCII"); // for debugging + md.update(seedAsBytes); + md.update(passwordHashStage2); + + byte[] toBeXord = md.digest(); + + int numToXor = toBeXord.length; + + for (int i = 0; i < numToXor; i++) { + toBeXord[i] = (byte) (toBeXord[i] ^ passwordHashStage1[i]); + } + + return toBeXord; + } + + /** + * Prevent construction. + */ + private Security() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SequentialBalanceStrategy.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SequentialBalanceStrategy.java new file mode 100644 index 0000000..b99d146 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SequentialBalanceStrategy.java @@ -0,0 +1,162 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * A balancing strategy that starts at a random point, and then advances in the list (wrapping around) for each new pickConnection() call. + * + * The initial point selection, and subsequent point selections are blacklist-aware. + */ +public class SequentialBalanceStrategy implements BalanceStrategy { + private int currentHostIndex = -1; + + public SequentialBalanceStrategy() { + } + + public void destroy() { + // we don't have anything to clean up + } + + public void init(Connection conn, Properties props) throws SQLException { + // we don't have anything to initialize + } + + public ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + int numHosts = configuredHosts.size(); + + SQLException ex = null; + + Map blackList = proxy.getGlobalBlacklist(); + + for (int attempts = 0; attempts < numRetries;) { + if (numHosts == 1) { + this.currentHostIndex = 0; // pathological case + } else if (this.currentHostIndex == -1) { + int random = (int) Math.floor((Math.random() * numHosts)); + + for (int i = random; i < numHosts; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + break; + } + } + + if (this.currentHostIndex == -1) { + for (int i = 0; i < random; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + break; + } + } + } + + if (this.currentHostIndex == -1) { + blackList = proxy.getGlobalBlacklist(); // it may have changed + // and the proxy returns a copy + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; // retry + } + } else { + + int i = this.currentHostIndex + 1; + boolean foundGoodHost = false; + + for (; i < numHosts; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + foundGoodHost = true; + break; + } + } + + if (!foundGoodHost) { + for (i = 0; i < this.currentHostIndex; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + foundGoodHost = true; + break; + } + } + } + + if (!foundGoodHost) { + blackList = proxy.getGlobalBlacklist(); // it may have changed + // and the proxy returns a copy + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; // retry + } + } + + String hostPortSpec = configuredHosts.get(this.currentHostIndex); + + ConnectionImpl conn = liveConnections.get(hostPortSpec); + + if (conn == null) { + try { + conn = proxy.createConnectionForHost(hostPortSpec); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (proxy.shouldExceptionTriggerConnectionSwitch(sqlEx)) { + + proxy.addToGlobalBlacklist(hostPortSpec); + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; + } + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ServerPreparedStatement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ServerPreparedStatement.java new file mode 100644 index 0000000..4241410 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/ServerPreparedStatement.java @@ -0,0 +1,2805 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.log.LogUtils; +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements. + */ +public class ServerPreparedStatement extends PreparedStatement { + private static final Constructor JDBC_4_SPS_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.JDBC42ServerPreparedStatement" : "com.mysql.jdbc.JDBC4ServerPreparedStatement"; + JDBC_4_SPS_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { MySQLConnection.class, String.class, String.class, Integer.TYPE, Integer.TYPE }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_SPS_CTOR = null; + } + } + + protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192; + + public static class BatchedBindValues { + public BindValue[] batchedParameterValues; + + BatchedBindValues(BindValue[] paramVals) { + int numParams = paramVals.length; + + this.batchedParameterValues = new BindValue[numParams]; + + for (int i = 0; i < numParams; i++) { + this.batchedParameterValues[i] = new BindValue(paramVals[i]); + } + } + } + + public static class BindValue { + + public long boundBeforeExecutionNum = 0; + + public long bindLength; /* Default length of data */ + + public int bufferType; /* buffer type */ + + public double doubleBinding; + + public float floatBinding; + + public boolean isLongData; /* long data indicator */ + + public boolean isNull; /* NULL indicator */ + + public boolean isSet = false; /* has this parameter been set? */ + + public long longBinding; /* all integral values are stored here */ + + public Object value; /* The value to store */ + + BindValue() { + } + + BindValue(BindValue copyMe) { + this.value = copyMe.value; + this.isSet = copyMe.isSet; + this.isLongData = copyMe.isLongData; + this.isNull = copyMe.isNull; + this.bufferType = copyMe.bufferType; + this.bindLength = copyMe.bindLength; + this.longBinding = copyMe.longBinding; + this.floatBinding = copyMe.floatBinding; + this.doubleBinding = copyMe.doubleBinding; + } + + void reset() { + this.isNull = false; + this.isSet = false; + this.value = null; + this.isLongData = false; + + this.longBinding = 0L; + this.floatBinding = 0; + this.doubleBinding = 0D; + } + + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean quoteIfNeeded) { + if (this.isLongData) { + return "' STREAM DATA '"; + } + + if (this.isNull) { + return "NULL"; + } + + switch (this.bufferType) { + case MysqlDefs.FIELD_TYPE_TINY: + case MysqlDefs.FIELD_TYPE_SHORT: + case MysqlDefs.FIELD_TYPE_LONG: + case MysqlDefs.FIELD_TYPE_LONGLONG: + return String.valueOf(this.longBinding); + case MysqlDefs.FIELD_TYPE_FLOAT: + return String.valueOf(this.floatBinding); + case MysqlDefs.FIELD_TYPE_DOUBLE: + return String.valueOf(this.doubleBinding); + case MysqlDefs.FIELD_TYPE_TIME: + case MysqlDefs.FIELD_TYPE_DATE: + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + if (quoteIfNeeded) { + return "'" + String.valueOf(this.value) + "'"; + } + return String.valueOf(this.value); + + default: + if (this.value instanceof byte[]) { + return "byte data"; + } + if (quoteIfNeeded) { + return "'" + String.valueOf(this.value) + "'"; + } + return String.valueOf(this.value); + } + } + + long getBoundLength() { + if (this.isNull) { + return 0; + } + + if (this.isLongData) { + return this.bindLength; + } + + switch (this.bufferType) { + + case MysqlDefs.FIELD_TYPE_TINY: + return 1; + case MysqlDefs.FIELD_TYPE_SHORT: + return 2; + case MysqlDefs.FIELD_TYPE_LONG: + return 4; + case MysqlDefs.FIELD_TYPE_LONGLONG: + return 8; + case MysqlDefs.FIELD_TYPE_FLOAT: + return 4; + case MysqlDefs.FIELD_TYPE_DOUBLE: + return 8; + case MysqlDefs.FIELD_TYPE_TIME: + return 9; + case MysqlDefs.FIELD_TYPE_DATE: + return 7; + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + return 11; + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + if (this.value instanceof byte[]) { + return ((byte[]) this.value).length; + } + return ((String) this.value).length(); + + default: + return 0; + } + } + } + + /* 1 (length) + 2 (year) + 1 (month) + 1 (day) */ + //private static final byte MAX_DATE_REP_LENGTH = (byte) 5; + + /* + * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1 + * (second) + 4 (microseconds) + */ + //private static final byte MAX_DATETIME_REP_LENGTH = 12; + + /* + * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1 + * (seconds) + 4 (microseconds) + */ + //private static final byte MAX_TIME_REP_LENGTH = 13; + + private boolean hasOnDuplicateKeyUpdate = false; + + private void storeTime(Buffer intoBuf, Time tm) throws SQLException { + + intoBuf.ensureCapacity(9); + intoBuf.writeByte((byte) 8); // length + intoBuf.writeByte((byte) 0); // neg flag + intoBuf.writeLong(0); // tm->day, not used + + Calendar sessionCalendar = getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar) { + java.util.Date oldTime = sessionCalendar.getTime(); + try { + sessionCalendar.setTime(tm); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND)); + + // intoBuf.writeLongInt(0); // tm-second_part + } finally { + sessionCalendar.setTime(oldTime); + } + } + } + + /** + * Flag indicating whether or not the long parameters have been 'switched' + * back to normal parameters. We can not execute() if clearParameters() + * hasn't been called in this case. + */ + private boolean detectedLongParameterSwitch = false; + + /** + * The number of fields in the result set (if any) for this + * PreparedStatement. + */ + private int fieldCount; + + /** Has this prepared statement been marked invalid? */ + private boolean invalid = false; + + /** If this statement has been marked invalid, what was the reason? */ + private SQLException invalidationException; + + private Buffer outByteBuffer; + + /** Bind values for individual fields */ + private BindValue[] parameterBindings; + + /** Field-level metadata for parameters */ + private Field[] parameterFields; + + /** Field-level metadata for result sets. */ + private Field[] resultFields; + + /** Do we need to send/resend types to the server? */ + private boolean sendTypesToServer = false; + + /** The ID that the server uses to identify this PreparedStatement */ + private long serverStatementId; + + /** The type used for string bindings, changes from version-to-version */ + private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING; + + private boolean serverNeedsResetBeforeEachExecution; + + /** + * Creates a prepared statement instance -- We need to provide factory-style + * methods so we can support both JDBC3 (and older) and JDBC4 runtimes, + * otherwise the class verifier complains when it tries to load JDBC4-only + * interface classes that are present in JDBC4 method signatures. + */ + + protected static ServerPreparedStatement getInstance(MySQLConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) + throws SQLException { + if (!Util.isJdbc4()) { + return new ServerPreparedStatement(conn, sql, catalog, resultSetType, resultSetConcurrency); + } + + try { + return (ServerPreparedStatement) JDBC_4_SPS_CTOR + .newInstance(new Object[] { conn, sql, catalog, Integer.valueOf(resultSetType), Integer.valueOf(resultSetConcurrency) }); + } catch (IllegalArgumentException e) { + throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR); + } catch (InstantiationException e) { + throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR); + } catch (IllegalAccessException e) { + throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR); + } catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + + if (target instanceof SQLException) { + throw (SQLException) target; + } + + throw new SQLException(target.toString(), SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + /** + * Creates a new ServerPreparedStatement object. + * + * @param conn + * the connection creating us. + * @param sql + * the SQL containing the statement to prepare. + * @param catalog + * the catalog in use when we were created. + * + * @throws SQLException + * If an error occurs + */ + protected ServerPreparedStatement(MySQLConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) throws SQLException { + super(conn, catalog); + + checkNullOrEmptyQuery(sql); + + int startOfStatement = findStartOfStatement(sql); + + this.firstCharOfStmt = StringUtils.firstAlphaCharUc(sql, startOfStatement); + + this.hasOnDuplicateKeyUpdate = this.firstCharOfStmt == 'I' && containsOnDuplicateKeyInString(sql); + + if (this.connection.versionMeetsMinimum(5, 0, 0)) { + this.serverNeedsResetBeforeEachExecution = !this.connection.versionMeetsMinimum(5, 0, 3); + } else { + this.serverNeedsResetBeforeEachExecution = !this.connection.versionMeetsMinimum(4, 1, 10); + } + + this.useAutoSlowLog = this.connection.getAutoSlowLog(); + this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23); + + String statementComment = this.connection.getStatementComment(); + + this.originalSql = (statementComment == null) ? sql : "/* " + statementComment + " */ " + sql; + + if (this.connection.versionMeetsMinimum(4, 1, 2)) { + this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING; + } else { + this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING; + } + + try { + serverPrepare(sql); + } catch (SQLException sqlEx) { + realClose(false, true); + // don't wrap SQLExceptions + throw sqlEx; + } catch (Exception ex) { + realClose(false, true); + + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } + + setResultSetType(resultSetType); + setResultSetConcurrency(resultSetConcurrency); + + this.parameterTypes = new int[this.parameterCount]; + } + + /** + * JDBC 2.0 Add a set of parameters to the batch. + * + * @exception SQLException + * if a database-access error occurs. + * + * @see StatementImpl#addBatch + */ + @Override + public void addBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.batchedArgs == null) { + this.batchedArgs = new ArrayList(); + } + + this.batchedArgs.add(new BatchedBindValues(this.parameterBindings)); + } + } + + @Override + public String asSql(boolean quoteStreamsAndUnknowns) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + + PreparedStatement pStmtForSub = null; + + try { + pStmtForSub = PreparedStatement.getInstance(this.connection, this.originalSql, this.currentCatalog); + + int numParameters = pStmtForSub.parameterCount; + int ourNumParameters = this.parameterCount; + + for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) { + if (this.parameterBindings[i] != null) { + if (this.parameterBindings[i].isNull) { + pStmtForSub.setNull(i + 1, Types.NULL); + } else { + BindValue bindValue = this.parameterBindings[i]; + + // + // Handle primitives first + // + switch (bindValue.bufferType) { + + case MysqlDefs.FIELD_TYPE_TINY: + pStmtForSub.setByte(i + 1, (byte) bindValue.longBinding); + break; + case MysqlDefs.FIELD_TYPE_SHORT: + pStmtForSub.setShort(i + 1, (short) bindValue.longBinding); + break; + case MysqlDefs.FIELD_TYPE_LONG: + pStmtForSub.setInt(i + 1, (int) bindValue.longBinding); + break; + case MysqlDefs.FIELD_TYPE_LONGLONG: + pStmtForSub.setLong(i + 1, bindValue.longBinding); + break; + case MysqlDefs.FIELD_TYPE_FLOAT: + pStmtForSub.setFloat(i + 1, bindValue.floatBinding); + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + pStmtForSub.setDouble(i + 1, bindValue.doubleBinding); + break; + default: + pStmtForSub.setObject(i + 1, this.parameterBindings[i].value); + break; + } + } + } + } + + return pStmtForSub.asSql(quoteStreamsAndUnknowns); + } finally { + if (pStmtForSub != null) { + try { + pStmtForSub.close(); + } catch (SQLException sqlEx) { + // ignore + } + } + } + } + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.Statement#checkClosed() + */ + @Override + protected MySQLConnection checkClosed() throws SQLException { + if (this.invalid) { + throw this.invalidationException; + } + + return super.checkClosed(); + } + + /** + * @see java.sql.PreparedStatement#clearParameters() + */ + @Override + public void clearParameters() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + clearParametersInternal(true); + } + } + + private void clearParametersInternal(boolean clearServerParameters) throws SQLException { + boolean hadLongData = false; + + if (this.parameterBindings != null) { + for (int i = 0; i < this.parameterCount; i++) { + if ((this.parameterBindings[i] != null) && this.parameterBindings[i].isLongData) { + hadLongData = true; + } + + this.parameterBindings[i].reset(); + } + } + + if (clearServerParameters && hadLongData) { + serverResetStatement(); + + this.detectedLongParameterSwitch = false; + } + } + + protected boolean isCached = false; + + private boolean useAutoSlowLog; + + private Calendar serverTzCalendar; + + private Calendar defaultTzCalendar; + + protected void setClosed(boolean flag) { + this.isClosed = flag; + } + + /** + * @see java.sql.Statement#close() + */ + @Override + public void close() throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (this.isCached && isPoolable() && !this.isClosed) { + clearParameters(); + this.isClosed = true; + this.connection.recachePreparedStatement(this); + return; + } + + realClose(true, true); + } + } + + private void dumpCloseForTestcase() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + StringBuilder buf = new StringBuilder(); + this.connection.generateConnectionCommentBlock(buf); + buf.append("DEALLOCATE PREPARE debug_stmt_"); + buf.append(this.statementId); + buf.append(";\n"); + + this.connection.dumpTestcaseQuery(buf.toString()); + } + } + + private void dumpExecuteForTestcase() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < this.parameterCount; i++) { + this.connection.generateConnectionCommentBlock(buf); + + buf.append("SET @debug_stmt_param"); + buf.append(this.statementId); + buf.append("_"); + buf.append(i); + buf.append("="); + + if (this.parameterBindings[i].isNull) { + buf.append("NULL"); + } else { + buf.append(this.parameterBindings[i].toString(true)); + } + + buf.append(";\n"); + } + + this.connection.generateConnectionCommentBlock(buf); + + buf.append("EXECUTE debug_stmt_"); + buf.append(this.statementId); + + if (this.parameterCount > 0) { + buf.append(" USING "); + for (int i = 0; i < this.parameterCount; i++) { + if (i > 0) { + buf.append(", "); + } + + buf.append("@debug_stmt_param"); + buf.append(this.statementId); + buf.append("_"); + buf.append(i); + + } + } + + buf.append(";\n"); + + this.connection.dumpTestcaseQuery(buf.toString()); + } + } + + private void dumpPrepareForTestcase() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + StringBuilder buf = new StringBuilder(this.originalSql.length() + 64); + + this.connection.generateConnectionCommentBlock(buf); + + buf.append("PREPARE debug_stmt_"); + buf.append(this.statementId); + buf.append(" FROM \""); + buf.append(this.originalSql); + buf.append("\";\n"); + + this.connection.dumpTestcaseQuery(buf.toString()); + } + } + + @Override + protected long[] executeBatchSerially(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.2") + Messages.getString("ServerPreparedStatement.3"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + clearWarnings(); + + // Store this for later, we're going to 'swap' them out + // as we execute each batched statement... + BindValue[] oldBindValues = this.parameterBindings; + + try { + long[] updateCounts = null; + + if (this.batchedArgs != null) { + int nbrCommands = this.batchedArgs.size(); + updateCounts = new long[nbrCommands]; + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList(nbrCommands); + } + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + int commandIndex = 0; + + BindValue[] previousBindValuesForBatch = null; + + CancelTask timeoutTask = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && batchTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, batchTimeout); + } + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + Object arg = this.batchedArgs.get(commandIndex); + + try { + if (arg instanceof String) { + updateCounts[commandIndex] = executeUpdateInternal((String) arg, true, this.retrieveGeneratedKeys); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString((String) arg) ? 1 : 0); + } else { + this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues; + + // We need to check types each time, as the user might have bound different types in each addBatch() + + if (previousBindValuesForBatch != null) { + for (int j = 0; j < this.parameterBindings.length; j++) { + if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) { + this.sendTypesToServer = true; + + break; + } + } + } + + try { + updateCounts[commandIndex] = executeUpdateInternal(false, true); + } finally { + previousBindValuesForBatch = this.parameterBindings; + } + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(containsOnDuplicateKeyUpdateInSQL() ? 1 : 0); + } + } catch (SQLException ex) { + updateCounts[commandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[commandIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, commandIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + this.parameterBindings = oldBindValues; + this.sendTypesToServer = true; + + clearBatch(); + } + } + } + + /** + * @see com.mysql.jdbc.PreparedStatement#executeInternal(int, com.mysql.jdbc.Buffer, boolean, boolean) + */ + @Override + protected com.mysql.jdbc.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, Buffer sendPacket, boolean createStreamingResultSet, + boolean queryIsSelectOnly, Field[] metadataFromCache, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.numberOfExecutions++; + + // We defer to server-side execution + try { + return serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadataFromCache); + } catch (SQLException sqlEx) { + // don't wrap SQLExceptions + if (this.connection.getEnablePacketDebug()) { + this.connection.getIO().dumpPacketRingBuffer(); + } + + if (this.connection.getDumpQueriesOnException()) { + String extractedSql = toString(); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + + sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf.toString(), getExceptionInterceptor()); + } + + throw sqlEx; + } catch (Exception ex) { + if (this.connection.getEnablePacketDebug()) { + this.connection.getIO().dumpPacketRingBuffer(); + } + + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + + if (this.connection.getDumpQueriesOnException()) { + String extractedSql = toString(); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + + sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf.toString(), getExceptionInterceptor()); + } + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + } + + /** + * @see com.mysql.jdbc.PreparedStatement#fillSendPacket() + */ + @Override + protected Buffer fillSendPacket() throws SQLException { + return null; // we don't use this type of packet + } + + /** + * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte, java.io.InputStream, boolean, int) + */ + @Override + protected Buffer fillSendPacket(byte[][] batchedParameterStrings, InputStream[] batchedParameterStreams, boolean[] batchedIsStream, + int[] batchedStreamLengths) throws SQLException { + return null; // we don't use this type of packet + } + + /** + * Returns the structure representing the value that (can be)/(is) + * bound at the given parameter index. + * + * @param parameterIndex + * 1-based + * @param forLongData + * is this for a stream? + * @throws SQLException + */ + protected BindValue getBinding(int parameterIndex, boolean forLongData) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.parameterBindings.length == 0) { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.8"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + parameterIndex--; + + if ((parameterIndex < 0) || (parameterIndex >= this.parameterBindings.length)) { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.9") + (parameterIndex + 1) + + Messages.getString("ServerPreparedStatement.10") + this.parameterBindings.length, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (this.parameterBindings[parameterIndex] == null) { + this.parameterBindings[parameterIndex] = new BindValue(); + } else { + if (this.parameterBindings[parameterIndex].isLongData && !forLongData) { + this.detectedLongParameterSwitch = true; + } + } + + return this.parameterBindings[parameterIndex]; + } + } + + /** + * Return current bind values for use by Statement Interceptors. + * + * @return the bind values as set by setXXX and stored by addBatch + * @see #executeBatch() + * @see #addBatch() + */ + public BindValue[] getParameterBindValues() { + return this.parameterBindings; + } + + /** + * @see com.mysql.jdbc.PreparedStatement#getBytes(int) + */ + byte[] getBytes(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + BindValue bindValue = getBinding(parameterIndex, false); + + if (bindValue.isNull) { + return null; + } else if (bindValue.isLongData) { + throw SQLError.createSQLFeatureNotSupportedException(); + } else { + if (this.outByteBuffer == null) { + this.outByteBuffer = new Buffer(this.connection.getNetBufferLength()); + } + + this.outByteBuffer.clear(); + + int originalPosition = this.outByteBuffer.getPosition(); + + storeBinding(this.outByteBuffer, bindValue, this.connection.getIO()); + + int newPosition = this.outByteBuffer.getPosition(); + + int length = newPosition - originalPosition; + + byte[] valueAsBytes = new byte[length]; + + System.arraycopy(this.outByteBuffer.getByteBuffer(), originalPosition, valueAsBytes, 0, length); + + return valueAsBytes; + } + } + } + + /** + * @see java.sql.PreparedStatement#getMetaData() + */ + @Override + public java.sql.ResultSetMetaData getMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.resultFields == null) { + return null; + } + + return new ResultSetMetaData(this.resultFields, this.connection.getUseOldAliasMetadataBehavior(), this.connection.getYearIsDateType(), + getExceptionInterceptor()); + } + } + + /** + * @see java.sql.PreparedStatement#getParameterMetaData() + */ + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.parameterMetaData == null) { + this.parameterMetaData = new MysqlParameterMetadata(this.parameterFields, this.parameterCount, getExceptionInterceptor()); + } + + return this.parameterMetaData; + } + } + + /** + * @see com.mysql.jdbc.PreparedStatement#isNull(int) + */ + @Override + boolean isNull(int paramIndex) { + throw new IllegalArgumentException(Messages.getString("ServerPreparedStatement.7")); + } + + /** + * Closes this connection and frees all resources. + * + * @param calledExplicitly + * was this called from close()? + * + * @throws SQLException + * if an error occurs + */ + @Override + protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + if (this.connection != null) { + if (this.connection.getAutoGenerateTestcaseScript()) { + dumpCloseForTestcase(); + } + + // + // Don't communicate with the server if we're being called from the finalizer... + // + // This will leak server resources, but if we don't do this, we'll deadlock (potentially, because there's no guarantee when, what order, and + // what concurrency finalizers will be called with). Well-behaved programs won't rely on finalizers to clean up their statements. + // + + SQLException exceptionDuringClose = null; + + if (calledExplicitly && !this.connection.isClosed()) { + synchronized (this.connection.getConnectionMutex()) { + try { + + MysqlIO mysql = this.connection.getIO(); + + Buffer packet = mysql.getSharedSendPacket(); + + packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT); + packet.writeLong(this.serverStatementId); + + mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null, packet, true, null, 0); + } catch (SQLException sqlEx) { + exceptionDuringClose = sqlEx; + } + } + } + + if (this.isCached) { + this.connection.decachePreparedStatement(this); + } + super.realClose(calledExplicitly, closeOpenResults); + + clearParametersInternal(false); + this.parameterBindings = null; + + this.parameterFields = null; + this.resultFields = null; + + if (exceptionDuringClose != null) { + throw exceptionDuringClose; + } + } + } + } + + /** + * Used by Connection when auto-reconnecting to retrieve 'lost' prepared + * statements. + * + * @throws SQLException + * if an error occurs. + */ + protected void rePrepare() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.invalidationException = null; + + try { + serverPrepare(this.originalSql); + } catch (SQLException sqlEx) { + // don't wrap SQLExceptions + this.invalidationException = sqlEx; + } catch (Exception ex) { + this.invalidationException = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + this.invalidationException.initCause(ex); + } + + if (this.invalidationException != null) { + this.invalid = true; + + this.parameterBindings = null; + + this.parameterFields = null; + this.resultFields = null; + + if (this.results != null) { + try { + this.results.close(); + } catch (Exception ex) { + } + } + + if (this.generatedKeysResults != null) { + try { + this.generatedKeysResults.close(); + } catch (Exception ex) { + } + } + + try { + closeAllOpenResults(); + } catch (Exception e) { + } + + if (this.connection != null) { + if (!this.connection.getDontTrackOpenResources()) { + this.connection.unregisterStatement(this); + } + } + } + } + } + + @Override + boolean isCursorRequired() throws SQLException { + // we only create cursor-backed result sets if + // a) The query is a SELECT + // b) The server supports it + // c) We know it is forward-only (note this doesn't preclude updatable result sets) + // d) The user has set a fetch size + return this.resultFields != null && this.connection.isCursorFetchEnabled() && getResultSetType() == ResultSet.TYPE_FORWARD_ONLY + && getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY && getFetchSize() > 0; + } + + /** + * Tells the server to execute this prepared statement with the current + * parameter bindings. + * + *
    +     * 
    +     * 
    +     *    -   Server gets the command 'COM_EXECUTE' to execute the
    +     *        previously         prepared query. If there is any param markers;
    +     *  then client will send the data in the following format:
    +     * 
    +     *  [COM_EXECUTE:1]
    +     *  [STMT_ID:4]
    +     *  [NULL_BITS:(param_count+7)/8)]
    +     *  [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
    +     *  [[length]data]
    +     *  [[length]data] .. [[length]data].
    +     * 
    +     *  (Note: Except for string/binary types; all other types will not be
    +     *  supplied with length field)
    +     * 
    +     * 
    +     * 
    + * + * @param maxRowsToRetrieve + * @param createStreamingResultSet + * + * @throws SQLException + */ + private com.mysql.jdbc.ResultSetInternalMethods serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, Field[] metadataFromCache) + throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MysqlIO mysql = this.connection.getIO(); + + if (mysql.shouldIntercept()) { + ResultSetInternalMethods interceptedResults = mysql.invokeStatementInterceptorsPre(this.originalSql, this, true); + + if (interceptedResults != null) { + return interceptedResults; + } + } + + if (this.detectedLongParameterSwitch) { + // Check when values were bound + boolean firstFound = false; + long boundTimeToCheck = 0; + + for (int i = 0; i < this.parameterCount - 1; i++) { + if (this.parameterBindings[i].isLongData) { + if (firstFound && boundTimeToCheck != this.parameterBindings[i].boundBeforeExecutionNum) { + throw SQLError.createSQLException( + Messages.getString("ServerPreparedStatement.11") + Messages.getString("ServerPreparedStatement.12"), + SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + firstFound = true; + boundTimeToCheck = this.parameterBindings[i].boundBeforeExecutionNum; + } + } + + // Okay, we've got all "newly"-bound streams, so reset server-side state to clear out previous bindings + + serverResetStatement(); + } + + // Check bindings + for (int i = 0; i < this.parameterCount; i++) { + if (!this.parameterBindings[i].isSet) { + throw SQLError.createSQLException( + Messages.getString("ServerPreparedStatement.13") + (i + 1) + Messages.getString("ServerPreparedStatement.14"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + // + // Send all long data + // + for (int i = 0; i < this.parameterCount; i++) { + if (this.parameterBindings[i].isLongData) { + serverLongData(i, this.parameterBindings[i]); + } + } + + if (this.connection.getAutoGenerateTestcaseScript()) { + dumpExecuteForTestcase(); + } + + // + // store the parameter values + // + + Buffer packet = mysql.getSharedSendPacket(); + + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_EXECUTE); + packet.writeLong(this.serverStatementId); + + if (this.connection.versionMeetsMinimum(4, 1, 2)) { + if (isCursorRequired()) { + packet.writeByte(MysqlDefs.OPEN_CURSOR_FLAG); + } else { + packet.writeByte((byte) 0); // placeholder for flags + } + + packet.writeLong(1); // placeholder for parameter iterations + } + + /* Reserve place for null-marker bytes */ + int nullCount = (this.parameterCount + 7) / 8; + + // if (mysql.versionMeetsMinimum(4, 1, 2)) { + // nullCount = (this.parameterCount + 9) / 8; + // } + int nullBitsPosition = packet.getPosition(); + + for (int i = 0; i < nullCount; i++) { + packet.writeByte((byte) 0); + } + + byte[] nullBitsBuffer = new byte[nullCount]; + + /* In case if buffers (type) altered, indicate to server */ + packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0); + + if (this.sendTypesToServer) { + /* + * Store types of parameters in first in first package that is sent to the server. + */ + for (int i = 0; i < this.parameterCount; i++) { + packet.writeInt(this.parameterBindings[i].bufferType); + } + } + + // + // store the parameter values + // + for (int i = 0; i < this.parameterCount; i++) { + if (!this.parameterBindings[i].isLongData) { + if (!this.parameterBindings[i].isNull) { + storeBinding(packet, this.parameterBindings[i], mysql); + } else { + nullBitsBuffer[i / 8] |= (1 << (i & 7)); + } + } + } + + // + // Go back and write the NULL flags to the beginning of the packet + // + int endPosition = packet.getPosition(); + packet.setPosition(nullBitsPosition); + packet.writeBytesNoNull(nullBitsBuffer); + packet.setPosition(endPosition); + + long begin = 0; + + boolean logSlowQueries = this.connection.getLogSlowQueries(); + boolean gatherPerformanceMetrics = this.connection.getGatherPerformanceMetrics(); + + if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) { + begin = mysql.getCurrentTimeNanosOrMillis(); + } + + resetCancelledState(); + + CancelTask timeoutTask = null; + + try { + // Get this before executing to avoid a shared packet pollution in the case some other query is issued internally, such as when using I_S. + String queryAsString = ""; + if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) { + queryAsString = asSql(true); + } + + if (this.connection.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && this.connection.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + this.connection.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis); + } + + statementBegins(); + + Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE, null, packet, false, null, 0); + + long queryEndTime = 0L; + + if (logSlowQueries || gatherPerformanceMetrics || this.profileSQL) { + queryEndTime = mysql.getCurrentTimeNanosOrMillis(); + } + + if (timeoutTask != null) { + timeoutTask.cancel(); + + this.connection.getCancelTimer().purge(); + + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + SQLException cause = null; + + if (this.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + resetCancelledState(); + + throw cause; + } + } + + boolean queryWasSlow = false; + + if (logSlowQueries || gatherPerformanceMetrics) { + long elapsedTime = queryEndTime - begin; + + if (logSlowQueries) { + if (this.useAutoSlowLog) { + queryWasSlow = elapsedTime > this.connection.getSlowQueryThresholdMillis(); + } else { + queryWasSlow = this.connection.isAbonormallyLongQuery(elapsedTime); + + this.connection.reportQueryTime(elapsedTime); + } + } + + if (queryWasSlow) { + + StringBuilder mesgBuf = new StringBuilder(48 + this.originalSql.length()); + mesgBuf.append(Messages.getString("ServerPreparedStatement.15")); + mesgBuf.append(mysql.getSlowQueryThreshold()); + mesgBuf.append(Messages.getString("ServerPreparedStatement.15a")); + mesgBuf.append(elapsedTime); + mesgBuf.append(Messages.getString("ServerPreparedStatement.16")); + + mesgBuf.append("as prepared: "); + mesgBuf.append(this.originalSql); + mesgBuf.append("\n\n with parameters bound:\n\n"); + mesgBuf.append(queryAsString); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, "", this.currentCatalog, this.connection.getId(), getId(), + 0, System.currentTimeMillis(), elapsedTime, mysql.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), mesgBuf.toString())); + } + + if (gatherPerformanceMetrics) { + this.connection.registerQueryExecutionTime(elapsedTime); + } + } + + this.connection.incrementNumberOfPreparedExecutes(); + + if (this.profileSQL) { + this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_EXECUTE, "", this.currentCatalog, this.connectionId, this.statementId, -1, + System.currentTimeMillis(), mysql.getCurrentTimeNanosOrMillis() - begin, mysql.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), truncateQueryToLog(queryAsString))); + } + + com.mysql.jdbc.ResultSetInternalMethods rs = mysql.readAllResults(this, maxRowsToRetrieve, this.resultSetType, this.resultSetConcurrency, + createStreamingResultSet, this.currentCatalog, resultPacket, true, this.fieldCount, metadataFromCache); + + if (mysql.shouldIntercept()) { + ResultSetInternalMethods interceptedResults = mysql.invokeStatementInterceptorsPost(this.originalSql, this, rs, true, null); + + if (interceptedResults != null) { + rs = interceptedResults; + } + } + + if (this.profileSQL) { + long fetchEndTime = mysql.getCurrentTimeNanosOrMillis(); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH, "", this.currentCatalog, this.connection.getId(), getId(), 0 /* + * FIXME + * rs. + * resultId + */, + System.currentTimeMillis(), (fetchEndTime - queryEndTime), mysql.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), null)); + } + + if (queryWasSlow && this.connection.getExplainSlowQueries()) { + mysql.explainSlowQuery(StringUtils.getBytes(queryAsString), queryAsString); + } + + if (!createStreamingResultSet && this.serverNeedsResetBeforeEachExecution) { + serverResetStatement(); // clear any long data... + } + + this.sendTypesToServer = false; + this.results = rs; + + if (mysql.hadWarnings()) { + mysql.scanForAndThrowDataTruncation(); + } + + return rs; + } catch (SQLException sqlEx) { + if (mysql.shouldIntercept()) { + mysql.invokeStatementInterceptorsPost(this.originalSql, this, null, true, sqlEx); + } + + throw sqlEx; + } finally { + this.statementExecuting.set(false); + + if (timeoutTask != null) { + timeoutTask.cancel(); + this.connection.getCancelTimer().purge(); + } + } + } + } + + /** + * Sends stream-type data parameters to the server. + * + *
    +     * 
    +     *  Long data handling:
    +     * 
    +     *  - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
    +     *  - The packet recieved will have the format as:
    +     *    [COM_LONG_DATA:     1][STMT_ID:4][parameter_number:2][type:2][data]
    +     *  - Checks if the type is specified by client, and if yes reads the type,
    +     *    and  stores the data in that format.
    +     *  - It's up to the client to check for read data ended. The server doesn't
    +     *    care;  and also server doesn't notify to the client that it got the
    +     *    data  or not; if there is any error; then during execute; the error
    +     *    will  be returned
    +     * 
    +     * 
    + * + * @param parameterIndex + * @param longData + * + * @throws SQLException + * if an error occurs. + */ + private void serverLongData(int parameterIndex, BindValue longData) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MysqlIO mysql = this.connection.getIO(); + + Buffer packet = mysql.getSharedSendPacket(); + + Object value = longData.value; + + if (value instanceof byte[]) { + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); + packet.writeLong(this.serverStatementId); + packet.writeInt((parameterIndex)); + + packet.writeBytesNoNull((byte[]) longData.value); + + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + } else if (value instanceof InputStream) { + storeStream(mysql, parameterIndex, packet, (InputStream) value); + } else if (value instanceof java.sql.Blob) { + storeStream(mysql, parameterIndex, packet, ((java.sql.Blob) value).getBinaryStream()); + } else if (value instanceof Reader) { + storeReader(mysql, parameterIndex, packet, (Reader) value); + } else { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.18") + value.getClass().getName() + "'", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + private void serverPrepare(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MysqlIO mysql = this.connection.getIO(); + + if (this.connection.getAutoGenerateTestcaseScript()) { + dumpPrepareForTestcase(); + } + + try { + long begin = 0; + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { + this.isLoadDataQuery = true; + } else { + this.isLoadDataQuery = false; + } + + if (this.connection.getProfileSql()) { + begin = System.currentTimeMillis(); + } + + String characterEncoding = null; + String connectionEncoding = this.connection.getEncoding(); + + if (!this.isLoadDataQuery && this.connection.getUseUnicode() && (connectionEncoding != null)) { + characterEncoding = connectionEncoding; + } + + Buffer prepareResultPacket = mysql.sendCommand(MysqlDefs.COM_PREPARE, sql, null, false, characterEncoding, 0); + + if (this.connection.versionMeetsMinimum(4, 1, 1)) { + // 4.1.1 and newer use the first byte as an 'ok' or 'error' flag, so move the buffer pointer past it to start reading the statement id. + prepareResultPacket.setPosition(1); + } else { + // 4.1.0 doesn't use the first byte as an 'ok' or 'error' flag + prepareResultPacket.setPosition(0); + } + + this.serverStatementId = prepareResultPacket.readLong(); + this.fieldCount = prepareResultPacket.readInt(); + this.parameterCount = prepareResultPacket.readInt(); + this.parameterBindings = new BindValue[this.parameterCount]; + + for (int i = 0; i < this.parameterCount; i++) { + this.parameterBindings[i] = new BindValue(); + } + + this.connection.incrementNumberOfPrepares(); + + if (this.profileSQL) { + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_PREPARE, "", this.currentCatalog, this.connectionId, this.statementId, -1, + System.currentTimeMillis(), mysql.getCurrentTimeNanosOrMillis() - begin, mysql.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), truncateQueryToLog(sql))); + } + + boolean checkEOF = !mysql.isEOFDeprecated(); + + if (this.parameterCount > 0 && this.connection.versionMeetsMinimum(4, 1, 2) && !mysql.isVersion(5, 0, 0)) { + this.parameterFields = new Field[this.parameterCount]; + + Buffer metaDataPacket; + for (int i = 0; i < this.parameterCount; i++) { + metaDataPacket = mysql.readPacket(); + this.parameterFields[i] = mysql.unpackField(metaDataPacket, false); + } + if (checkEOF) { // Skip the following EOF packet. + mysql.readPacket(); + } + } + + // Read in the result set column information + if (this.fieldCount > 0) { + this.resultFields = new Field[this.fieldCount]; + + Buffer fieldPacket; + for (int i = 0; i < this.fieldCount; i++) { + fieldPacket = mysql.readPacket(); + this.resultFields[i] = mysql.unpackField(fieldPacket, false); + } + if (checkEOF) { // Skip the following EOF packet. + mysql.readPacket(); + } + } + } catch (SQLException sqlEx) { + if (this.connection.getDumpQueriesOnException()) { + StringBuilder messageBuf = new StringBuilder(this.originalSql.length() + 32); + messageBuf.append("\n\nQuery being prepared when exception was thrown:\n\n"); + messageBuf.append(this.originalSql); + + sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf.toString(), getExceptionInterceptor()); + } + + throw sqlEx; + } finally { + // Leave the I/O channel in a known state...there might be packets out there that we're not interested in + this.connection.getIO().clearInputStream(); + } + } + } + + private String truncateQueryToLog(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String query = null; + + if (sql.length() > this.connection.getMaxQuerySizeToLog()) { + StringBuilder queryBuf = new StringBuilder(this.connection.getMaxQuerySizeToLog() + 12); + queryBuf.append(sql.substring(0, this.connection.getMaxQuerySizeToLog())); + queryBuf.append(Messages.getString("MysqlIO.25")); + + query = queryBuf.toString(); + } else { + query = sql; + } + + return query; + } + } + + private void serverResetStatement() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + MysqlIO mysql = this.connection.getIO(); + + Buffer packet = mysql.getSharedSendPacket(); + + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_RESET_STMT); + packet.writeLong(this.serverStatementId); + + try { + mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet, !this.connection.versionMeetsMinimum(4, 1, 2), null, 0); + } catch (SQLException sqlEx) { + throw sqlEx; + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ex); + + throw sqlEx; + } finally { + mysql.clearInputStream(); + } + } + } + + /** + * @see java.sql.PreparedStatement#setArray(int, java.sql.Array) + */ + @Override + public void setArray(int i, Array x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream, int) + */ + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = x; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = length; + } else { + binding.bindLength = -1; + } + } + } + } + + /** + * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal) + */ + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (x == null) { + setNull(parameterIndex, java.sql.Types.DECIMAL); + } else { + + BindValue binding = getBinding(parameterIndex, false); + + if (this.connection.versionMeetsMinimum(5, 0, 3)) { + resetToType(binding, MysqlDefs.FIELD_TYPE_NEW_DECIMAL); + } else { + resetToType(binding, this.stringTypeCode); + } + + binding.value = StringUtils.fixDecimalExponent(StringUtils.consistentToString(x)); + } + } + } + + /** + * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, int) + */ + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = x; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = length; + } else { + binding.bindLength = -1; + } + } + } + } + + /** + * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob) + */ + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = x; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = x.length(); + } else { + binding.bindLength = -1; + } + } + } + } + + /** + * @see java.sql.PreparedStatement#setBoolean(int, boolean) + */ + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + setByte(parameterIndex, (x ? (byte) 1 : (byte) 0)); + } + + /** + * @see java.sql.PreparedStatement#setByte(int, byte) + */ + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_TINY); + + binding.longBinding = x; + } + + /** + * @see java.sql.PreparedStatement#setBytes(int, byte) + */ + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + checkClosed(); + + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING); + + binding.value = x; + } + } + + /** + * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int) + */ + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (reader == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = reader; + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = length; + } else { + binding.bindLength = -1; + } + } + } + } + + /** + * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob) + */ + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (x == null) { + setNull(parameterIndex, java.sql.Types.BINARY); + } else { + BindValue binding = getBinding(parameterIndex, true); + resetToType(binding, MysqlDefs.FIELD_TYPE_BLOB); + + binding.value = x.getCharacterStream(); + binding.isLongData = true; + + if (this.connection.getUseStreamLengthsInPrepStmts()) { + binding.bindLength = x.length(); + } else { + binding.bindLength = -1; + } + } + } + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this to a + * SQL DATE value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + setDate(parameterIndex, x, null); + } + + /** + * Set a parameter to a java.sql.Date value. The driver converts this to a + * SQL DATE value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the calendar to interpret the date with + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.DATE); + } else { + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_DATE); + + binding.value = x; + } + } + + /** + * @see java.sql.PreparedStatement#setDouble(int, double) + */ + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (!this.connection.getAllowNanAndInf() && (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) { + throw SQLError.createSQLException("'" + x + "' is not a valid numeric or approximate numeric value", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + } + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_DOUBLE); + + binding.doubleBinding = x; + } + } + + /** + * @see java.sql.PreparedStatement#setFloat(int, float) + */ + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_FLOAT); + + binding.floatBinding = x; + } + + /** + * @see java.sql.PreparedStatement#setInt(int, int) + */ + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_LONG); + + binding.longBinding = x; + } + + /** + * @see java.sql.PreparedStatement#setLong(int, long) + */ + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_LONGLONG); + + binding.longBinding = x; + } + + /** + * @see java.sql.PreparedStatement#setNull(int, int) + */ + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_NULL); + + binding.isNull = true; + } + + /** + * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String) + */ + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_NULL); + + binding.isNull = true; + } + + /** + * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref) + */ + @Override + public void setRef(int i, Ref x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see java.sql.PreparedStatement#setShort(int, short) + */ + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + checkClosed(); + + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_SHORT); + + binding.longBinding = x; + } + + /** + * @see java.sql.PreparedStatement#setString(int, java.lang.String) + */ + @Override + public void setString(int parameterIndex, String x) throws SQLException { + checkClosed(); + + if (x == null) { + setNull(parameterIndex, java.sql.Types.CHAR); + } else { + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, this.stringTypeCode); + + binding.value = x; + } + } + + /** + * Set a parameter to a java.sql.Time value. + * + * @param parameterIndex + * the first parameter is 1...)); + * @param x + * the parameter value + * + * @throws SQLException + * if a database access error occurs + */ + @Override + public void setTime(int parameterIndex, java.sql.Time x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimeInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false); + } + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts this to a + * SQL TIME value when it sends it to the database, using the given + * timezone. + * + * @param parameterIndex + * the first parameter is 1...)); + * @param x + * the parameter value + * @param cal + * the timezone to use + * + * @throws SQLException + * if a database access error occurs + */ + @Override + public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true); + } + } + + /** + * Set a parameter to a java.sql.Time value. The driver converts this to a + * SQL TIME value when it sends it to the database, using the given + * timezone. + * + * @param parameterIndex + * the first parameter is 1...)); + * @param x + * the parameter value + * @param tz + * the timezone to use + * + * @throws SQLException + * if a database access error occurs + */ + private void setTimeInternal(int parameterIndex, java.sql.Time x, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.TIME); + } else { + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_TIME); + + if (!this.useLegacyDatetimeCode) { + binding.value = x; + } else { + Calendar sessionCalendar = getCalendarInstanceForSessionOrNew(); + + binding.value = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), + rollForward); + } + } + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts this + * to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * + * @throws SQLException + * if a database-access error occurs. + */ + @Override + public void setTimestamp(int parameterIndex, java.sql.Timestamp x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimestampInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false); + } + } + + /** + * Set a parameter to a java.sql.Timestamp value. The driver converts this + * to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex + * the first parameter is 1, the second is 2, ... + * @param x + * the parameter value + * @param cal + * the timezone to use + * + * @throws SQLException + * if a database-access error occurs. + */ + @Override + public void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true); + } + } + + private void setTimestampInternal(int parameterIndex, java.sql.Timestamp x, Calendar targetCalendar, TimeZone tz, boolean rollForward) throws SQLException { + if (x == null) { + setNull(parameterIndex, java.sql.Types.TIMESTAMP); + } else { + BindValue binding = getBinding(parameterIndex, false); + resetToType(binding, MysqlDefs.FIELD_TYPE_DATETIME); + + if (!this.sendFractionalSeconds) { + x = TimeUtil.truncateFractionalSeconds(x); + } + + if (!this.useLegacyDatetimeCode) { + binding.value = x; + } else { + Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + + binding.value = TimeUtil.changeTimezone(this.connection, sessionCalendar, targetCalendar, x, tz, this.connection.getServerTimezoneTZ(), + rollForward); + } + } + } + + /** + * Reset a bind value to be used for a new value of the given type. + */ + protected void resetToType(BindValue oldValue, int bufferType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + // clear any possible old value + oldValue.reset(); + + if (bufferType == MysqlDefs.FIELD_TYPE_NULL && oldValue.bufferType != 0) { + // preserve the previous type to (possibly) avoid sending types at execution time + } else if (oldValue.bufferType != bufferType) { + this.sendTypesToServer = true; + oldValue.bufferType = bufferType; + } + + // setup bind value for use + oldValue.isSet = true; + oldValue.boundBeforeExecutionNum = this.numberOfExecutions; + } + } + + /** + * @param parameterIndex + * @param x + * @param length + * + * @throws SQLException + * @throws NotImplemented + * + * @see java.sql.PreparedStatement#setUnicodeStream(int, java.io.InputStream, int) + * @deprecated + */ + @Deprecated + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + checkClosed(); + + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * @see java.sql.PreparedStatement#setURL(int, java.net.URL) + */ + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + checkClosed(); + + setString(parameterIndex, x.toString()); + } + + /** + * Method storeBinding. + * + * @param packet + * @param bindValue + * @param mysql + * + * @throws SQLException + */ + private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + Object value = bindValue.value; + + // + // Handle primitives first + // + switch (bindValue.bufferType) { + + case MysqlDefs.FIELD_TYPE_TINY: + packet.writeByte((byte) bindValue.longBinding); + return; + case MysqlDefs.FIELD_TYPE_SHORT: + packet.ensureCapacity(2); + packet.writeInt((int) bindValue.longBinding); + return; + case MysqlDefs.FIELD_TYPE_LONG: + packet.ensureCapacity(4); + packet.writeLong((int) bindValue.longBinding); + return; + case MysqlDefs.FIELD_TYPE_LONGLONG: + packet.ensureCapacity(8); + packet.writeLongLong(bindValue.longBinding); + return; + case MysqlDefs.FIELD_TYPE_FLOAT: + packet.ensureCapacity(4); + packet.writeFloat(bindValue.floatBinding); + return; + case MysqlDefs.FIELD_TYPE_DOUBLE: + packet.ensureCapacity(8); + packet.writeDouble(bindValue.doubleBinding); + return; + case MysqlDefs.FIELD_TYPE_TIME: + storeTime(packet, (Time) value); + return; + case MysqlDefs.FIELD_TYPE_DATE: + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + storeDateTime(packet, (java.util.Date) value, mysql, bindValue.bufferType); + return; + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + if (value instanceof byte[]) { + packet.writeLenBytes((byte[]) value); + } else if (!this.isLoadDataQuery) { + packet.writeLenString((String) value, this.charEncoding, this.connection.getServerCharset(), this.charConverter, + this.connection.parserKnowsUnicode(), this.connection); + } else { + packet.writeLenBytes(StringUtils.getBytes((String) value)); + } + + return; + } + + } catch (UnsupportedEncodingException uEE) { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.22") + this.connection.getEncoding() + "'", + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } + } + + private void storeDateTime412AndOlder(Buffer intoBuf, java.util.Date dt, int bufferType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Calendar sessionCalendar = null; + + if (!this.useLegacyDatetimeCode) { + if (bufferType == MysqlDefs.FIELD_TYPE_DATE) { + sessionCalendar = getDefaultTzCalendar(); + } else { + sessionCalendar = getServerTzCalendar(); + } + } else { + sessionCalendar = (dt instanceof Timestamp && this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + } + + java.util.Date oldTime = sessionCalendar.getTime(); + + try { + intoBuf.ensureCapacity(8); + intoBuf.writeByte((byte) 7); // length + + sessionCalendar.setTime(dt); + + int year = sessionCalendar.get(Calendar.YEAR); + int month = sessionCalendar.get(Calendar.MONTH) + 1; + int date = sessionCalendar.get(Calendar.DATE); + + intoBuf.writeInt(year); + intoBuf.writeByte((byte) month); + intoBuf.writeByte((byte) date); + + if (dt instanceof java.sql.Date) { + intoBuf.writeByte((byte) 0); + intoBuf.writeByte((byte) 0); + intoBuf.writeByte((byte) 0); + } else { + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND)); + } + } finally { + sessionCalendar.setTime(oldTime); + } + } + } + + /** + * @param intoBuf + * @param dt + * @param mysql + * @param bufferType + * @throws SQLException + */ + private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql, int bufferType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection.versionMeetsMinimum(4, 1, 3)) { + storeDateTime413AndNewer(intoBuf, dt, bufferType); + } else { + storeDateTime412AndOlder(intoBuf, dt, bufferType); + } + } + } + + private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt, int bufferType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Calendar sessionCalendar = null; + + if (!this.useLegacyDatetimeCode) { + if (bufferType == MysqlDefs.FIELD_TYPE_DATE) { + sessionCalendar = getDefaultTzCalendar(); + } else { + sessionCalendar = getServerTzCalendar(); + } + } else { + sessionCalendar = (dt instanceof Timestamp && this.connection.getUseJDBCCompliantTimezoneShift()) ? this.connection.getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + } + + java.util.Date oldTime = sessionCalendar.getTime(); + + try { + sessionCalendar.setTime(dt); + + if (dt instanceof java.sql.Date) { + sessionCalendar.set(Calendar.HOUR_OF_DAY, 0); + sessionCalendar.set(Calendar.MINUTE, 0); + sessionCalendar.set(Calendar.SECOND, 0); + } + + byte length = (byte) 7; + + if (dt instanceof java.sql.Timestamp) { + length = (byte) 11; + } + + intoBuf.ensureCapacity(length); + + intoBuf.writeByte(length); // length + + int year = sessionCalendar.get(Calendar.YEAR); + int month = sessionCalendar.get(Calendar.MONTH) + 1; + int date = sessionCalendar.get(Calendar.DAY_OF_MONTH); + + intoBuf.writeInt(year); + intoBuf.writeByte((byte) month); + intoBuf.writeByte((byte) date); + + if (dt instanceof java.sql.Date) { + intoBuf.writeByte((byte) 0); + intoBuf.writeByte((byte) 0); + intoBuf.writeByte((byte) 0); + } else { + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE)); + intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND)); + } + + if (length == 11) { + // MySQL expects microseconds, not nanos + intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos() / 1000); + } + + } finally { + sessionCalendar.setTime(oldTime); + } + } + } + + private Calendar getServerTzCalendar() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.serverTzCalendar == null) { + this.serverTzCalendar = new GregorianCalendar(this.connection.getServerTimezoneTZ()); + } + + return this.serverTzCalendar; + } + } + + private Calendar getDefaultTzCalendar() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.defaultTzCalendar == null) { + this.defaultTzCalendar = new GregorianCalendar(TimeZone.getDefault()); + } + + return this.defaultTzCalendar; + } + } + + // + // TO DO: Investigate using NIO to do this faster + // + private void storeReader(MysqlIO mysql, int parameterIndex, Buffer packet, Reader inStream) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String forcedEncoding = this.connection.getClobCharacterEncoding(); + + String clobEncoding = (forcedEncoding == null ? this.connection.getEncoding() : forcedEncoding); + + int maxBytesChar = 2; + + if (clobEncoding != null) { + if (!clobEncoding.equals("UTF-16")) { + maxBytesChar = this.connection.getMaxBytesPerChar(clobEncoding); + + if (maxBytesChar == 1) { + maxBytesChar = 2; // for safety + } + } else { + maxBytesChar = 4; + } + } + + char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar]; + + int numRead = 0; + + int bytesInPacket = 0; + int totalBytesRead = 0; + int bytesReadAtLastSend = 0; + int packetIsFullAt = this.connection.getBlobSendChunkSize(); + + try { + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); + packet.writeLong(this.serverStatementId); + packet.writeInt((parameterIndex)); + + boolean readAny = false; + + while ((numRead = inStream.read(buf)) != -1) { + readAny = true; + + byte[] valueAsBytes = StringUtils.getBytes(buf, null, clobEncoding, this.connection.getServerCharset(), 0, numRead, + this.connection.parserKnowsUnicode(), getExceptionInterceptor()); + + packet.writeBytesNoNull(valueAsBytes, 0, valueAsBytes.length); + + bytesInPacket += valueAsBytes.length; + totalBytesRead += valueAsBytes.length; + + if (bytesInPacket >= packetIsFullAt) { + bytesReadAtLastSend = totalBytesRead; + + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + + bytesInPacket = 0; + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); + packet.writeLong(this.serverStatementId); + packet.writeInt((parameterIndex)); + } + } + + if (totalBytesRead != bytesReadAtLastSend) { + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + } + + if (!readAny) { + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + } + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("ServerPreparedStatement.24") + ioEx.toString(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ioEx); + + throw sqlEx; + } finally { + if (this.connection.getAutoClosePStmtStreams()) { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException ioEx) { + // ignore + } + } + } + } + } + } + + private void storeStream(MysqlIO mysql, int parameterIndex, Buffer packet, InputStream inStream) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE]; + + int numRead = 0; + + try { + int bytesInPacket = 0; + int totalBytesRead = 0; + int bytesReadAtLastSend = 0; + int packetIsFullAt = this.connection.getBlobSendChunkSize(); + + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); + packet.writeLong(this.serverStatementId); + packet.writeInt((parameterIndex)); + + boolean readAny = false; + + while ((numRead = inStream.read(buf)) != -1) { + + readAny = true; + + packet.writeBytesNoNull(buf, 0, numRead); + bytesInPacket += numRead; + totalBytesRead += numRead; + + if (bytesInPacket >= packetIsFullAt) { + bytesReadAtLastSend = totalBytesRead; + + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + + bytesInPacket = 0; + packet.clear(); + packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); + packet.writeLong(this.serverStatementId); + packet.writeInt((parameterIndex)); + } + } + + if (totalBytesRead != bytesReadAtLastSend) { + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + } + + if (!readAny) { + mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null, 0); + } + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("ServerPreparedStatement.25") + ioEx.toString(), + SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + sqlEx.initCause(ioEx); + + throw sqlEx; + } finally { + if (this.connection.getAutoClosePStmtStreams()) { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException ioEx) { + // ignore + } + } + } + } + } + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder toStringBuf = new StringBuilder(); + + toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); + toStringBuf.append(this.serverStatementId); + toStringBuf.append("] - "); + + try { + toStringBuf.append(asSql()); + } catch (SQLException sqlEx) { + toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); + toStringBuf.append(sqlEx); + } + + return toStringBuf.toString(); + } + + protected long getServerStatementId() { + return this.serverStatementId; + } + + private boolean hasCheckedRewrite = false; + private boolean canRewrite = false; + + @Override + public boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.hasCheckedRewrite) { + this.hasCheckedRewrite = true; + this.canRewrite = canRewrite(this.originalSql, isOnDuplicateKeyUpdate(), getLocationOfOnDuplicateKeyUpdate(), 0); + // We need to client-side parse this to get the VALUES clause, etc. + this.parseInfo = new ParseInfo(this.originalSql, this.connection, this.connection.getMetaData(), this.charEncoding, this.charConverter); + } + + return this.canRewrite; + } + } + + public boolean canRewriteAsMultivalueInsertStatement() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!canRewriteAsMultiValueInsertAtSqlLevel()) { + return false; + } + + BindValue[] currentBindValues = null; + BindValue[] previousBindValues = null; + + int nbrCommands = this.batchedArgs.size(); + + // Can't have type changes between sets of bindings for this to work... + + for (int commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + Object arg = this.batchedArgs.get(commandIndex); + + if (!(arg instanceof String)) { + + currentBindValues = ((BatchedBindValues) arg).batchedParameterValues; + + // We need to check types each time, as the user might have bound different types in each addBatch() + + if (previousBindValues != null) { + for (int j = 0; j < this.parameterBindings.length; j++) { + if (currentBindValues[j].bufferType != previousBindValues[j].bufferType) { + return false; + } + } + } + } + } + + return true; + } + } + + private int locationOfOnDuplicateKeyUpdate = -2; + + @Override + protected int getLocationOfOnDuplicateKeyUpdate() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.locationOfOnDuplicateKeyUpdate == -2) { + this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(this.originalSql, this.connection.getDontCheckOnDuplicateKeyUpdateInSQL(), + this.connection.getRewriteBatchedStatements(), this.connection.isNoBackslashEscapesSet()); + } + + return this.locationOfOnDuplicateKeyUpdate; + } + } + + protected boolean isOnDuplicateKeyUpdate() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return getLocationOfOnDuplicateKeyUpdate() != -1; + } + } + + /** + * Computes the maximum parameter set size, and entire batch size given + * the number of arguments in the batch. + * + * @throws SQLException + */ + @Override + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + long sizeOfEntireBatch = 1 + /* com_execute */+4 /* stmt id */ + 1 /* flags */ + 4 /* batch count padding */; + long maxSizeOfParameterSet = 0; + + for (int i = 0; i < numBatchedArgs; i++) { + BindValue[] paramArg = ((BatchedBindValues) this.batchedArgs.get(i)).batchedParameterValues; + + long sizeOfParameterSet = 0; + + sizeOfParameterSet += (this.parameterCount + 7) / 8; // for isNull + + sizeOfParameterSet += this.parameterCount * 2; // have to send types + + for (int j = 0; j < this.parameterBindings.length; j++) { + if (!paramArg[j].isNull) { + + long size = paramArg[j].getBoundLength(); + + if (paramArg[j].isLongData) { + if (size != -1) { + sizeOfParameterSet += size; + } + } else { + sizeOfParameterSet += size; + } + } + } + + sizeOfEntireBatch += sizeOfParameterSet; + + if (sizeOfParameterSet > maxSizeOfParameterSet) { + maxSizeOfParameterSet = sizeOfParameterSet; + } + } + + return new long[] { maxSizeOfParameterSet, sizeOfEntireBatch }; + } + } + + @Override + protected int setOneBatchedParameterSet(java.sql.PreparedStatement batchedStatement, int batchedParamIndex, Object paramSet) throws SQLException { + BindValue[] paramArg = ((BatchedBindValues) paramSet).batchedParameterValues; + + for (int j = 0; j < paramArg.length; j++) { + if (paramArg[j].isNull) { + batchedStatement.setNull(batchedParamIndex++, Types.NULL); + } else { + if (paramArg[j].isLongData) { + Object value = paramArg[j].value; + + if (value instanceof InputStream) { + batchedStatement.setBinaryStream(batchedParamIndex++, (InputStream) value, (int) paramArg[j].bindLength); + } else { + batchedStatement.setCharacterStream(batchedParamIndex++, (Reader) value, (int) paramArg[j].bindLength); + } + } else { + + switch (paramArg[j].bufferType) { + + case MysqlDefs.FIELD_TYPE_TINY: + batchedStatement.setByte(batchedParamIndex++, (byte) paramArg[j].longBinding); + break; + case MysqlDefs.FIELD_TYPE_SHORT: + batchedStatement.setShort(batchedParamIndex++, (short) paramArg[j].longBinding); + break; + case MysqlDefs.FIELD_TYPE_LONG: + batchedStatement.setInt(batchedParamIndex++, (int) paramArg[j].longBinding); + break; + case MysqlDefs.FIELD_TYPE_LONGLONG: + batchedStatement.setLong(batchedParamIndex++, paramArg[j].longBinding); + break; + case MysqlDefs.FIELD_TYPE_FLOAT: + batchedStatement.setFloat(batchedParamIndex++, paramArg[j].floatBinding); + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + batchedStatement.setDouble(batchedParamIndex++, paramArg[j].doubleBinding); + break; + case MysqlDefs.FIELD_TYPE_TIME: + batchedStatement.setTime(batchedParamIndex++, (Time) paramArg[j].value); + break; + case MysqlDefs.FIELD_TYPE_DATE: + batchedStatement.setDate(batchedParamIndex++, (Date) paramArg[j].value); + break; + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + batchedStatement.setTimestamp(batchedParamIndex++, (Timestamp) paramArg[j].value); + break; + case MysqlDefs.FIELD_TYPE_VAR_STRING: + case MysqlDefs.FIELD_TYPE_STRING: + case MysqlDefs.FIELD_TYPE_VARCHAR: + case MysqlDefs.FIELD_TYPE_DECIMAL: + case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: + Object value = paramArg[j].value; + + if (value instanceof byte[]) { + batchedStatement.setBytes(batchedParamIndex, (byte[]) value); + } else { + batchedStatement.setString(batchedParamIndex, (String) value); + } + + // If we ended up here as a multi-statement, we're not working with a server prepared statement + + if (batchedStatement instanceof ServerPreparedStatement) { + BindValue asBound = ((ServerPreparedStatement) batchedStatement).getBinding(batchedParamIndex, false); + asBound.bufferType = paramArg[j].bufferType; + } + + batchedParamIndex++; + + break; + default: + throw new IllegalArgumentException( + "Unknown type when re-binding parameter into batched statement for parameter index " + batchedParamIndex); + } + } + } + } + + return batchedParamIndex; + } + + @Override + protected boolean containsOnDuplicateKeyUpdateInSQL() { + return this.hasOnDuplicateKeyUpdate; + } + + @Override + protected PreparedStatement prepareBatchedInsertSQL(MySQLConnection localConn, int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + PreparedStatement pstmt = ((Wrapper) localConn.prepareStatement(this.parseInfo.getSqlForBatch(numBatches), this.resultSetConcurrency, + this.resultSetType)).unwrap(PreparedStatement.class); + pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys); + + return pstmt; + } catch (UnsupportedEncodingException e) { + SQLException sqlEx = SQLError.createSQLException("Unable to prepare batch statement", SQLError.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + } + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + if (!poolable) { + this.connection.decachePreparedStatement(this); + } + super.setPoolable(poolable); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SingleByteCharsetConverter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SingleByteCharsetConverter.java new file mode 100644 index 0000000..a056fc6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SingleByteCharsetConverter.java @@ -0,0 +1,309 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * Converter for char[]->byte[] and byte[]->char[] for single-byte character sets. + */ +public class SingleByteCharsetConverter { + + private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE; + private static byte[] allBytes = new byte[BYTE_RANGE]; + private static final Map CONVERTER_MAP = new HashMap(); + + private final static byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + // The initial charToByteMap, with all char mappings mapped to (byte) '?', so that unknown characters are mapped to '?' instead of '\0' (which means + // end-of-string to MySQL). + private static byte[] unknownCharsMap = new byte[65536]; + + static { + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + allBytes[i - Byte.MIN_VALUE] = (byte) i; + } + + for (int i = 0; i < unknownCharsMap.length; i++) { + unknownCharsMap[i] = (byte) '?'; // use something 'sane' for unknown chars + } + } + + /** + * Get a converter for the given encoding name + * + * @param encodingName + * the Java character encoding name + * @param conn + * + * @return a converter for the given encoding name + * @throws UnsupportedEncodingException + * if the character encoding is not supported + */ + public static synchronized SingleByteCharsetConverter getInstance(String encodingName, Connection conn) throws UnsupportedEncodingException, SQLException { + SingleByteCharsetConverter instance = CONVERTER_MAP.get(encodingName); + + if (instance == null) { + instance = initCharset(encodingName); + } + + return instance; + } + + /** + * Initialize the shared instance of a converter for the given character + * encoding. + * + * @param javaEncodingName + * the Java name for the character set to initialize + * @return a converter for the given character set + * @throws UnsupportedEncodingException + * if the character encoding is not supported + */ + public static SingleByteCharsetConverter initCharset(String javaEncodingName) throws UnsupportedEncodingException, SQLException { + try { + if (CharsetMapping.isMultibyteCharset(javaEncodingName)) { + return null; + } + } catch (RuntimeException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null); + sqlEx.initCause(ex); + throw sqlEx; + } + + SingleByteCharsetConverter converter = new SingleByteCharsetConverter(javaEncodingName); + + CONVERTER_MAP.put(javaEncodingName, converter); + + return converter; + } + + /** + * Convert the byte buffer from startPos to a length of length to a string + * using the default platform encoding. + * + * @param buffer + * the bytes to convert + * @param startPos + * the index to start at + * @param length + * the number of bytes to convert + * @return the String representation of the given bytes + */ + public static String toStringDefaultEncoding(byte[] buffer, int startPos, int length) { + return new String(buffer, startPos, length); + } + + private char[] byteToChars = new char[BYTE_RANGE]; + + private byte[] charToByteMap = new byte[65536]; + + /** + * Prevent instantiation, called out of static method initCharset(). + * + * @param encodingName + * a JVM character encoding + * @throws UnsupportedEncodingException + * if the JVM does not support the encoding + */ + private SingleByteCharsetConverter(String encodingName) throws UnsupportedEncodingException { + String allBytesString = new String(allBytes, 0, BYTE_RANGE, encodingName); + int allBytesLen = allBytesString.length(); + + System.arraycopy(unknownCharsMap, 0, this.charToByteMap, 0, this.charToByteMap.length); + + for (int i = 0; i < BYTE_RANGE && i < allBytesLen; i++) { + char c = allBytesString.charAt(i); + this.byteToChars[i] = c; + this.charToByteMap[c] = allBytes[i]; + } + } + + public final byte[] toBytes(char[] c) { + if (c == null) { + return null; + } + + int length = c.length; + byte[] bytes = new byte[length]; + + for (int i = 0; i < length; i++) { + bytes[i] = this.charToByteMap[c[i]]; + } + + return bytes; + } + + public final byte[] toBytesWrapped(char[] c, char beginWrap, char endWrap) { + if (c == null) { + return null; + } + + int length = c.length + 2; + int charLength = c.length; + + byte[] bytes = new byte[length]; + bytes[0] = this.charToByteMap[beginWrap]; + + for (int i = 0; i < charLength; i++) { + bytes[i + 1] = this.charToByteMap[c[i]]; + } + + bytes[length - 1] = this.charToByteMap[endWrap]; + + return bytes; + } + + public final byte[] toBytes(char[] chars, int offset, int length) { + if (chars == null) { + return null; + } + + if (length == 0) { + return EMPTY_BYTE_ARRAY; + } + + byte[] bytes = new byte[length]; + + for (int i = 0; (i < length); i++) { + bytes[i] = this.charToByteMap[chars[i + offset]]; + } + + return bytes; + } + + /** + * Convert the given string to an array of bytes. + * + * @param s + * the String to convert + * @return the bytes that make up the String + */ + public final byte[] toBytes(String s) { + if (s == null) { + return null; + } + + int length = s.length(); + byte[] bytes = new byte[length]; + + for (int i = 0; i < length; i++) { + bytes[i] = this.charToByteMap[s.charAt(i)]; + } + + return bytes; + } + + public final byte[] toBytesWrapped(String s, char beginWrap, char endWrap) { + if (s == null) { + return null; + } + + int stringLength = s.length(); + + int length = stringLength + 2; + + byte[] bytes = new byte[length]; + + bytes[0] = this.charToByteMap[beginWrap]; + + for (int i = 0; i < stringLength; i++) { + bytes[i + 1] = this.charToByteMap[s.charAt(i)]; + } + + bytes[length - 1] = this.charToByteMap[endWrap]; + + return bytes; + } + + /** + * Convert the given string to an array of bytes. + * + * @param s + * the String to convert + * @param offset + * the offset to start at + * @param length + * length (max) to convert + * + * @return the bytes that make up the String + */ + public final byte[] toBytes(String s, int offset, int length) { + if (s == null) { + return null; + } + + if (length == 0) { + return EMPTY_BYTE_ARRAY; + } + + byte[] bytes = new byte[length]; + + for (int i = 0; (i < length); i++) { + char c = s.charAt(i + offset); + bytes[i] = this.charToByteMap[c]; + } + + return bytes; + } + + /** + * Convert the byte buffer to a string using this instance's character + * encoding. + * + * @param buffer + * the bytes to convert to a String + * @return the converted String + */ + public final String toString(byte[] buffer) { + return toString(buffer, 0, buffer.length); + } + + /** + * Convert the byte buffer from startPos to a length of length to a string + * using this instance's character encoding. + * + * @param buffer + * the bytes to convert + * @param startPos + * the index to start at + * @param length + * the number of bytes to convert + * @return the String representation of the given bytes + */ + public final String toString(byte[] buffer, int startPos, int length) { + char[] charArray = new char[length]; + int readpoint = startPos; + + for (int i = 0; i < length; i++) { + charArray[i] = this.byteToChars[buffer[readpoint] - Byte.MIN_VALUE]; + readpoint++; + } + + return new String(charArray); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketFactory.java new file mode 100644 index 0000000..bfb8f9b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketFactory.java @@ -0,0 +1,89 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; +import java.util.Properties; + +/** + * Interface to allow pluggable socket creation in the driver + */ +public interface SocketFactory { + + /** + * Called by the driver after issuing the MySQL protocol handshake and + * reading the results of the handshake. + * + * @throws SocketException + * if a socket error occurs + * @throws IOException + * if an I/O error occurs + * + * @return the socket to use after the handshake + */ + Socket afterHandshake() throws SocketException, IOException; + + /** + * Called by the driver before issuing the MySQL protocol handshake. Should + * return the socket instance that should be used during the handshake. + * + * @throws SocketException + * if a socket error occurs + * @throws IOException + * if an I/O error occurs + * + * @return the socket to use before the handshake + */ + Socket beforeHandshake() throws SocketException, IOException; + + /** + * Creates a new socket using the given properties. Properties are parsed by + * the driver from the URL. All properties other than sensitive ones (user + * and password) are passed to this method. The driver will instantiate the + * socket factory with the class name given in the property + * "socketFactory", where the standard is com.mysql.jdbc.StandardSocketFactory Implementing classes + * are responsible for handling synchronization of this method (if needed). + * + * @param host + * the hostname passed in the JDBC URL. It will be a single + * hostname, as the driver parses multi-hosts (for failover) and + * calls this method for each host connection attempt. + * + * @param portNumber + * the port number to connect to (if required). + * + * @param props + * properties passed to the driver via the URL and/or properties + * instance. + * + * @return a socket connected to the given host + * @throws SocketException + * if a socket error occurs + * @throws IOException + * if an I/O error occurs + */ + Socket connect(String host, int portNumber, Properties props) throws SocketException, IOException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketMetadata.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketMetadata.java new file mode 100644 index 0000000..780dbe2 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocketMetadata.java @@ -0,0 +1,152 @@ +/* + Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.sql.ResultSet; +import java.sql.SQLException; + +public interface SocketMetadata { + + public boolean isLocallyConnected(ConnectionImpl conn) throws SQLException; + + /* + * Provides a standard way of determining whether a socket connection is local. + * + * This ensures socket factories (e.g. StandardSocketFactory, StandardSSLSocketFactory) which need to implement this interface, can delegate to a generic + * implementation. + */ + static class Helper { + + public static final String IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME = "com.mysql.jdbc.test.isLocalHostnameReplacement"; + + public static boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn) throws SQLException { + long threadId = conn.getId(); + java.sql.Statement processListStmt = conn.getMetadataSafeStatement(); + ResultSet rs = null; + String processHost = null; + + // "inject" for tests + if (System.getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) { + processHost = System.getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME); + + } else if (conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) { + processHost = conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME); + + } else { // get it from server + try { + processHost = findProcessHost(threadId, processListStmt); + + if (processHost == null) { + // http://bugs.mysql.com/bug.php?id=44167 - connection ids on the wire wrap at 4 bytes even though they're 64-bit numbers + conn.getLog() + .logWarn(String.format( + "Connection id %d not found in \"SHOW PROCESSLIST\", assuming 32-bit overflow, using SELECT CONNECTION_ID() instead", + threadId)); + + rs = processListStmt.executeQuery("SELECT CONNECTION_ID()"); + + if (rs.next()) { + threadId = rs.getLong(1); + + processHost = findProcessHost(threadId, processListStmt); + } else { + conn.getLog().logError( + "No rows returned for statement \"SELECT CONNECTION_ID()\", local connection check will most likely be incorrect"); + } + } + } finally { + processListStmt.close(); + } + } + + if (processHost != null) { + conn.getLog().logDebug(String.format("Using 'host' value of '%s' to determine locality of connection", processHost)); + + int endIndex = processHost.lastIndexOf(":"); + if (endIndex != -1) { + processHost = processHost.substring(0, endIndex); + + try { + boolean isLocal = false; + + InetAddress[] allHostAddr = InetAddress.getAllByName(processHost); + + // mysqlConnection should be the raw socket + SocketAddress remoteSocketAddr = conn.getIO().mysqlConnection.getRemoteSocketAddress(); + + if (remoteSocketAddr instanceof InetSocketAddress) { + InetAddress whereIConnectedTo = ((InetSocketAddress) remoteSocketAddr).getAddress(); + + for (InetAddress hostAddr : allHostAddr) { + if (hostAddr.equals(whereIConnectedTo)) { + conn.getLog().logDebug( + String.format("Locally connected - HostAddress(%s).equals(whereIconnectedTo({%s})", hostAddr, whereIConnectedTo)); + isLocal = true; + break; + } + conn.getLog() + .logDebug(String.format("Attempted locally connected check failed - ! HostAddress(%s).equals(whereIconnectedTo(%s)", + hostAddr, whereIConnectedTo)); + } + } else { + String msg = String.format("Remote socket address %s is not an inet socket address", remoteSocketAddr); + conn.getLog().logDebug(msg); + } + + return isLocal; + } catch (UnknownHostException e) { + conn.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] { processHost }), e); + return false; + } + } + conn.getLog().logWarn(String + .format("No port number present in 'host' from SHOW PROCESSLIST '%s', unable to determine whether locally connected", processHost)); + return false; + } + conn.getLog().logWarn(String + .format("Cannot find process listing for connection %d in SHOW PROCESSLIST output, unable to determine if locally connected", threadId)); + return false; + } + + private static String findProcessHost(long threadId, java.sql.Statement processListStmt) throws SQLException { + String processHost = null; + ResultSet rs = processListStmt.executeQuery("SHOW PROCESSLIST"); + + while (rs.next()) { + long id = rs.getLong(1); + + if (threadId == id) { + processHost = rs.getString(3); + break; + } + } + + return processHost; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocksProxySocketFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocksProxySocketFactory.java new file mode 100644 index 0000000..e2fea17 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/SocksProxySocketFactory.java @@ -0,0 +1,50 @@ +/* + Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.util.Properties; + +/** + * A socket factory used to create sockets connecting through a SOCKS proxy. The socket still supports all the same TCP features as the "standard" socket. + */ +public class SocksProxySocketFactory extends StandardSocketFactory { + public static int SOCKS_DEFAULT_PORT = 1080; + + @Override + protected Socket createSocket(Properties props) { + String socksProxyHost = props.getProperty("socksProxyHost"); + String socksProxyPortString = props.getProperty("socksProxyPort", String.valueOf(SOCKS_DEFAULT_PORT)); + int socksProxyPort = SOCKS_DEFAULT_PORT; + try { + socksProxyPort = Integer.valueOf(socksProxyPortString); + } catch (NumberFormatException ex) { + // ignore. fall back to default + } + + return new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksProxyHost, socksProxyPort))); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardLoadBalanceExceptionChecker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardLoadBalanceExceptionChecker.java new file mode 100644 index 0000000..26ac55a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardLoadBalanceExceptionChecker.java @@ -0,0 +1,116 @@ +/* + Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +public class StandardLoadBalanceExceptionChecker implements LoadBalanceExceptionChecker { + + private List sqlStateList; + private List> sqlExClassList; + + public boolean shouldExceptionTriggerFailover(SQLException ex) { + String sqlState = ex.getSQLState(); + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + // connection error + return true; + } + if (this.sqlStateList != null) { + // check against SQLState list + for (Iterator i = this.sqlStateList.iterator(); i.hasNext();) { + if (sqlState.startsWith(i.next().toString())) { + return true; + } + } + } + } + + // always handle CommunicationException + if (ex instanceof CommunicationsException) { + return true; + } + + if (this.sqlExClassList != null) { + // check against configured class lists + for (Iterator> i = this.sqlExClassList.iterator(); i.hasNext();) { + if (i.next().isInstance(ex)) { + return true; + } + } + } + // no matches + return false; + } + + public void destroy() { + } + + public void init(Connection conn, Properties props) throws SQLException { + configureSQLStateList(props.getProperty("loadBalanceSQLStateFailover", null)); + configureSQLExceptionSubclassList(props.getProperty("loadBalanceSQLExceptionSubclassFailover", null)); + } + + private void configureSQLStateList(String sqlStates) { + if (sqlStates == null || "".equals(sqlStates)) { + return; + } + List states = StringUtils.split(sqlStates, ",", true); + List newStates = new ArrayList(); + + for (String state : states) { + if (state.length() > 0) { + newStates.add(state); + } + } + if (newStates.size() > 0) { + this.sqlStateList = newStates; + } + } + + private void configureSQLExceptionSubclassList(String sqlExClasses) { + if (sqlExClasses == null || "".equals(sqlExClasses)) { + return; + } + List classes = StringUtils.split(sqlExClasses, ",", true); + List> newClasses = new ArrayList>(); + + for (String exClass : classes) { + try { + Class c = Class.forName(exClass); + newClasses.add(c); + } catch (Exception e) { + // ignore and don't check, class doesn't exist + } + } + if (newClasses.size() > 0) { + this.sqlExClassList = newClasses; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardSocketFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardSocketFactory.java new file mode 100644 index 0000000..74b67d5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StandardSocketFactory.java @@ -0,0 +1,268 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +/** + * Socket factory for vanilla TCP/IP sockets (the standard) + */ +public class StandardSocketFactory implements SocketFactory, SocketMetadata { + + public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay"; + + public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true"; + + public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive"; + + public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf"; + + public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf"; + + public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass"; + + public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0"; + + public static final String TCP_SND_BUF_DEFAULT_VALUE = "0"; + + public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0"; + + public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true"; + + /** The hostname to connect to */ + protected String host = null; + + /** The port number to connect to */ + protected int port = 3306; + + /** The underlying TCP/IP socket to use */ + protected Socket rawSocket = null; + + /** The remaining login time in milliseconds. Initial value set from defined DriverManager.setLoginTimeout() */ + protected int loginTimeoutCountdown = DriverManager.getLoginTimeout() * 1000; + + /** Time when last Login Timeout check occurred */ + protected long loginTimeoutCheckTimestamp = System.currentTimeMillis(); + + /** Backup original Socket timeout to be restored after handshake */ + protected int socketTimeoutBackup = 0; + + /** + * Called by the driver after issuing the MySQL protocol handshake and + * reading the results of the handshake. + * + * @throws SocketException + * if a socket error occurs + * @throws IOException + * if an I/O error occurs + * + * @return The socket to use after the handshake + */ + public Socket afterHandshake() throws SocketException, IOException { + resetLoginTimeCountdown(); + this.rawSocket.setSoTimeout(this.socketTimeoutBackup); + return this.rawSocket; + } + + /** + * Called by the driver before issuing the MySQL protocol handshake. Should + * return the socket instance that should be used during the handshake. + * + * @throws SocketException + * if a socket error occurs + * @throws IOException + * if an I/O error occurs + * + * @return the socket to use before the handshake + */ + public Socket beforeHandshake() throws SocketException, IOException { + resetLoginTimeCountdown(); + this.socketTimeoutBackup = this.rawSocket.getSoTimeout(); + this.rawSocket.setSoTimeout(getRealTimeout(this.socketTimeoutBackup)); + return this.rawSocket; + } + + /** + * Create the raw socket. + * + * @param props + * properties available to affect behavior during socket creation. + */ + protected Socket createSocket(Properties props) { + return new Socket(); + } + + /** + * Configures socket properties based on properties from the connection + * (tcpNoDelay, snd/rcv buf, traffic class, etc). + * + * @param props + * @throws SocketException + * @throws IOException + */ + private void configureSocket(Socket sock, Properties props) throws SocketException, IOException { + sock.setTcpNoDelay(Boolean.valueOf(props.getProperty(TCP_NO_DELAY_PROPERTY_NAME, TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue()); + + String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME, TCP_KEEP_ALIVE_DEFAULT_VALUE); + + if (keepAlive != null && keepAlive.length() > 0) { + sock.setKeepAlive(Boolean.valueOf(keepAlive).booleanValue()); + } + + int receiveBufferSize = Integer.parseInt(props.getProperty(TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE)); + + if (receiveBufferSize > 0) { + sock.setReceiveBufferSize(receiveBufferSize); + } + + int sendBufferSize = Integer.parseInt(props.getProperty(TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE)); + + if (sendBufferSize > 0) { + sock.setSendBufferSize(sendBufferSize); + } + + int trafficClass = Integer.parseInt(props.getProperty(TCP_TRAFFIC_CLASS_PROPERTY_NAME, TCP_TRAFFIC_CLASS_DEFAULT_VALUE)); + + if (trafficClass > 0) { + sock.setTrafficClass(trafficClass); + } + } + + /** + * @see com.mysql.jdbc.SocketFactory#createSocket(Properties) + */ + public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { + + if (props != null) { + this.host = hostname; + + this.port = portNumber; + + String localSocketHostname = props.getProperty("localSocketAddress"); + InetSocketAddress localSockAddr = null; + if (localSocketHostname != null && localSocketHostname.length() > 0) { + localSockAddr = new InetSocketAddress(InetAddress.getByName(localSocketHostname), 0); + } + + String connectTimeoutStr = props.getProperty("connectTimeout"); + + int connectTimeout = 0; + + if (connectTimeoutStr != null) { + try { + connectTimeout = Integer.parseInt(connectTimeoutStr); + } catch (NumberFormatException nfe) { + throw new SocketException("Illegal value '" + connectTimeoutStr + "' for connectTimeout"); + } + } + + if (this.host != null) { + InetAddress[] possibleAddresses = InetAddress.getAllByName(this.host); + + if (possibleAddresses.length == 0) { + throw new SocketException("No addresses for host"); + } + + // save last exception to propagate to caller if connection fails + SocketException lastException = null; + + // Need to loop through all possible addresses. Name lookup may return multiple addresses including IPv4 and IPv6 addresses. Some versions of + // MySQL don't listen on the IPv6 address so we try all addresses. + for (int i = 0; i < possibleAddresses.length; i++) { + try { + this.rawSocket = createSocket(props); + + configureSocket(this.rawSocket, props); + + InetSocketAddress sockAddr = new InetSocketAddress(possibleAddresses[i], this.port); + // bind to the local port if not using the ephemeral port + if (localSockAddr != null) { + this.rawSocket.bind(localSockAddr); + } + + this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout)); + + break; + } catch (SocketException ex) { + lastException = ex; + resetLoginTimeCountdown(); + this.rawSocket = null; + } + } + + if (this.rawSocket == null && lastException != null) { + throw lastException; + } + + resetLoginTimeCountdown(); + + return this.rawSocket; + } + } + + throw new SocketException("Unable to create socket"); + } + + public boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn) throws SQLException { + return SocketMetadata.Helper.isLocallyConnected(conn); + } + + /** + * Decrements elapsed time since last reset from login timeout count down. + * + * @throws SocketException + * If the login timeout is reached or exceeded. + */ + protected void resetLoginTimeCountdown() throws SocketException { + if (this.loginTimeoutCountdown > 0) { + long now = System.currentTimeMillis(); + this.loginTimeoutCountdown -= now - this.loginTimeoutCheckTimestamp; + if (this.loginTimeoutCountdown <= 0) { + throw new SocketException(Messages.getString("Connection.LoginTimeout")); + } + this.loginTimeoutCheckTimestamp = now; + } + } + + /** + * Validates the connection/socket timeout that must really be used. + * + * @param expectedTimeout + * The timeout to validate. + * @return The timeout to be used. + */ + protected int getRealTimeout(int expectedTimeout) { + if (this.loginTimeoutCountdown > 0 && (expectedTimeout == 0 || expectedTimeout > this.loginTimeoutCountdown)) { + return this.loginTimeoutCountdown; + } + return expectedTimeout; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Statement.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Statement.java new file mode 100644 index 0000000..c3499bb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Statement.java @@ -0,0 +1,98 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.sql.SQLException; + +/** + * This interface contains methods that are considered the "vendor extension" to the JDBC API for MySQL's implementation of java.sql.Statement. + * + * For those looking further into the driver implementation, it is not an API that is used for plugability of implementations inside our driver + * (which is why there are still references to StatementImpl throughout the code). + */ +public interface Statement extends java.sql.Statement, Wrapper { + + /** + * Workaround for containers that 'check' for sane values of + * Statement.setFetchSize() so that applications can use + * the Java variant of libmysql's mysql_use_result() behavior. + * + * @throws SQLException + */ + public abstract void enableStreamingResults() throws SQLException; + + /** + * Resets this statements fetch size and result set type to the values + * they had before enableStreamingResults() was called. + * + * @throws SQLException + */ + public abstract void disableStreamingResults() throws SQLException; + + /** + * Sets an InputStream instance that will be used to send data + * to the MySQL server for a "LOAD DATA LOCAL INFILE" statement + * rather than a FileInputStream or URLInputStream that represents + * the path given as an argument to the statement. + * + * This stream will be read to completion upon execution of a + * "LOAD DATA LOCAL INFILE" statement, and will automatically + * be closed by the driver, so it needs to be reset + * before each call to execute*() that would cause the MySQL + * server to request data to fulfill the request for + * "LOAD DATA LOCAL INFILE". + * + * If this value is set to NULL, the driver will revert to using + * a FileInputStream or URLInputStream as required. + */ + public abstract void setLocalInfileInputStream(InputStream stream); + + /** + * Returns the InputStream instance that will be used to send + * data in response to a "LOAD DATA LOCAL INFILE" statement. + * + * This method returns NULL if no such stream has been set + * via setLocalInfileInputStream(). + */ + public abstract InputStream getLocalInfileInputStream(); + + public void setPingTarget(PingTarget pingTarget); + + public ExceptionInterceptor getExceptionInterceptor(); + + /** + * Callback for result set instances to remove them from the Set that + * tracks them per-statement + */ + + public abstract void removeOpenResultSet(ResultSetInternalMethods rs); + + /** + * Returns the number of open result sets for this statement. + */ + public abstract int getOpenResultSetCount(); + + public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementImpl.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementImpl.java new file mode 100644 index 0000000..ed9fe32 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementImpl.java @@ -0,0 +1,2671 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.InputStream; +import java.math.BigInteger; +import java.sql.BatchUpdateException; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.log.LogUtils; +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.profiler.ProfilerEventHandler; + +/** + * A Statement object is used for executing a static SQL statement and obtaining + * the results produced by it. + * + * Only one ResultSet per Statement can be open at any point in time. Therefore, if the reading of one ResultSet is interleaved with the reading of another, + * each must have been generated by different Statements. All statement execute methods implicitly close a statement's current ResultSet if an open one exists. + */ +public class StatementImpl implements Statement { + protected static final String PING_MARKER = "/* ping */"; + + protected static final String[] ON_DUPLICATE_KEY_UPDATE_CLAUSE = new String[] { "ON", "DUPLICATE", "KEY", "UPDATE" }; + + /** + * Thread used to implement query timeouts...Eventually we could be more + * efficient and have one thread with timers, but this is a straightforward + * and simple way to implement a feature that isn't used all that often. + */ + class CancelTask extends TimerTask { + + long connectionId = 0; + SQLException caughtWhileCancelling = null; + StatementImpl toCancel; + Properties origConnProps = null; + String origConnURL = ""; + + CancelTask(StatementImpl cancellee) throws SQLException { + this.connectionId = cancellee.connectionId; + this.toCancel = cancellee; + this.origConnProps = new Properties(); + + Properties props = StatementImpl.this.connection.getProperties(); + + Enumeration keys = props.propertyNames(); + + while (keys.hasMoreElements()) { + String key = keys.nextElement().toString(); + this.origConnProps.setProperty(key, props.getProperty(key)); + } + + this.origConnURL = StatementImpl.this.connection.getURL(); + } + + @Override + public void run() { + + Thread cancelThread = new Thread() { + + @Override + public void run() { + + Connection cancelConn = null; + java.sql.Statement cancelStmt = null; + + try { + if (StatementImpl.this.connection.getQueryTimeoutKillsConnection()) { + CancelTask.this.toCancel.wasCancelled = true; + CancelTask.this.toCancel.wasCancelledByTimeout = true; + StatementImpl.this.connection.realClose(false, false, true, + new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout"))); + } else { + synchronized (StatementImpl.this.cancelTimeoutMutex) { + if (CancelTask.this.origConnURL.equals(StatementImpl.this.connection.getURL())) { + //All's fine + cancelConn = StatementImpl.this.connection.duplicate(); + cancelStmt = cancelConn.createStatement(); + cancelStmt.execute("KILL QUERY " + CancelTask.this.connectionId); + } else { + try { + cancelConn = (Connection) DriverManager.getConnection(CancelTask.this.origConnURL, CancelTask.this.origConnProps); + cancelStmt = cancelConn.createStatement(); + cancelStmt.execute("KILL QUERY " + CancelTask.this.connectionId); + } catch (NullPointerException npe) { + //Log this? "Failed to connect to " + origConnURL + " and KILL query" + } + } + CancelTask.this.toCancel.wasCancelled = true; + CancelTask.this.toCancel.wasCancelledByTimeout = true; + } + } + } catch (SQLException sqlEx) { + CancelTask.this.caughtWhileCancelling = sqlEx; + } catch (NullPointerException npe) { + // Case when connection closed while starting to cancel + // We can't easily synchronize this, because then one thread can't cancel() a running query + + // ignore, we shouldn't re-throw this, because the connection's already closed, so the statement has been timed out. + } finally { + if (cancelStmt != null) { + try { + cancelStmt.close(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + if (cancelConn != null) { + try { + cancelConn.close(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + CancelTask.this.toCancel = null; + CancelTask.this.origConnProps = null; + CancelTask.this.origConnURL = null; + } + } + }; + + cancelThread.start(); + } + } + + /** + * Mutex to prevent race between returning query results and noticing + * that we're timed-out or cancelled. + */ + + protected Object cancelTimeoutMutex = new Object(); + + /** Used to generate IDs when profiling. */ + static int statementCounter = 1; + + public final static byte USES_VARIABLES_FALSE = 0; + + public final static byte USES_VARIABLES_TRUE = 1; + + public final static byte USES_VARIABLES_UNKNOWN = -1; + + protected boolean wasCancelled = false; + protected boolean wasCancelledByTimeout = false; + + /** Holds batched commands */ + protected List batchedArgs; + + /** The character converter to use (if available) */ + protected SingleByteCharsetConverter charConverter = null; + + /** The character encoding to use (if available) */ + protected String charEncoding = null; + + /** The connection that created us */ + protected volatile MySQLConnection connection = null; + + protected long connectionId = 0; + + /** The catalog in use */ + protected String currentCatalog = null; + + /** Should we process escape codes? */ + protected boolean doEscapeProcessing = true; + + /** If we're profiling, where should events go to? */ + protected ProfilerEventHandler eventSink = null; + + /** The number of rows to fetch at a time (currently ignored) */ + private int fetchSize = 0; + + /** Has this statement been closed? */ + protected boolean isClosed = false; + + /** The auto_increment value for the last insert */ + protected long lastInsertId = -1; + + /** The max field size for this statement */ + protected int maxFieldSize = MysqlIO.getMaxBuf(); + + /** + * The maximum number of rows to return for this statement (-1 means _all_ + * rows) + */ + protected int maxRows = -1; + + /** Set of currently-open ResultSets */ + protected Set openResults = new HashSet(); + + /** Are we in pedantic mode? */ + protected boolean pedantic = false; + + /** + * Where this statement was created, only used if profileSql or + * useUsageAdvisor set to true. + */ + protected String pointOfOrigin; + + /** Should we profile? */ + protected boolean profileSQL = false; + + /** The current results */ + protected ResultSetInternalMethods results = null; + + protected ResultSetInternalMethods generatedKeysResults = null; + + /** The concurrency for this result set (updatable or not) */ + protected int resultSetConcurrency = 0; + + /** The type of this result set (scroll sensitive or in-sensitive) */ + protected int resultSetType = 0; + + /** Used to identify this statement when profiling. */ + protected int statementId; + + /** The timeout for a query */ + protected int timeoutInMillis = 0; + + /** The update count for this statement */ + protected long updateCount = -1; + + /** Should we use the usage advisor? */ + protected boolean useUsageAdvisor = false; + + /** The warnings chain. */ + protected SQLWarning warningChain = null; + + /** Has clearWarnings() been called? */ + protected boolean clearWarningsCalled = false; + + /** + * Should this statement hold results open over .close() irregardless of + * connection's setting? + */ + protected boolean holdResultsOpenOverClose = false; + + protected ArrayList batchedGeneratedKeys = null; + + protected boolean retrieveGeneratedKeys = false; + + protected boolean continueBatchOnError = false; + + protected PingTarget pingTarget = null; + + protected boolean useLegacyDatetimeCode; + + protected boolean sendFractionalSeconds; + + private ExceptionInterceptor exceptionInterceptor; + + /** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */ + protected boolean lastQueryIsOnDupKeyUpdate = false; + + /** Are we currently executing a statement? */ + protected final AtomicBoolean statementExecuting = new AtomicBoolean(false); + + /** Are we currently closing results implicitly (internally)? */ + private boolean isImplicitlyClosingResults = false; + + /** + * Constructor for a Statement. + * + * @param c + * the Connection instantation that creates us + * @param catalog + * the database name in use when we were created + * + * @throws SQLException + * if an error occurs. + */ + public StatementImpl(MySQLConnection c, String catalog) throws SQLException { + if ((c == null) || c.isClosed()) { + throw SQLError.createSQLException(Messages.getString("Statement.0"), SQLError.SQL_STATE_CONNECTION_NOT_OPEN, null); + } + + this.connection = c; + this.connectionId = this.connection.getId(); + this.exceptionInterceptor = this.connection.getExceptionInterceptor(); + + this.currentCatalog = catalog; + this.pedantic = this.connection.getPedantic(); + this.continueBatchOnError = this.connection.getContinueBatchOnError(); + this.useLegacyDatetimeCode = this.connection.getUseLegacyDatetimeCode(); + this.sendFractionalSeconds = this.connection.getSendFractionalSeconds(); + this.doEscapeProcessing = this.connection.getEnableEscapeProcessing(); + + if (!this.connection.getDontTrackOpenResources()) { + this.connection.registerStatement(this); + } + + this.maxFieldSize = this.connection.getMaxAllowedPacket(); + + int defaultFetchSize = this.connection.getDefaultFetchSize(); + if (defaultFetchSize != 0) { + setFetchSize(defaultFetchSize); + } + + if (this.connection.getUseUnicode()) { + this.charEncoding = this.connection.getEncoding(); + this.charConverter = this.connection.getCharsetConverter(this.charEncoding); + } + + boolean profiling = this.connection.getProfileSql() || this.connection.getUseUsageAdvisor() || this.connection.getLogSlowQueries(); + if (this.connection.getAutoGenerateTestcaseScript() || profiling) { + this.statementId = statementCounter++; + } + if (profiling) { + this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable()); + this.profileSQL = this.connection.getProfileSql(); + this.useUsageAdvisor = this.connection.getUseUsageAdvisor(); + this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + } + + int maxRowsConn = this.connection.getMaxRows(); + if (maxRowsConn != -1) { + setMaxRows(maxRowsConn); + } + + this.holdResultsOpenOverClose = this.connection.getHoldResultsOpenOverStatementClose(); + + this.version5013OrNewer = this.connection.versionMeetsMinimum(5, 0, 13); + } + + /** + * @param sql + * + * @throws SQLException + */ + public void addBatch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.batchedArgs == null) { + this.batchedArgs = new ArrayList(); + } + + if (sql != null) { + this.batchedArgs.add(sql); + } + } + } + + /** + * Get the batched args as added by the addBatch method(s). + * The list is unmodifiable and might contain any combination of String, + * BatchParams, or BatchedBindValues depending on how the parameters were + * batched. + * + * @return an unmodifiable List of batched args + */ + public List getBatchedArgs() { + return this.batchedArgs == null ? null : Collections.unmodifiableList(this.batchedArgs); + } + + /** + * Cancels this Statement object if both the DBMS and driver support + * aborting an SQL statement. This method can be used by one thread to + * cancel a statement that is being executed by another thread. + */ + public void cancel() throws SQLException { + if (!this.statementExecuting.get()) { + return; + } + + if (!this.isClosed && this.connection != null && this.connection.versionMeetsMinimum(5, 0, 0)) { + Connection cancelConn = null; + java.sql.Statement cancelStmt = null; + + try { + cancelConn = this.connection.duplicate(); + cancelStmt = cancelConn.createStatement(); + cancelStmt.execute("KILL QUERY " + this.connection.getIO().getThreadId()); + this.wasCancelled = true; + } finally { + if (cancelStmt != null) { + cancelStmt.close(); + } + + if (cancelConn != null) { + cancelConn.close(); + } + } + + } + } + + // --------------------------JDBC 2.0----------------------------- + + /** + * Checks if closed() has been called, and throws an exception if so + * + * @throws SQLException + * if this statement has been closed + */ + protected MySQLConnection checkClosed() throws SQLException { + MySQLConnection c = this.connection; + + if (c == null) { + throw SQLError.createSQLException(Messages.getString("Statement.49"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return c; + } + + /** + * Checks if the given SQL query with the given first non-ws char is a DML + * statement. Throws an exception if it is. + * + * @param sql + * the SQL to check + * @param firstStatementChar + * the UC first non-ws char of the statement + * + * @throws SQLException + * if the statement contains DML + */ + protected void checkForDml(String sql, char firstStatementChar) throws SQLException { + if ((firstStatementChar == 'I') || (firstStatementChar == 'U') || (firstStatementChar == 'D') || (firstStatementChar == 'A') + || (firstStatementChar == 'C') || (firstStatementChar == 'T') || (firstStatementChar == 'R')) { + String noCommentSql = StringUtils.stripComments(sql, "'\"", "'\"", true, false, true, true); + + if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "TRUNCATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "RENAME")) { + throw SQLError.createSQLException(Messages.getString("Statement.57"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + /** + * Method checkNullOrEmptyQuery. + * + * @param sql + * the SQL to check + * + * @throws SQLException + * if query is null or empty. + */ + protected void checkNullOrEmptyQuery(String sql) throws SQLException { + if (sql == null) { + throw SQLError.createSQLException(Messages.getString("Statement.59"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (sql.length() == 0) { + throw SQLError.createSQLException(Messages.getString("Statement.61"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Make the set of commands in the current batch empty. This method + * is optional. + * + * @exception SQLException + * if a database-access error occurs, or the driver does not + * support batch statements + */ + public void clearBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.batchedArgs != null) { + this.batchedArgs.clear(); + } + } + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this Statement. + * + * @exception SQLException + * if a database access error occurs (why?) + */ + public void clearWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.clearWarningsCalled = true; + this.warningChain = null; + } + } + + /** + * In many cases, it is desirable to immediately release a Statement's + * database and JDBC resources instead of waiting for this to happen when it + * is automatically closed. The close method provides this immediate + * release. + * + *

    + * Note: A Statement is automatically closed when it is garbage collected. When a Statement is closed, its current ResultSet, if one exists, is also + * closed. + *

    + * + * @exception SQLException + * if a database access error occurs + */ + public void close() throws SQLException { + realClose(true, true); + } + + /** + * Close any open result sets that have been 'held open' + */ + protected void closeAllOpenResults() throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (this.openResults != null) { + for (ResultSetInternalMethods element : this.openResults) { + try { + element.realClose(false); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + } + + this.openResults.clear(); + } + } + } + + /** + * Close all result sets in this statement. This includes multi-results + */ + protected void implicitlyCloseAllOpenResults() throws SQLException { + this.isImplicitlyClosingResults = true; + try { + if (!(this.connection.getHoldResultsOpenOverStatementClose() || this.connection.getDontTrackOpenResources() || this.holdResultsOpenOverClose)) { + if (this.results != null) { + this.results.realClose(false); + } + if (this.generatedKeysResults != null) { + this.generatedKeysResults.realClose(false); + } + closeAllOpenResults(); + } + } finally { + this.isImplicitlyClosingResults = false; + } + } + + public void removeOpenResultSet(ResultSetInternalMethods rs) { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.openResults != null) { + this.openResults.remove(rs); + } + + boolean hasMoreResults = rs.getNextResultSet() != null; + + // clear the current results or GGK results + if (this.results == rs && !hasMoreResults) { + this.results = null; + } + if (this.generatedKeysResults == rs) { + this.generatedKeysResults = null; + } + + // trigger closeOnCompletion if: + // a) the result set removal wasn't triggered internally + // b) there are no additional results + if (!this.isImplicitlyClosingResults && !hasMoreResults) { + checkAndPerformCloseOnCompletionAction(); + } + } + } catch (SQLException e) { + // we can't break the interface, having this be no-op in case of error is ok + } + } + + public int getOpenResultSetCount() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.openResults != null) { + return this.openResults.size(); + } + + return 0; + } + } catch (SQLException e) { + // we can't break the interface, having this be no-op in case of error is ok + + return 0; + } + } + + /** + * Check if all ResultSets generated by this statement are closed. If so, + * close this statement. + */ + private void checkAndPerformCloseOnCompletionAction() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (isCloseOnCompletion() && !this.connection.getDontTrackOpenResources() && getOpenResultSetCount() == 0 + && (this.results == null || !this.results.reallyResult() || this.results.isClosed()) + && (this.generatedKeysResults == null || !this.generatedKeysResults.reallyResult() || this.generatedKeysResults.isClosed())) { + realClose(false, false); + } + } + } catch (SQLException e) { + } + } + + /** + * @param sql + */ + private ResultSetInternalMethods createResultSetUsingServerFetch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + java.sql.PreparedStatement pStmt = this.connection.prepareStatement(sql, this.resultSetType, this.resultSetConcurrency); + + pStmt.setFetchSize(this.fetchSize); + + if (this.maxRows > -1) { + pStmt.setMaxRows(this.maxRows); + } + + statementBegins(); + + pStmt.execute(); + + // + // Need to be able to get resultset irrespective if we issued DML or not to make this work. + // + ResultSetInternalMethods rs = ((StatementImpl) pStmt).getResultSetInternal(); + + rs.setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt); + + this.results = rs; + + return rs; + } + } + + /** + * We only stream result sets when they are forward-only, read-only, and the + * fetch size has been set to Integer.MIN_VALUE + * + * @return true if this result set should be streamed row at-a-time, rather + * than read all at once. + */ + protected boolean createStreamingResultSet() { + return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) + && (this.fetchSize == Integer.MIN_VALUE)); + } + + private int originalResultSetType = 0; + private int originalFetchSize = 0; + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.IStatement#enableStreamingResults() + */ + public void enableStreamingResults() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.originalResultSetType = this.resultSetType; + this.originalFetchSize = this.fetchSize; + + setFetchSize(Integer.MIN_VALUE); + setResultSetType(ResultSet.TYPE_FORWARD_ONLY); + } + } + + public void disableStreamingResults() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.fetchSize == Integer.MIN_VALUE && this.resultSetType == ResultSet.TYPE_FORWARD_ONLY) { + setFetchSize(this.originalFetchSize); + setResultSetType(this.originalResultSetType); + } + } + } + + /** + * Adjust net_write_timeout to a higher value if we're streaming result sets. More often than not, someone runs into + * an issue where they blow net_write_timeout when using this feature, and if they're willing to hold a result set open + * for 30 seconds or more, one more round-trip isn't going to hurt. + * + * This is reset by RowDataDynamic.close(). + */ + protected void setupStreamingTimeout(MySQLConnection con) throws SQLException { + if (createStreamingResultSet() && con.getNetTimeoutForStreamingResults() > 0) { + executeSimpleNonQuery(con, "SET net_write_timeout=" + con.getNetTimeoutForStreamingResults()); + } + } + + /** + * Execute a SQL statement that may return multiple results. We don't have + * to worry about this since we do not support multiple ResultSets. You can + * use getResultSet or getUpdateCount to retrieve the result. + * + * @param sql + * any SQL statement + * + * @return true if the next result is a ResulSet, false if it is an update + * count or there are no more results + * + * @exception SQLException + * if a database access error occurs + */ + public boolean execute(String sql) throws SQLException { + return executeInternal(sql, false); + } + + private boolean executeInternal(String sql, boolean returnGeneratedKeys) throws SQLException { + MySQLConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + checkClosed(); + + checkNullOrEmptyQuery(sql); + + resetCancelledState(); + + char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + boolean maybeSelect = firstNonWsChar == 'S'; + + this.retrieveGeneratedKeys = returnGeneratedKeys; + + this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstNonWsChar == 'I' && containsOnDuplicateKeyInString(sql); + + if (!maybeSelect && locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("Statement.27") + Messages.getString("Statement.28"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + boolean readInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); + if (returnGeneratedKeys && firstNonWsChar == 'R') { + // If this is a 'REPLACE' query, we need to be able to parse the 'info' message returned from the server to determine the actual number of keys + // generated. + locallyScopedConn.setReadInfoMsgEnabled(true); + } + + try { + setupStreamingTimeout(locallyScopedConn); + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn); + + if (escapedSqlResult instanceof String) { + sql = (String) escapedSqlResult; + } else { + sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + } + + implicitlyCloseAllOpenResults(); + + if (sql.charAt(0) == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return true; + } + } + + CachedResultSetMetaData cachedMetaData = null; + + ResultSetInternalMethods rs = null; + + this.batchedGeneratedKeys = null; + + if (useServerFetch()) { + rs = createResultSetUsingServerFetch(sql); + } else { + CancelTask timeoutTask = null; + + String oldCatalog = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis); + } + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + + Field[] cachedFields = null; + + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetaData = locallyScopedConn.getCachedMetaData(sql); + + if (cachedMetaData != null) { + cachedFields = cachedMetaData.fields; + } + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(maybeSelect ? this.maxRows : -1); + + statementBegins(); + + rs = locallyScopedConn.execSQL(this, sql, this.maxRows, null, this.resultSetType, this.resultSetConcurrency, createStreamingResultSet(), + this.currentCatalog, cachedFields); + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + SQLException cause = null; + + if (this.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + resetCancelledState(); + + throw cause; + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + locallyScopedConn.getCancelTimer().purge(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + } + + if (rs != null) { + this.lastInsertId = rs.getUpdateID(); + + this.results = rs; + + rs.setFirstCharOfQuery(firstNonWsChar); + + if (rs.reallyResult()) { + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results); + } else { + if (this.connection.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results); + } + } + } + } + + return ((rs != null) && rs.reallyResult()); + } finally { + locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState); + + this.statementExecuting.set(false); + } + } + } + + protected void statementBegins() { + this.clearWarningsCalled = false; + this.statementExecuting.set(true); + } + + protected void resetCancelledState() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.cancelTimeoutMutex == null) { + return; + } + + synchronized (this.cancelTimeoutMutex) { + this.wasCancelled = false; + this.wasCancelledByTimeout = false; + } + } + } + + /** + * @see StatementImpl#execute(String, int) + */ + public boolean execute(String sql, int returnGeneratedKeys) throws SQLException { + return executeInternal(sql, returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS); + } + + /** + * @see StatementImpl#execute(String, int[]) + */ + public boolean execute(String sql, int[] generatedKeyIndices) throws SQLException { + return executeInternal(sql, generatedKeyIndices != null && generatedKeyIndices.length > 0); + } + + /** + * @see StatementImpl#execute(String, String[]) + */ + public boolean execute(String sql, String[] generatedKeyNames) throws SQLException { + return executeInternal(sql, generatedKeyNames != null && generatedKeyNames.length > 0); + } + + /** + * JDBC 2.0 Submit a batch of commands to the database for execution. This + * method is optional. + * + * @return an array of update counts containing one element for each command + * in the batch. The array is ordered according to the order in + * which commands were inserted into the batch + * + * @exception SQLException + * if a database-access error occurs, or the driver does not + * support batch statements + * @throws java.sql.BatchUpdateException + */ + public int[] executeBatch() throws SQLException { + return Util.truncateAndConvertToInt(executeBatchInternal()); + } + + protected long[] executeBatchInternal() throws SQLException { + MySQLConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("Statement.34") + Messages.getString("Statement.35"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + implicitlyCloseAllOpenResults(); + + if (this.batchedArgs == null || this.batchedArgs.size() == 0) { + return new long[0]; + } + + // we timeout the entire batch, not individual statements + int individualStatementTimeout = this.timeoutInMillis; + this.timeoutInMillis = 0; + + CancelTask timeoutTask = null; + + try { + resetCancelledState(); + + statementBegins(); + + try { + this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do.. + + long[] updateCounts = null; + + if (this.batchedArgs != null) { + int nbrCommands = this.batchedArgs.size(); + + this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size()); + + boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries(); + + if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) + && (multiQueriesEnabled || (locallyScopedConn.getRewriteBatchedStatements() && nbrCommands > 4))) { + return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout); + } + + if (locallyScopedConn.getEnableQueryTimeouts() && individualStatementTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, individualStatementTimeout); + } + + updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + int commandIndex = 0; + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + try { + String sql = (String) this.batchedArgs.get(commandIndex); + updateCounts[commandIndex] = executeUpdateInternal(sql, true, true); + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString(sql) ? 1 : 0); + } catch (SQLException ex) { + updateCounts[commandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[commandIndex]; + + if (hasDeadlockOrTimeoutRolledBackTx(ex)) { + for (int i = 0; i < newUpdateCounts.length; i++) { + newUpdateCounts[i] = java.sql.Statement.EXECUTE_FAILED; + } + } else { + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, commandIndex); + } + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + } + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + } + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + timeoutTask = null; + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + this.statementExecuting.set(false); + } + } finally { + + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + + this.timeoutInMillis = individualStatementTimeout; + + clearBatch(); + } + } + } + + protected final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) { + int vendorCode = ex.getErrorCode(); + + switch (vendorCode) { + case MysqlErrorNumbers.ER_LOCK_DEADLOCK: + case MysqlErrorNumbers.ER_LOCK_TABLE_FULL: + return true; + case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT: + return !this.version5013OrNewer; + default: + return false; + } + } + + /** + * Rewrites batch into a single query to send to the server. This method + * will constrain each batch to be shorter than max_allowed_packet on the + * server. + * + * @return update counts in the same manner as executeBatch() + * @throws SQLException + */ + private long[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled, int nbrCommands, int individualStatementTimeout) throws SQLException { + + MySQLConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (!multiQueriesEnabled) { + locallyScopedConn.getIO().enableMultiQueries(); + } + + java.sql.Statement batchStmt = null; + + CancelTask timeoutTask = null; + + try { + long[] updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = Statement.EXECUTE_FAILED; + } + + int commandIndex = 0; + + StringBuilder queryBuf = new StringBuilder(); + + batchStmt = locallyScopedConn.createStatement(); + + if (locallyScopedConn.getEnableQueryTimeouts() && individualStatementTimeout != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask((StatementImpl) batchStmt); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, individualStatementTimeout); + } + + int counter = 0; + + int numberOfBytesPerChar = 1; + + String connectionEncoding = locallyScopedConn.getEncoding(); + + if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) { + numberOfBytesPerChar = 3; + } else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) { + numberOfBytesPerChar = 2; + } + + int escapeAdjust = 1; + + batchStmt.setEscapeProcessing(this.doEscapeProcessing); + + if (this.doEscapeProcessing) { + escapeAdjust = 2; // We assume packet _could_ grow by this amount, as we're not sure how big statement will end up after escape processing + } + + SQLException sqlEx = null; + + int argumentSetsInBatchSoFar = 0; + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + String nextQuery = (String) this.batchedArgs.get(commandIndex); + + if (((((queryBuf.length() + nextQuery.length()) * numberOfBytesPerChar) + 1 /* for semicolon */ + + MysqlIO.HEADER_LENGTH) * escapeAdjust) + 32 > this.connection.getMaxAllowedPacket()) { + try { + batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(commandIndex, argumentSetsInBatchSoFar, updateCounts, ex); + } + + counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts); + + queryBuf = new StringBuilder(); + argumentSetsInBatchSoFar = 0; + } + + queryBuf.append(nextQuery); + queryBuf.append(";"); + argumentSetsInBatchSoFar++; + } + + if (queryBuf.length() > 0) { + try { + batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(commandIndex - 1, argumentSetsInBatchSoFar, updateCounts, ex); + } + + counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts); + } + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + + timeoutTask = null; + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + } + + resetCancelledState(); + + try { + if (batchStmt != null) { + batchStmt.close(); + } + } finally { + if (!multiQueriesEnabled) { + locallyScopedConn.getIO().disableMultiQueries(); + } + } + } + } + } + + protected int processMultiCountsAndKeys(StatementImpl batchedStatement, int updateCountCounter, long[] updateCounts) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount(); + + boolean doGenKeys = this.batchedGeneratedKeys != null; + + byte[][] row = null; + + if (doGenKeys) { + long generatedKey = batchedStatement.getLastInsertID(); + + row = new byte[1][]; + row[0] = StringUtils.getBytes(Long.toString(generatedKey)); + this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + + while (batchedStatement.getMoreResults() || batchedStatement.getLargeUpdateCount() != -1) { + updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount(); + + if (doGenKeys) { + long generatedKey = batchedStatement.getLastInsertID(); + + row = new byte[1][]; + row[0] = StringUtils.getBytes(Long.toString(generatedKey)); + this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + } + + return updateCountCounter; + } + } + + protected SQLException handleExceptionForBatch(int endOfBatchIndex, int numValuesPerBatch, long[] updateCounts, SQLException ex) + throws BatchUpdateException, SQLException { + for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) { + updateCounts[j] = EXECUTE_FAILED; + } + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + return ex; + } // else: throw the exception immediately + + long[] newUpdateCounts = new long[endOfBatchIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, endOfBatchIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + + /** + * Execute a SQL statement that returns a single ResultSet + * + * @param sql + * typically a static SQL SELECT statement + * + * @return a ResulSet that contains the data produced by the query + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.ResultSet executeQuery(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MySQLConnection locallyScopedConn = this.connection; + + this.retrieveGeneratedKeys = false; + + resetCancelledState(); + + checkNullOrEmptyQuery(sql); + + setupStreamingTimeout(locallyScopedConn); + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, locallyScopedConn.serverSupportsConvertFn(), this.connection); + + if (escapedSqlResult instanceof String) { + sql = (String) escapedSqlResult; + } else { + sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + } + + char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + + if (sql.charAt(0) == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return this.results; + } + } + + checkForDml(sql, firstStatementChar); + + implicitlyCloseAllOpenResults(); + + CachedResultSetMetaData cachedMetaData = null; + + if (useServerFetch()) { + this.results = createResultSetUsingServerFetch(sql); + + return this.results; + } + + CancelTask timeoutTask = null; + + String oldCatalog = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis); + } + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + + Field[] cachedFields = null; + + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetaData = locallyScopedConn.getCachedMetaData(sql); + + if (cachedMetaData != null) { + cachedFields = cachedMetaData.fields; + } + } + + locallyScopedConn.setSessionMaxRows(this.maxRows); + + statementBegins(); + + this.results = locallyScopedConn.execSQL(this, sql, this.maxRows, null, this.resultSetType, this.resultSetConcurrency, + createStreamingResultSet(), this.currentCatalog, cachedFields); + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + SQLException cause = null; + + if (this.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + resetCancelledState(); + + throw cause; + } + } + } finally { + this.statementExecuting.set(false); + + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + + this.lastInsertId = this.results.getUpdateID(); + + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results); + } else { + if (this.connection.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results); + } + } + + return this.results; + } + } + + protected void doPingInstead() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.pingTarget != null) { + this.pingTarget.doPing(); + } else { + this.connection.ping(); + } + + ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet(); + this.results = fakeSelectOneResultSet; + } + } + + protected ResultSetInternalMethods generatePingResultSet() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Field[] fields = { new Field(null, "1", Types.BIGINT, 1) }; + ArrayList rows = new ArrayList(); + byte[] colVal = new byte[] { (byte) '1' }; + + rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor())); + + return (ResultSetInternalMethods) DatabaseMetaData.buildResultSet(fields, rows, this.connection); + } + } + + protected void executeSimpleNonQuery(MySQLConnection c, String nonQuery) throws SQLException { + c.execSQL(this, nonQuery, -1, null, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog, null, false).close(); + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL statements that return nothing such as SQL DDL statements can be executed. + * + * @param sql + * a SQL statement + * + * @return either a row count, or 0 for SQL commands + * + * @exception SQLException + * if a database access error occurs + */ + public int executeUpdate(String sql) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql)); + } + + protected long executeUpdateInternal(String sql, boolean isBatch, boolean returnGeneratedKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + MySQLConnection locallyScopedConn = this.connection; + + checkNullOrEmptyQuery(sql); + + resetCancelledState(); + + char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + + this.retrieveGeneratedKeys = returnGeneratedKeys; + + this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstStatementChar == 'I' && containsOnDuplicateKeyInString(sql); + + ResultSetInternalMethods rs = null; + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.connection.serverSupportsConvertFn(), this.connection); + + if (escapedSqlResult instanceof String) { + sql = (String) escapedSqlResult; + } else { + sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + } + + if (locallyScopedConn.isReadOnly(false)) { + throw SQLError.createSQLException(Messages.getString("Statement.42") + Messages.getString("Statement.43"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { + throw SQLError.createSQLException(Messages.getString("Statement.46"), "01S03", getExceptionInterceptor()); + } + + implicitlyCloseAllOpenResults(); + + // The checking and changing of catalogs must happen in sequence, so synchronize on the same mutex that _conn is using + + CancelTask timeoutTask = null; + + String oldCatalog = null; + + boolean readInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); + if (returnGeneratedKeys && firstStatementChar == 'R') { + // If this is a 'REPLACE' query, we need to be able to parse the 'info' message returned from the server to determine the actual number of keys + // generated. + locallyScopedConn.setReadInfoMsgEnabled(true); + } + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(this); + locallyScopedConn.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis); + } + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(-1); + + statementBegins(); + + // null catalog: force read of field info on DML + rs = locallyScopedConn.execSQL(this, sql, -1, null, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.currentCatalog, null, isBatch); + + if (timeoutTask != null) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + SQLException cause = null; + + if (this.wasCancelledByTimeout) { + cause = new MySQLTimeoutException(); + } else { + cause = new MySQLStatementCancelledException(); + } + + resetCancelledState(); + + throw cause; + } + } + } finally { + locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState); + + if (timeoutTask != null) { + timeoutTask.cancel(); + + locallyScopedConn.getCancelTimer().purge(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (!isBatch) { + this.statementExecuting.set(false); + } + } + + this.results = rs; + + rs.setFirstCharOfQuery(firstStatementChar); + + this.updateCount = rs.getUpdateCount(); + + this.lastInsertId = rs.getUpdateID(); + + return this.updateCount; + } + } + + /** + * @see StatementImpl#executeUpdate(String, int) + */ + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, autoGeneratedKeys)); + } + + /** + * @see StatementImpl#executeUpdate(String, int[]) + */ + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnIndexes)); + } + + /** + * @see StatementImpl#executeUpdate(String, String[]) + */ + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnNames)); + } + + /** + * Optimization to only use one calendar per-session, or calculate it for + * each call, depending on user configuration + */ + protected Calendar getCalendarInstanceForSessionOrNew() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection != null) { + return this.connection.getCalendarInstanceForSessionOrNew(); + } + // punt, no connection around + return new GregorianCalendar(); + } + } + + /** + * JDBC 2.0 Return the Connection that produced the Statement. + * + * @return the Connection that produced the Statement + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.connection; + } + } + + /** + * JDBC 2.0 Determine the fetch direction. + * + * @return the default fetch direction + * + * @exception SQLException + * if a database-access error occurs + */ + public int getFetchDirection() throws SQLException { + return java.sql.ResultSet.FETCH_FORWARD; + } + + /** + * JDBC 2.0 Determine the default fetch size. + * + * @return the number of rows to fetch at a time + * + * @throws SQLException + * if an error occurs + */ + public int getFetchSize() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.fetchSize; + } + } + + /** + * @throws SQLException + */ + public java.sql.ResultSet getGeneratedKeys() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.retrieveGeneratedKeys) { + throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (this.batchedGeneratedKeys == null) { + if (this.lastQueryIsOnDupKeyUpdate) { + return this.generatedKeysResults = getGeneratedKeysInternal(1); + } + return this.generatedKeysResults = getGeneratedKeysInternal(); + } + + Field[] fields = new Field[1]; + fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 20); + fields[0].setConnection(this.connection); + + this.generatedKeysResults = com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields, new RowDataStatic(this.batchedGeneratedKeys), + this.connection, this, false); + + return this.generatedKeysResults; + } + } + + /* + * Needed because there's no concept of super.super to get to this + * implementation from ServerPreparedStatement when dealing with batched + * updates. + */ + protected ResultSetInternalMethods getGeneratedKeysInternal() throws SQLException { + long numKeys = getLargeUpdateCount(); + return getGeneratedKeysInternal(numKeys); + } + + protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + Field[] fields = new Field[1]; + fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 20); + fields[0].setConnection(this.connection); + fields[0].setUseOldNameMetadata(true); + + ArrayList rowSet = new ArrayList(); + + long beginAt = getLastInsertID(); + + if (beginAt < 0) { // looking at an UNSIGNED BIGINT that has overflowed + fields[0].setUnsigned(); + } + + if (this.results != null) { + String serverInfo = this.results.getServerInfo(); + + // + // Only parse server info messages for 'REPLACE' queries + // + if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R') && (serverInfo != null) && (serverInfo.length() > 0)) { + numKeys = getRecordCountFromInfo(serverInfo); + } + + if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) { + for (int i = 0; i < numKeys; i++) { + byte[][] row = new byte[1][]; + if (beginAt > 0) { + row[0] = StringUtils.getBytes(Long.toString(beginAt)); + } else { + byte[] asBytes = new byte[8]; + asBytes[7] = (byte) (beginAt & 0xff); + asBytes[6] = (byte) (beginAt >>> 8); + asBytes[5] = (byte) (beginAt >>> 16); + asBytes[4] = (byte) (beginAt >>> 24); + asBytes[3] = (byte) (beginAt >>> 32); + asBytes[2] = (byte) (beginAt >>> 40); + asBytes[1] = (byte) (beginAt >>> 48); + asBytes[0] = (byte) (beginAt >>> 56); + + BigInteger val = new BigInteger(1, asBytes); + + row[0] = val.toString().getBytes(); + } + rowSet.add(new ByteArrayRow(row, getExceptionInterceptor())); + beginAt += this.connection.getAutoIncrementIncrement(); + } + } + } + + com.mysql.jdbc.ResultSetImpl gkRs = com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields, new RowDataStatic(rowSet), + this.connection, this, false); + + return gkRs; + } + } + + /** + * Returns the id used when profiling + * + * @return the id used when profiling. + */ + protected int getId() { + return this.statementId; + } + + /** + * getLastInsertID returns the value of the auto_incremented key after an + * executeQuery() or excute() call. + * + *

    + * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()" which is tied to the Connection that created this Statement, and therefore could + * have had many INSERTS performed before one gets a chance to call "select LAST_INSERT_ID()". + *

    + * + * @return the last update ID. + */ + public long getLastInsertID() { + try { + synchronized (checkClosed().getConnectionMutex()) { + return this.lastInsertId; + } + } catch (SQLException e) { + throw new RuntimeException(e); // evolve interface to throw SQLException + } + } + + /** + * getLongUpdateCount returns the current result as an update count, if the + * result is a ResultSet or there are no more results, -1 is returned. It + * should only be called once per result. + * + *

    + * This method returns longs as MySQL server versions newer than 3.22.4 return 64-bit values for update counts + *

    + * + * @return the current update count. + */ + public long getLongUpdateCount() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return -1; + } + + if (this.results.reallyResult()) { + return -1; + } + + return this.updateCount; + } + } catch (SQLException e) { + throw new RuntimeException(e); // evolve interface to throw SQLException + } + } + + /** + * The maxFieldSize limit (in bytes) is the maximum amount of data returned + * for any column value; it only applies to BINARY, VARBINARY, + * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is + * exceeded, the excess data is silently discarded. + * + * @return the current max column size limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getMaxFieldSize() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.maxFieldSize; + } + } + + /** + * The maxRows limit is set to limit the number of rows that any ResultSet + * can contain. If the limit is exceeded, the excess rows are silently + * dropped. + * + * @return the current maximum row limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getMaxRows() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.maxRows <= 0) { + return 0; + } + + return this.maxRows; + } + } + + /** + * getMoreResults moves to a Statement's next result. If it returns true, + * this result is a ResulSet. + * + * @return true if the next ResultSet is valid + * + * @exception SQLException + * if a database access error occurs + */ + public boolean getMoreResults() throws SQLException { + return getMoreResults(CLOSE_CURRENT_RESULT); + } + + /** + * @see StatementImpl#getMoreResults(int) + */ + public boolean getMoreResults(int current) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return false; + } + + boolean streamingMode = createStreamingResultSet(); + + if (streamingMode) { + if (this.results.reallyResult()) { + while (this.results.next()) { + // need to drain remaining rows to get to server status which tells us whether more results actually exist or not + } + } + } + + ResultSetInternalMethods nextResultSet = this.results.getNextResultSet(); + + switch (current) { + case java.sql.Statement.CLOSE_CURRENT_RESULT: + + if (this.results != null) { + if (!(streamingMode || this.connection.getDontTrackOpenResources())) { + this.results.realClose(false); + } + + this.results.clearNextResult(); + } + + break; + + case java.sql.Statement.CLOSE_ALL_RESULTS: + + if (this.results != null) { + if (!(streamingMode || this.connection.getDontTrackOpenResources())) { + this.results.realClose(false); + } + + this.results.clearNextResult(); + } + + closeAllOpenResults(); + + break; + + case java.sql.Statement.KEEP_CURRENT_RESULT: + if (!this.connection.getDontTrackOpenResources()) { + this.openResults.add(this.results); + } + + this.results.clearNextResult(); // nobody besides us should + // ever need this value... + break; + + default: + throw SQLError.createSQLException(Messages.getString("Statement.19"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.results = nextResultSet; + + if (this.results == null) { + this.updateCount = -1; + this.lastInsertId = -1; + } else if (this.results.reallyResult()) { + this.updateCount = -1; + this.lastInsertId = -1; + } else { + this.updateCount = this.results.getUpdateCount(); + this.lastInsertId = this.results.getUpdateID(); + } + + boolean moreResults = (this.results != null) && this.results.reallyResult(); + if (!moreResults) { + checkAndPerformCloseOnCompletionAction(); + } + return moreResults; + } + } + + /** + * The queryTimeout limit is the number of seconds the driver will wait for + * a Statement to execute. If the limit is exceeded, a SQLException is + * thrown. + * + * @return the current query timeout limit in seconds; 0 = unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getQueryTimeout() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.timeoutInMillis / 1000; + } + } + + /** + * Parses actual record count from 'info' message + * + * @param serverInfo + */ + private long getRecordCountFromInfo(String serverInfo) { + StringBuilder recordsBuf = new StringBuilder(); + long recordsCount = 0; + long duplicatesCount = 0; + + char c = (char) 0; + + int length = serverInfo.length(); + int i = 0; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (Character.isDigit(c)) { + break; + } + } + + recordsBuf.append(c); + i++; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (!Character.isDigit(c)) { + break; + } + + recordsBuf.append(c); + } + + recordsCount = Long.parseLong(recordsBuf.toString()); + + StringBuilder duplicatesBuf = new StringBuilder(); + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (Character.isDigit(c)) { + break; + } + } + + duplicatesBuf.append(c); + i++; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (!Character.isDigit(c)) { + break; + } + + duplicatesBuf.append(c); + } + + duplicatesCount = Long.parseLong(duplicatesBuf.toString()); + + return recordsCount - duplicatesCount; + } + + /** + * getResultSet returns the current result as a ResultSet. It should only be + * called once per result. + * + * @return the current result set; null if there are no more + * + * @exception SQLException + * if a database access error occurs (why?) + */ + public java.sql.ResultSet getResultSet() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results : null; + } + } + + /** + * JDBC 2.0 Determine the result set concurrency. + * + * @return CONCUR_UPDATABLE or CONCUR_READONLY + * + * @throws SQLException + * if an error occurs + */ + public int getResultSetConcurrency() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.resultSetConcurrency; + } + } + + /** + * @see StatementImpl#getResultSetHoldability() + */ + public int getResultSetHoldability() throws SQLException { + return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT; + } + + protected ResultSetInternalMethods getResultSetInternal() { + try { + synchronized (checkClosed().getConnectionMutex()) { + return this.results; + } + } catch (SQLException e) { + return this.results; // you end up with the same thing as before, you'll get exception when actually trying to use it + } + } + + /** + * JDBC 2.0 Determine the result set type. + * + * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE) + * + * @throws SQLException + * if an error occurs. + */ + public int getResultSetType() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.resultSetType; + } + } + + /** + * getUpdateCount returns the current result as an update count, if the + * result is a ResultSet or there are no more results, -1 is returned. It + * should only be called once per result. + * + * @return the current result as an update count. + * + * @exception SQLException + * if a database access error occurs + */ + public int getUpdateCount() throws SQLException { + return Util.truncateAndConvertToInt(getLargeUpdateCount()); + } + + /** + * The first warning reported by calls on this Statement is returned. A + * Statement's execute methods clear its java.sql.SQLWarning chain. + * Subsequent Statement warnings will be chained to this + * java.sql.SQLWarning. + * + *

    + * The Warning chain is automatically cleared each time a statement is (re)executed. + *

    + * + *

    + * Note: If you are processing a ResultSet then any warnings associated with ResultSet reads will be chained on the ResultSet object. + *

    + * + * @return the first java.sql.SQLWarning or null + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.SQLWarning getWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.clearWarningsCalled) { + return null; + } + + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + SQLWarning pendingWarningsFromServer = SQLError.convertShowWarningsToSQLWarnings(this.connection); + + if (this.warningChain != null) { + this.warningChain.setNextWarning(pendingWarningsFromServer); + } else { + this.warningChain = pendingWarningsFromServer; + } + + return this.warningChain; + } + + return this.warningChain; + } + } + + /** + * Closes this statement, and frees resources. + * + * @param calledExplicitly + * was this called from close()? + * + * @throws SQLException + * if an error occurs + */ + protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null || this.isClosed) { + return; // already closed + } + + // do it ASAP to reduce the chance of calling this method concurrently from ConnectionImpl.closeAllOpenStatements() + if (!locallyScopedConn.getDontTrackOpenResources()) { + locallyScopedConn.unregisterStatement(this); + } + + if (this.useUsageAdvisor) { + if (!calledExplicitly) { + String message = Messages.getString("Statement.63") + Messages.getString("Statement.64"); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", this.currentCatalog, this.connectionId, this.getId(), -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + if (closeOpenResults) { + closeOpenResults = !(this.holdResultsOpenOverClose || this.connection.getDontTrackOpenResources()); + } + + if (closeOpenResults) { + if (this.results != null) { + + try { + this.results.close(); + } catch (Exception ex) { + } + } + + if (this.generatedKeysResults != null) { + + try { + this.generatedKeysResults.close(); + } catch (Exception ex) { + } + } + + closeAllOpenResults(); + } + + this.isClosed = true; + + this.results = null; + this.generatedKeysResults = null; + this.connection = null; + this.warningChain = null; + this.openResults = null; + this.batchedGeneratedKeys = null; + this.localInfileInputStream = null; + this.pingTarget = null; + } + + /** + * setCursorName defines the SQL cursor name that will be used by subsequent + * execute methods. This name can then be used in SQL positioned + * update/delete statements to identify the current row in the ResultSet + * generated by this statement. If a database doesn't support positioned + * update/delete, this method is a no-op. + * + *

    + * Note: This MySQL driver does not support cursors. + *

    + * + * @param name + * the new cursor name + * + * @exception SQLException + * if a database access error occurs + */ + public void setCursorName(String name) throws SQLException { + // No-op + } + + /** + * If escape scanning is on (the default), the driver will do escape + * substitution before sending the SQL to the database. + * + * @param enable + * true to enable; false to disable + * + * @exception SQLException + * if a database access error occurs + */ + public void setEscapeProcessing(boolean enable) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.doEscapeProcessing = enable; + } + } + + /** + * JDBC 2.0 Give a hint as to the direction in which the rows in a result + * set will be processed. The hint applies only to result sets created using + * this Statement object. The default value is ResultSet.FETCH_FORWARD. + * + * @param direction + * the initial direction for processing rows + * + * @exception SQLException + * if a database-access error occurs or direction is not one + * of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or + * ResultSet.FETCH_UNKNOWN + */ + public void setFetchDirection(int direction) throws SQLException { + switch (direction) { + case java.sql.ResultSet.FETCH_FORWARD: + case java.sql.ResultSet.FETCH_REVERSE: + case java.sql.ResultSet.FETCH_UNKNOWN: + break; + + default: + throw SQLError.createSQLException(Messages.getString("Statement.5"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should + * be fetched from the database when more rows are needed. The number of + * rows specified only affects result sets created using this statement. If + * the value specified is zero, then the hint is ignored. The default value + * is zero. + * + * @param rows + * the number of rows to fetch + * + * @exception SQLException + * if a database-access error occurs, or the condition 0 + * <= rows <= this.getMaxRows() is not satisfied. + */ + public void setFetchSize(int rows) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (((rows < 0) && (rows != Integer.MIN_VALUE)) || ((this.maxRows > 0) && (rows > this.getMaxRows()))) { + throw SQLError.createSQLException(Messages.getString("Statement.7"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.fetchSize = rows; + } + } + + public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.holdResultsOpenOverClose = holdResultsOpenOverClose; + } + } catch (SQLException e) { + // FIXME: can't break interface at this point + } + } + + /** + * Sets the maxFieldSize + * + * @param max + * the new max column size limit; zero means unlimited + * + * @exception SQLException + * if size exceeds buffer size + */ + public void setMaxFieldSize(int max) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (max < 0) { + throw SQLError.createSQLException(Messages.getString("Statement.11"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + int maxBuf = (this.connection != null) ? this.connection.getMaxAllowedPacket() : MysqlIO.getMaxBuf(); + + if (max > maxBuf) { + throw SQLError.createSQLException(Messages.getString("Statement.13", new Object[] { Long.valueOf(maxBuf) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.maxFieldSize = max; + } + } + + /** + * Set the maximum number of rows + * + * @param max + * the new max rows limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + * + * @see getMaxRows + */ + public void setMaxRows(int max) throws SQLException { + setLargeMaxRows(max); + } + + /** + * Sets the queryTimeout limit + * + * @param seconds + * - + * the new query timeout limit in seconds + * + * @exception SQLException + * if a database access error occurs + */ + public void setQueryTimeout(int seconds) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (seconds < 0) { + throw SQLError.createSQLException(Messages.getString("Statement.21"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.timeoutInMillis = seconds * 1000; + } + } + + /** + * Sets the concurrency for result sets generated by this statement + * + * @param concurrencyFlag + */ + void setResultSetConcurrency(int concurrencyFlag) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.resultSetConcurrency = concurrencyFlag; + } + } catch (SQLException e) { + // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... + } + } + + /** + * Sets the result set type for result sets generated by this statement + * + * @param typeFlag + */ + void setResultSetType(int typeFlag) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.resultSetType = typeFlag; + } + } catch (SQLException e) { + // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... + } + } + + protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.retrieveGeneratedKeys) { + java.sql.ResultSet rs = null; + + try { + rs = batchedStatement.getGeneratedKeys(); + + while (rs.next()) { + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + } + } finally { + if (rs != null) { + rs.close(); + } + } + } + } + } + + protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.retrieveGeneratedKeys) { + java.sql.ResultSet rs = null; + + try { + if (maxKeys == 0) { + rs = getGeneratedKeysInternal(); + } else { + rs = getGeneratedKeysInternal(maxKeys); + } + + while (rs.next()) { + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + } + } finally { + this.isImplicitlyClosingResults = true; + try { + if (rs != null) { + rs.close(); + } + } finally { + this.isImplicitlyClosingResults = false; + } + } + } + } + } + + private boolean useServerFetch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.connection.isCursorFetchEnabled() && this.fetchSize > 0 && this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY + && this.resultSetType == ResultSet.TYPE_FORWARD_ONLY; + } + } + + public boolean isClosed() throws SQLException { + MySQLConnection locallyScopedConn = this.connection; + if (locallyScopedConn == null) { + return true; + } + synchronized (locallyScopedConn.getConnectionMutex()) { + return this.isClosed; + } + } + + private boolean isPoolable = true; + + public boolean isPoolable() throws SQLException { + return this.isPoolable; + } + + public void setPoolable(boolean poolable) throws SQLException { + this.isPoolable = poolable; + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + protected static int findStartOfStatement(String sql) { + int statementStartPos = 0; + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) { + statementStartPos = sql.indexOf("*/"); + + if (statementStartPos == -1) { + statementStartPos = 0; + } else { + statementStartPos += 2; + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--") || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) { + statementStartPos = sql.indexOf('\n'); + + if (statementStartPos == -1) { + statementStartPos = sql.indexOf('\r'); + + if (statementStartPos == -1) { + statementStartPos = 0; + } + } + } + + return statementStartPos; + } + + private InputStream localInfileInputStream; + + protected final boolean version5013OrNewer; + + public InputStream getLocalInfileInputStream() { + return this.localInfileInputStream; + } + + public void setLocalInfileInputStream(InputStream stream) { + this.localInfileInputStream = stream; + } + + public void setPingTarget(PingTarget pingTarget) { + this.pingTarget = pingTarget; + } + + public ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + protected boolean containsOnDuplicateKeyInString(String sql) { + return getOnDuplicateKeyLocation(sql, this.connection.getDontCheckOnDuplicateKeyUpdateInSQL(), this.connection.getRewriteBatchedStatements(), + this.connection.isNoBackslashEscapesSet()) != -1; + } + + protected static int getOnDuplicateKeyLocation(String sql, boolean dontCheckOnDuplicateKeyUpdateInSQL, boolean rewriteBatchedStatements, + boolean noBackslashEscapes) { + return dontCheckOnDuplicateKeyUpdateInSQL && !rewriteBatchedStatements ? -1 : StringUtils.indexOfIgnoreCase(0, sql, ON_DUPLICATE_KEY_UPDATE_CLAUSE, + "\"'`", "\"'`", noBackslashEscapes ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } + + private boolean closeOnCompletion = false; + + public void closeOnCompletion() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.closeOnCompletion = true; + } + } + + public boolean isCloseOnCompletion() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.closeOnCompletion; + } + } + + /** + * JDBC 4.2 + * Same as {@link #executeBatch()} but returns long[] instead of int[]. + */ + public long[] executeLargeBatch() throws SQLException { + return executeBatchInternal(); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql) throws SQLException { + return executeUpdateInternal(sql, false, false); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return executeUpdateInternal(sql, false, autoGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + return executeUpdateInternal(sql, false, columnIndexes != null && columnIndexes.length > 0); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, String[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + return executeUpdateInternal(sql, false, columnNames != null && columnNames.length > 0); + } + + /** + * JDBC 4.2 + * Same as {@link #getMaxRows()} but returns long instead of int. + */ + public long getLargeMaxRows() throws SQLException { + // Max rows is limited by MySQLDefs.MAX_ROWS anyway... + return getMaxRows(); + } + + /** + * JDBC 4.2 + * Same as {@link #getUpdateCount()} but returns long instead of int; + */ + public long getLargeUpdateCount() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return -1; + } + + if (this.results.reallyResult()) { + return -1; + } + + return this.results.getUpdateCount(); + } + } + + /** + * JDBC 4.2 + * Same as {@link #setMaxRows(int)} but accepts a long value instead of an int. + */ + public void setLargeMaxRows(long max) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) { + throw SQLError.createSQLException(Messages.getString("Statement.15") + max + " > " + MysqlDefs.MAX_ROWS + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (max == 0) { + max = -1; + } + + this.maxRows = (int) max; + } + } + + boolean isCursorRequired() throws SQLException { + return false; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptor.java new file mode 100644 index 0000000..06de633 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptor.java @@ -0,0 +1,145 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +/** + * Implement this interface to be placed "in between" query execution, so that you can influence it. (currently experimental). + * + * StatementInterceptors are "chainable" when configured by the user, the results returned by the "current" interceptor will be passed on to the next on in the + * chain, from left-to-right order, as specified by the user in the JDBC configuration property "statementInterceptors". + */ + +public interface StatementInterceptor extends Extension { + + /** + * Called once per connection that wants to use the interceptor + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param conn + * the connection for which this interceptor is being created + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. StatementInterceptor properties are not exposed via + * accessor/mutator methods on DataSources. + * + * @throws SQLException + * should be thrown if the the StatementInterceptor + * can not initialize itself. + */ + + public abstract void init(Connection conn, Properties props) throws SQLException; + + /** + * Called before the given statement is going to be sent to the + * server for processing. + * + * Interceptors are free to return a result set (which must implement the + * interface com.mysql.jdbc.ResultSetInternalMethods), and if so, + * the server will not execute the query, and the given result set will be + * returned to the application instead. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the SQL representation of the statement + * @param interceptedStatement + * the actual statement instance being intercepted + * @param connection + * the connection the statement is using (passed in to make + * thread-safe implementations straightforward) + * + * @return a result set that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * statement. + * + * @throws SQLException + * if an error occurs during execution + * + * @see com.mysql.jdbc.ResultSetInternalMethods + */ + + public abstract ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException; + + /** + * Called after the given statement has been sent to the server + * for processing. + * + * Interceptors are free to inspect the "original" result set, and if a + * different result set is returned by the interceptor, it is used in place + * of the "original" result set. (the result set returned by the interceptor + * must implement the interface + * com.mysql.jdbc.ResultSetInternalMethods). + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the SQL representation of the statement + * @param interceptedStatement + * the actual statement instance being intercepted + * @param connection + * the connection the statement is using (passed in to make + * thread-safe implementations straightforward) + * + * @return a result set that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * statement. + * + * @throws SQLException + * if an error occurs during execution + * + * @see com.mysql.jdbc.ResultSetInternalMethods + */ + public abstract ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, + Connection connection) throws SQLException; + + /** + * Should the driver execute this interceptor only for the + * "original" top-level query, and not put it in the execution + * path for queries that may be executed from other interceptors? + * + * If an interceptor issues queries using the connection it was created for, + * and does not return true for this method, it must ensure + * that it does not cause infinite recursion. + * + * @return true if the driver should ensure that this interceptor is only + * executed for the top-level "original" query. + */ + public abstract boolean executeTopLevelOnly(); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + public abstract void destroy(); +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptorV2.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptorV2.java new file mode 100644 index 0000000..6e0e37a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StatementInterceptorV2.java @@ -0,0 +1,139 @@ +/* + Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +public interface StatementInterceptorV2 extends Extension { + + /** + * Called once per connection that wants to use the interceptor + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param conn + * the connection for which this interceptor is being created + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. StatementInterceptor properties are not exposed via + * accessor/mutator methods on DataSources. + * + * @throws SQLException + * should be thrown if the the StatementInterceptor + * can not initialize itself. + */ + + public abstract void init(Connection conn, Properties props) throws SQLException; + + /** + * Called before the given statement is going to be sent to the + * server for processing. + * + * Interceptors are free to return a result set (which must implement the + * interface com.mysql.jdbc.ResultSetInternalMethods), and if so, + * the server will not execute the query, and the given result set will be + * returned to the application instead. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the SQL representation of the statement + * @param interceptedStatement + * the actual statement instance being intercepted + * @param connection + * the connection the statement is using (passed in to make + * thread-safe implementations straightforward) + * + * @return a result set that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * statement. + * + * @throws SQLException + * if an error occurs during execution + * + * @see com.mysql.jdbc.ResultSetInternalMethods + */ + + public abstract ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException; + + /** + * Should the driver execute this interceptor only for the + * "original" top-level query, and not put it in the execution + * path for queries that may be executed from other interceptors? + * + * If an interceptor issues queries using the connection it was created for, + * and does not return true for this method, it must ensure + * that it does not cause infinite recursion. + * + * @return true if the driver should ensure that this interceptor is only + * executed for the top-level "original" query. + */ + public abstract boolean executeTopLevelOnly(); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + public abstract void destroy(); + + /** + * Called after the given statement has been sent to the server + * for processing, instead of the StatementAware postProcess() of the earlier + * api. + * + * Interceptors are free to inspect the "original" result set, and if a + * different result set is returned by the interceptor, it is used in place + * of the "original" result set. (the result set returned by the interceptor + * must implement the interface + * com.mysql.jdbc.ResultSetInternalMethods). + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the SQL representation of the statement + * @param interceptedStatement + * the actual statement instance being intercepted + * @param connection + * the connection the statement is using (passed in to make + * thread-safe implementations straightforward) + * + * @return a result set that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * statement. + * + * @throws SQLException + * if an error occurs during execution + * + * @see com.mysql.jdbc.ResultSetInternalMethods + */ + public abstract ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, + Connection connection, int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException; +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StreamingNotifiable.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StreamingNotifiable.java new file mode 100644 index 0000000..52c0470 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StreamingNotifiable.java @@ -0,0 +1,30 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +public interface StreamingNotifiable { + + public abstract void setWasStreamingResults(); + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StringUtils.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StringUtils.java new file mode 100644 index 0000000..d7b7c87 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/StringUtils.java @@ -0,0 +1,2382 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Various utility methods for converting to/from byte arrays in the platform encoding + */ +public class StringUtils { + public enum SearchMode { + ALLOW_BACKSLASH_ESCAPE, SKIP_BETWEEN_MARKERS, SKIP_BLOCK_COMMENTS, SKIP_LINE_COMMENTS, SKIP_WHITE_SPACE; + } + + /* + * Convenience EnumSets for several SearchMode combinations + */ + + /** + * Full search mode: allow backslash escape, skip between markers, skip block comments, skip line comments and skip white space. + */ + public static final Set SEARCH_MODE__ALL = Collections.unmodifiableSet(EnumSet.allOf(SearchMode.class)); + + /** + * Search mode: skip between markers, skip block comments, skip line comments and skip white space. + */ + public static final Set SEARCH_MODE__MRK_COM_WS = Collections.unmodifiableSet( + EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE)); + + /** + * Search mode: allow backslash escape, skip block comments, skip line comments and skip white space. + */ + public static final Set SEARCH_MODE__BSESC_COM_WS = Collections.unmodifiableSet( + EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BLOCK_COMMENTS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE)); + + /** + * Search mode: allow backslash escape, skip between markers and skip white space. + */ + public static final Set SEARCH_MODE__BSESC_MRK_WS = Collections + .unmodifiableSet(EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_WHITE_SPACE)); + + /** + * Search mode: skip block comments, skip line comments and skip white space. + */ + public static final Set SEARCH_MODE__COM_WS = Collections + .unmodifiableSet(EnumSet.of(SearchMode.SKIP_BLOCK_COMMENTS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE)); + + /** + * Search mode: skip between markers and skip white space. + */ + public static final Set SEARCH_MODE__MRK_WS = Collections + .unmodifiableSet(EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_WHITE_SPACE)); + + /** + * Empty search mode. + */ + public static final Set SEARCH_MODE__NONE = Collections.unmodifiableSet(EnumSet.noneOf(SearchMode.class)); + + // length of MySQL version reference in comments of type '/*![00000] */' + private static final int NON_COMMENTS_MYSQL_VERSION_REF_LENGTH = 5; + + private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE; + + private static byte[] allBytes = new byte[BYTE_RANGE]; + + private static char[] byteToChars = new char[BYTE_RANGE]; + + private static Method toPlainStringMethod; + + static final int WILD_COMPARE_MATCH_NO_WILD = 0; + + static final int WILD_COMPARE_MATCH_WITH_WILD = 1; + + static final int WILD_COMPARE_NO_MATCH = -1; + + private static final ConcurrentHashMap charsetsByAlias = new ConcurrentHashMap(); + + private static final String platformEncoding = System.getProperty("file.encoding"); + + private static final String VALID_ID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789$_#@"; + + static Charset findCharset(String alias) throws UnsupportedEncodingException { + try { + Charset cs = charsetsByAlias.get(alias); + + if (cs == null) { + cs = Charset.forName(alias); + Charset oldCs = charsetsByAlias.putIfAbsent(alias, cs); + if (oldCs != null) { + // if the previous value was recently set by another thread we return it instead of value we found here + cs = oldCs; + } + } + + return cs; + + // We re-throw these runtimes for compatibility with java.io + } catch (UnsupportedCharsetException uce) { + throw new UnsupportedEncodingException(alias); + } catch (IllegalCharsetNameException icne) { + throw new UnsupportedEncodingException(alias); + } catch (IllegalArgumentException iae) { + throw new UnsupportedEncodingException(alias); + } + } + + static { + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + allBytes[i - Byte.MIN_VALUE] = (byte) i; + } + + String allBytesString = new String(allBytes, 0, Byte.MAX_VALUE - Byte.MIN_VALUE); + + int allBytesStringLen = allBytesString.length(); + + for (int i = 0; (i < (Byte.MAX_VALUE - Byte.MIN_VALUE)) && (i < allBytesStringLen); i++) { + byteToChars[i] = allBytesString.charAt(i); + } + + try { + toPlainStringMethod = BigDecimal.class.getMethod("toPlainString", new Class[0]); + } catch (NoSuchMethodException nsme) { + // that's okay, we fallback to .toString() + } + } + + /** + * Takes care of the fact that Sun changed the output of + * BigDecimal.toString() between JDK-1.4 and JDK 5 + * + * @param decimal + * the big decimal to stringify + * + * @return a string representation of 'decimal' + */ + public static String consistentToString(BigDecimal decimal) { + if (decimal == null) { + return null; + } + + if (toPlainStringMethod != null) { + try { + return (String) toPlainStringMethod.invoke(decimal, (Object[]) null); + } catch (InvocationTargetException invokeEx) { + // that's okay, we fall-through to decimal.toString() + } catch (IllegalAccessException accessEx) { + // that's okay, we fall-through to decimal.toString() + } + } + + return decimal.toString(); + } + + /** + * Dumps the given bytes to STDOUT as a hex dump (up to length bytes). + * + * @param byteBuffer + * the data to print as hex + * @param length + * the number of bytes to print + * + * @return ... + */ + public static String dumpAsHex(byte[] byteBuffer, int length) { + StringBuilder outputBuilder = new StringBuilder(length * 4); + + int p = 0; + int rows = length / 8; + + for (int i = 0; (i < rows) && (p < length); i++) { + int ptemp = p; + + for (int j = 0; j < 8; j++) { + String hexVal = Integer.toHexString(byteBuffer[ptemp] & 0xff); + + if (hexVal.length() == 1) { + hexVal = "0" + hexVal; + } + + outputBuilder.append(hexVal + " "); + ptemp++; + } + + outputBuilder.append(" "); + + for (int j = 0; j < 8; j++) { + int b = 0xff & byteBuffer[p]; + + if (b > 32 && b < 127) { + outputBuilder.append((char) b + " "); + } else { + outputBuilder.append(". "); + } + + p++; + } + + outputBuilder.append("\n"); + } + + int n = 0; + + for (int i = p; i < length; i++) { + String hexVal = Integer.toHexString(byteBuffer[i] & 0xff); + + if (hexVal.length() == 1) { + hexVal = "0" + hexVal; + } + + outputBuilder.append(hexVal + " "); + n++; + } + + for (int i = n; i < 8; i++) { + outputBuilder.append(" "); + } + + outputBuilder.append(" "); + + for (int i = p; i < length; i++) { + int b = 0xff & byteBuffer[i]; + + if (b > 32 && b < 127) { + outputBuilder.append((char) b + " "); + } else { + outputBuilder.append(". "); + } + } + + outputBuilder.append("\n"); + + return outputBuilder.toString(); + } + + private static boolean endsWith(byte[] dataFrom, String suffix) { + for (int i = 1; i <= suffix.length(); i++) { + int dfOffset = dataFrom.length - i; + int suffixOffset = suffix.length() - i; + if (dataFrom[dfOffset] != suffix.charAt(suffixOffset)) { + return false; + } + } + return true; + } + + /** + * Unfortunately, SJIS has 0x5c as a high byte in some of its double-byte + * characters, so we need to escape it. + * + * @param origBytes + * the original bytes in SJIS format + * @param origString + * the string that had .getBytes() called on it + * + * @return byte[] with 0x5c escaped + */ + public static byte[] escapeEasternUnicodeByteStream(byte[] origBytes, String origString) { + if (origBytes == null) { + return null; + } + if (origBytes.length == 0) { + return new byte[0]; + } + + int bytesLen = origBytes.length; + int bufIndex = 0; + int strIndex = 0; + + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(bytesLen); + + while (true) { + if (origString.charAt(strIndex) == '\\') { + // write it out as-is + bytesOut.write(origBytes[bufIndex++]); + + // bytesOut.write(origBytes[bufIndex++]); + } else { + // Grab the first byte + int loByte = origBytes[bufIndex]; + + if (loByte < 0) { + loByte += 256; // adjust for signedness/wrap-around + } + + // We always write the first byte + bytesOut.write(loByte); + + // + // The codepage characters in question exist between + // 0x81-0x9F and 0xE0-0xFC... + // + // See: + // + // http://www.microsoft.com/GLOBALDEV/Reference/dbcs/932.htm + // + // Problematic characters in GBK + // + // U+905C : CJK UNIFIED IDEOGRAPH + // + // Problematic characters in Big5 + // + // B9F0 = U+5C62 : CJK UNIFIED IDEOGRAPH + // + if (loByte >= 0x80) { + if (bufIndex < (bytesLen - 1)) { + int hiByte = origBytes[bufIndex + 1]; + + if (hiByte < 0) { + hiByte += 256; // adjust for signedness/wrap-around + } + + // write the high byte here, and increment the index for the high byte + bytesOut.write(hiByte); + bufIndex++; + + // escape 0x5c if necessary + if (hiByte == 0x5C) { + bytesOut.write(hiByte); + } + } + } else if (loByte == 0x5c) { + if (bufIndex < (bytesLen - 1)) { + int hiByte = origBytes[bufIndex + 1]; + + if (hiByte < 0) { + hiByte += 256; // adjust for signedness/wrap-around + } + + if (hiByte == 0x62) { + // we need to escape the 0x5c + bytesOut.write(0x5c); + bytesOut.write(0x62); + bufIndex++; + } + } + } + + bufIndex++; + } + + if (bufIndex >= bytesLen) { + // we're done + break; + } + + strIndex++; + } + + return bytesOut.toByteArray(); + } + + /** + * Returns the first non whitespace char, converted to upper case + * + * @param searchIn + * the string to search in + * + * @return the first non-whitespace character, upper cased. + */ + public static char firstNonWsCharUc(String searchIn) { + return firstNonWsCharUc(searchIn, 0); + } + + public static char firstNonWsCharUc(String searchIn, int startAt) { + if (searchIn == null) { + return 0; + } + + int length = searchIn.length(); + + for (int i = startAt; i < length; i++) { + char c = searchIn.charAt(i); + + if (!Character.isWhitespace(c)) { + return Character.toUpperCase(c); + } + } + + return 0; + } + + public static char firstAlphaCharUc(String searchIn, int startAt) { + if (searchIn == null) { + return 0; + } + + int length = searchIn.length(); + + for (int i = startAt; i < length; i++) { + char c = searchIn.charAt(i); + + if (Character.isLetter(c)) { + return Character.toUpperCase(c); + } + } + + return 0; + } + + /** + * Adds '+' to decimal numbers that are positive (MySQL doesn't understand + * them otherwise + * + * @param dString + * The value as a string + * + * @return String the string with a '+' added (if needed) + */ + public static String fixDecimalExponent(String dString) { + int ePos = dString.indexOf('E'); + + if (ePos == -1) { + ePos = dString.indexOf('e'); + } + + if (ePos != -1) { + if (dString.length() > (ePos + 1)) { + char maybeMinusChar = dString.charAt(ePos + 1); + + if (maybeMinusChar != '-' && maybeMinusChar != '+') { + StringBuilder strBuilder = new StringBuilder(dString.length() + 1); + strBuilder.append(dString.substring(0, ePos + 1)); + strBuilder.append('+'); + strBuilder.append(dString.substring(ePos + 1, dString.length())); + dString = strBuilder.toString(); + } + } + } + + return dString; + } + + /** + * Returns the byte[] representation of the given char[] (re)using the given charset converter, and the given + * encoding. + */ + public static byte[] getBytes(char[] c, SingleByteCharsetConverter converter, String encoding, String serverEncoding, boolean parserKnowsUnicode, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + byte[] b; + + if (converter != null) { + b = converter.toBytes(c); + } else if (encoding == null) { + b = getBytes(c); + } else { + b = getBytes(c, encoding); + + if (!parserKnowsUnicode && CharsetMapping.requiresEscapeEasternUnicode(encoding)) { + + if (!encoding.equalsIgnoreCase(serverEncoding)) { + b = escapeEasternUnicodeByteStream(b, new String(c)); + } + } + } + + return b; + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.0") + encoding + Messages.getString("StringUtils.1"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of subset of the given char[] (re)using the given charset converter, and the + * given encoding. + */ + public static byte[] getBytes(char[] c, SingleByteCharsetConverter converter, String encoding, String serverEncoding, int offset, int length, + boolean parserKnowsUnicode, ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + byte[] b; + + if (converter != null) { + b = converter.toBytes(c, offset, length); + } else if (encoding == null) { + b = getBytes(c, offset, length); + } else { + b = getBytes(c, offset, length, encoding); + + if (!parserKnowsUnicode && CharsetMapping.requiresEscapeEasternUnicode(encoding)) { + + if (!encoding.equalsIgnoreCase(serverEncoding)) { + b = escapeEasternUnicodeByteStream(b, new String(c, offset, length)); + } + } + } + + return b; + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.0") + encoding + Messages.getString("StringUtils.1"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of the given char[] (re)using a cached charset converter, and the given + * encoding. + */ + public static byte[] getBytes(char[] c, String encoding, String serverEncoding, boolean parserKnowsUnicode, MySQLConnection conn, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + SingleByteCharsetConverter converter = conn != null ? conn.getCharsetConverter(encoding) : SingleByteCharsetConverter.getInstance(encoding, null); + + return getBytes(c, converter, encoding, serverEncoding, parserKnowsUnicode, exceptionInterceptor); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.0") + encoding + Messages.getString("StringUtils.1"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of the given string (re)using the given charset converter, and the given + * encoding. + */ + public static byte[] getBytes(String s, SingleByteCharsetConverter converter, String encoding, String serverEncoding, boolean parserKnowsUnicode, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + byte[] b; + + if (converter != null) { + b = converter.toBytes(s); + } else if (encoding == null) { + b = getBytes(s); + } else { + b = getBytes(s, encoding); + + if (!parserKnowsUnicode && CharsetMapping.requiresEscapeEasternUnicode(encoding)) { + + if (!encoding.equalsIgnoreCase(serverEncoding)) { + b = escapeEasternUnicodeByteStream(b, s); + } + } + } + + return b; + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.5") + encoding + Messages.getString("StringUtils.6"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of a substring of the given string (re)using the given charset converter, and + * the given encoding. + */ + public static byte[] getBytes(String s, SingleByteCharsetConverter converter, String encoding, String serverEncoding, int offset, int length, + boolean parserKnowsUnicode, ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + byte[] b; + + if (converter != null) { + b = converter.toBytes(s, offset, length); + } else if (encoding == null) { + b = getBytes(s, offset, length); + } else { + s = s.substring(offset, offset + length); + b = getBytes(s, encoding); + + if (!parserKnowsUnicode && CharsetMapping.requiresEscapeEasternUnicode(encoding)) { + + if (!encoding.equalsIgnoreCase(serverEncoding)) { + b = escapeEasternUnicodeByteStream(b, s); + } + } + } + + return b; + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.5") + encoding + Messages.getString("StringUtils.6"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of the given string (re)using a cached charset converter, and the given + * encoding. + */ + public static byte[] getBytes(String s, String encoding, String serverEncoding, boolean parserKnowsUnicode, MySQLConnection conn, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + SingleByteCharsetConverter converter = conn != null ? conn.getCharsetConverter(encoding) : SingleByteCharsetConverter.getInstance(encoding, null); + + return getBytes(s, converter, encoding, serverEncoding, parserKnowsUnicode, exceptionInterceptor); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.5") + encoding + Messages.getString("StringUtils.6"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of a substring of the given string (re)using a cached charset converter, and + * the given encoding. + */ + public static final byte[] getBytes(String s, String encoding, String serverEncoding, int offset, int length, boolean parserKnowsUnicode, + MySQLConnection conn, ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + SingleByteCharsetConverter converter = conn != null ? conn.getCharsetConverter(encoding) : SingleByteCharsetConverter.getInstance(encoding, null); + + return getBytes(s, converter, encoding, serverEncoding, offset, length, parserKnowsUnicode, exceptionInterceptor); + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.5") + encoding + Messages.getString("StringUtils.6"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + /** + * Returns the byte[] representation of the given string properly wrapped between the given char delimiters, + * (re)using the given charset converter, and the given encoding. + */ + public static byte[] getBytesWrapped(String s, char beginWrap, char endWrap, SingleByteCharsetConverter converter, String encoding, String serverEncoding, + boolean parserKnowsUnicode, ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + byte[] b; + + if (converter != null) { + b = converter.toBytesWrapped(s, beginWrap, endWrap); + } else if (encoding == null) { + StringBuilder strBuilder = new StringBuilder(s.length() + 2); + strBuilder.append(beginWrap); + strBuilder.append(s); + strBuilder.append(endWrap); + + b = getBytes(strBuilder.toString()); + } else { + StringBuilder strBuilder = new StringBuilder(s.length() + 2); + strBuilder.append(beginWrap); + strBuilder.append(s); + strBuilder.append(endWrap); + + s = strBuilder.toString(); + b = getBytes(s, encoding); + + if (!parserKnowsUnicode && CharsetMapping.requiresEscapeEasternUnicode(encoding)) { + + if (!encoding.equalsIgnoreCase(serverEncoding)) { + b = escapeEasternUnicodeByteStream(b, s); + } + } + } + + return b; + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException(Messages.getString("StringUtils.10") + encoding + Messages.getString("StringUtils.11"), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + } + + public static int getInt(byte[] buf) throws NumberFormatException { + return getInt(buf, 0, buf.length); + } + + public static int getInt(byte[] buf, int offset, int endPos) throws NumberFormatException { + int base = 10; + + int s = offset; + + /* Skip white space. */ + while (s < endPos && Character.isWhitespace((char) buf[s])) { + ++s; + } + + if (s == endPos) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Check for a sign. */ + boolean negative = false; + + if ((char) buf[s] == '-') { + negative = true; + ++s; + } else if ((char) buf[s] == '+') { + ++s; + } + + /* Save the pointer so we can check later if anything happened. */ + int save = s; + + int cutoff = Integer.MAX_VALUE / base; + int cutlim = (Integer.MAX_VALUE % base); + + if (negative) { + cutlim++; + } + + boolean overflow = false; + + int i = 0; + + for (; s < endPos; s++) { + char c = (char) buf[s]; + + if (Character.isDigit(c)) { + c -= '0'; + } else if (Character.isLetter(c)) { + c = (char) (Character.toUpperCase(c) - 'A' + 10); + } else { + break; + } + + if (c >= base) { + break; + } + + /* Check for overflow. */ + if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) { + overflow = true; + } else { + i *= base; + i += c; + } + } + + if (s == save) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + if (overflow) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Return the result of the appropriate sign. */ + return (negative ? (-i) : i); + } + + public static long getLong(byte[] buf) throws NumberFormatException { + return getLong(buf, 0, buf.length); + } + + public static long getLong(byte[] buf, int offset, int endpos) throws NumberFormatException { + int base = 10; + + int s = offset; + + /* Skip white space. */ + while (s < endpos && Character.isWhitespace((char) buf[s])) { + ++s; + } + + if (s == endpos) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Check for a sign. */ + boolean negative = false; + + if ((char) buf[s] == '-') { + negative = true; + ++s; + } else if ((char) buf[s] == '+') { + ++s; + } + + /* Save the pointer so we can check later if anything happened. */ + int save = s; + + long cutoff = Long.MAX_VALUE / base; + long cutlim = (int) (Long.MAX_VALUE % base); + + if (negative) { + cutlim++; + } + + boolean overflow = false; + long i = 0; + + for (; s < endpos; s++) { + char c = (char) buf[s]; + + if (Character.isDigit(c)) { + c -= '0'; + } else if (Character.isLetter(c)) { + c = (char) (Character.toUpperCase(c) - 'A' + 10); + } else { + break; + } + + if (c >= base) { + break; + } + + /* Check for overflow. */ + if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) { + overflow = true; + } else { + i *= base; + i += c; + } + } + + if (s == save) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + if (overflow) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Return the result of the appropriate sign. */ + return (negative ? (-i) : i); + } + + public static short getShort(byte[] buf) throws NumberFormatException { + return getShort(buf, 0, buf.length); + } + + public static short getShort(byte[] buf, int offset, int endpos) throws NumberFormatException { + short base = 10; + + int s = offset; + + /* Skip white space. */ + while (s < endpos && Character.isWhitespace((char) buf[s])) { + ++s; + } + + if (s == endpos) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Check for a sign. */ + boolean negative = false; + + if ((char) buf[s] == '-') { + negative = true; + ++s; + } else if ((char) buf[s] == '+') { + ++s; + } + + /* Save the pointer so we can check later if anything happened. */ + int save = s; + + short cutoff = (short) (Short.MAX_VALUE / base); + short cutlim = (short) (Short.MAX_VALUE % base); + + if (negative) { + cutlim++; + } + + boolean overflow = false; + short i = 0; + + for (; s < endpos; s++) { + char c = (char) buf[s]; + + if (Character.isDigit(c)) { + c -= '0'; + } else if (Character.isLetter(c)) { + c = (char) (Character.toUpperCase(c) - 'A' + 10); + } else { + break; + } + + if (c >= base) { + break; + } + + /* Check for overflow. */ + if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) { + overflow = true; + } else { + i *= base; + i += c; + } + } + + if (s == save) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + if (overflow) { + throw new NumberFormatException(StringUtils.toString(buf)); + } + + /* Return the result of the appropriate sign. */ + return (negative ? (short) -i : (short) i); + } + + /** + * Finds the position of a substring within a string ignoring case. + * + * @param searchIn + * the string to search in + * @param searchFor + * the array of strings to search for + * @return the position where searchFor is found within searchIn starting from startingPosition. + */ + public static int indexOfIgnoreCase(String searchIn, String searchFor) { + return indexOfIgnoreCase(0, searchIn, searchFor); + } + + /** + * Finds the position of a substring within a string ignoring case. + * + * @param startingPosition + * the position to start the search from + * @param searchIn + * the string to search in + * @param searchFor + * the array of strings to search for + * @return the position where searchFor is found within searchIn starting from startingPosition. + */ + public static int indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor) { + if ((searchIn == null) || (searchFor == null)) { + return -1; + } + + int searchInLength = searchIn.length(); + int searchForLength = searchFor.length(); + int stopSearchingAt = searchInLength - searchForLength; + + if (startingPosition > stopSearchingAt || searchForLength == 0) { + return -1; + } + + // Some locales don't follow upper-case rule, so need to check both + char firstCharOfSearchForUc = Character.toUpperCase(searchFor.charAt(0)); + char firstCharOfSearchForLc = Character.toLowerCase(searchFor.charAt(0)); + + for (int i = startingPosition; i <= stopSearchingAt; i++) { + if (isCharAtPosNotEqualIgnoreCase(searchIn, i, firstCharOfSearchForUc, firstCharOfSearchForLc)) { + // find the first occurrence of the first character of searchFor in searchIn + while (++i <= stopSearchingAt && (isCharAtPosNotEqualIgnoreCase(searchIn, i, firstCharOfSearchForUc, firstCharOfSearchForLc))) { + } + } + + if (i <= stopSearchingAt && startsWithIgnoreCase(searchIn, i, searchFor)) { + return i; + } + } + + return -1; + } + + /** + * Finds the position of the first of a consecutive sequence of strings within a string, ignoring case, with the option to skip text delimited by given + * markers or within comments. + *

    + * Independently of the searchMode provided, when searching for the second and following strings SearchMode.SKIP_WHITE_SPACE will + * be added and SearchMode.SKIP_BETWEEN_MARKERS removed. + *

    + * + * @param startingPosition + * the position to start the search from + * @param searchIn + * the string to search in + * @param searchFor + * the array of strings to search for + * @param openingMarkers + * characters which delimit the beginning of a text block to skip + * @param closingMarkers + * characters which delimit the end of a text block to skip + * @param searchMode + * a Set, ideally an EnumSet, containing the flags from the enum StringUtils.SearchMode that determine the + * behavior of the search + * @return the position where searchFor is found within searchIn starting from startingPosition. + */ + public static int indexOfIgnoreCase(int startingPosition, String searchIn, String[] searchForSequence, String openingMarkers, String closingMarkers, + Set searchMode) { + if ((searchIn == null) || (searchForSequence == null)) { + return -1; + } + + int searchInLength = searchIn.length(); + int searchForLength = 0; + for (String searchForPart : searchForSequence) { + searchForLength += searchForPart.length(); + } // minimum length for searchFor (without gaps between words) + + if (searchForLength == 0) { + return -1; + } + + int searchForWordsCount = searchForSequence.length; + searchForLength += searchForWordsCount > 0 ? searchForWordsCount - 1 : 0; // add gaps between words + int stopSearchingAt = searchInLength - searchForLength; + + if (startingPosition > stopSearchingAt) { + return -1; + } + + if (searchMode.contains(SearchMode.SKIP_BETWEEN_MARKERS) + && (openingMarkers == null || closingMarkers == null || openingMarkers.length() != closingMarkers.length())) { + throw new IllegalArgumentException(Messages.getString("StringUtils.15", new String[] { openingMarkers, closingMarkers })); + } + + if (Character.isWhitespace(searchForSequence[0].charAt(0)) && searchMode.contains(SearchMode.SKIP_WHITE_SPACE)) { + // Can't skip white spaces if first searchFor char is one + searchMode = EnumSet.copyOf(searchMode); + searchMode.remove(SearchMode.SKIP_WHITE_SPACE); + } + + // searchMode set used to search 2nd and following words can't contain SearchMode.SKIP_BETWEEN_MARKERS and must + // contain SearchMode.SKIP_WHITE_SPACE + Set searchMode2 = EnumSet.of(SearchMode.SKIP_WHITE_SPACE); + searchMode2.addAll(searchMode); + searchMode2.remove(SearchMode.SKIP_BETWEEN_MARKERS); + + for (int positionOfFirstWord = startingPosition; positionOfFirstWord <= stopSearchingAt; positionOfFirstWord++) { + positionOfFirstWord = indexOfIgnoreCase(positionOfFirstWord, searchIn, searchForSequence[0], openingMarkers, closingMarkers, searchMode); + + if (positionOfFirstWord == -1 || positionOfFirstWord > stopSearchingAt) { + return -1; + } + + int startingPositionForNextWord = positionOfFirstWord + searchForSequence[0].length(); + int wc = 0; + boolean match = true; + while (++wc < searchForWordsCount && match) { + int positionOfNextWord = indexOfNextChar(startingPositionForNextWord, searchInLength - 1, searchIn, null, null, searchMode2); + if (startingPositionForNextWord == positionOfNextWord || !startsWithIgnoreCase(searchIn, positionOfNextWord, searchForSequence[wc])) { + // either no gap between words or match failed + match = false; + } else { + startingPositionForNextWord = positionOfNextWord + searchForSequence[wc].length(); + } + } + + if (match) { + return positionOfFirstWord; + } + } + + return -1; + } + + /** + * Finds the position of a substring within a string, ignoring case, with the option to skip text delimited by given markers or within comments. + * + * @param startingPosition + * the position to start the search from + * @param searchIn + * the string to search in + * @param searchFor + * the string to search for + * @param openingMarkers + * characters which delimit the beginning of a text block to skip + * @param closingMarkers + * characters which delimit the end of a text block to skip + * @param searchMode + * a Set, ideally an EnumSet, containing the flags from the enum StringUtils.SearchMode that determine the + * behavior of the search + * @return the position where searchFor is found within searchIn starting from startingPosition. + */ + public static int indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, + Set searchMode) { + if (searchIn == null || searchFor == null) { + return -1; + } + + int searchInLength = searchIn.length(); + int searchForLength = searchFor.length(); + int stopSearchingAt = searchInLength - searchForLength; + + if (startingPosition > stopSearchingAt || searchForLength == 0) { + return -1; + } + + if (searchMode.contains(SearchMode.SKIP_BETWEEN_MARKERS) + && (openingMarkers == null || closingMarkers == null || openingMarkers.length() != closingMarkers.length())) { + throw new IllegalArgumentException(Messages.getString("StringUtils.15", new String[] { openingMarkers, closingMarkers })); + } + + // Some locales don't follow upper-case rule, so need to check both + char firstCharOfSearchForUc = Character.toUpperCase(searchFor.charAt(0)); + char firstCharOfSearchForLc = Character.toLowerCase(searchFor.charAt(0)); + + if (Character.isWhitespace(firstCharOfSearchForLc) && searchMode.contains(SearchMode.SKIP_WHITE_SPACE)) { + // Can't skip white spaces if first searchFor char is one + searchMode = EnumSet.copyOf(searchMode); + searchMode.remove(SearchMode.SKIP_WHITE_SPACE); + } + + for (int i = startingPosition; i <= stopSearchingAt; i++) { + i = indexOfNextChar(i, stopSearchingAt, searchIn, openingMarkers, closingMarkers, searchMode); + + if (i == -1) { + return -1; + } + + char c = searchIn.charAt(i); + + if (isCharEqualIgnoreCase(c, firstCharOfSearchForUc, firstCharOfSearchForLc) && startsWithIgnoreCase(searchIn, i, searchFor)) { + return i; + } + } + + return -1; + } + + /** + * Finds the position the next character from a string, possibly skipping white space, comments and text between markers. + * + * @param startingPosition + * the position to start the search from + * @param stopPosition + * the position where to stop the search (inclusive) + * @param searchIn + * the string to search in + * @param openingMarkers + * characters which delimit the beginning of a text block to skip + * @param closingMarkers + * characters which delimit the end of a text block to skip + * @param searchMode + * a Set, ideally an EnumSet, containing the flags from the enum StringUtils.SearchMode that determine the + * behavior of the search + * @return the position where searchFor is found within searchIn starting from startingPosition. + */ + private static int indexOfNextChar(int startingPosition, int stopPosition, String searchIn, String openingMarkers, String closingMarkers, + Set searchMode) { + if (searchIn == null) { + return -1; + } + + int searchInLength = searchIn.length(); + + if (startingPosition >= searchInLength) { + return -1; + } + + char c0 = Character.MIN_VALUE; // current char + char c1 = searchIn.charAt(startingPosition); // lookahead(1) + char c2 = startingPosition + 1 < searchInLength ? searchIn.charAt(startingPosition + 1) : Character.MIN_VALUE; // lookahead(2) + + for (int i = startingPosition; i <= stopPosition; i++) { + c0 = c1; + c1 = c2; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + + boolean dashDashCommentImmediateEnd = false; + int markerIndex = -1; + + if (searchMode.contains(SearchMode.ALLOW_BACKSLASH_ESCAPE) && c0 == '\\') { + i++; // next char is escaped, skip it + // reset lookahead + c1 = c2; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + + } else if (searchMode.contains(SearchMode.SKIP_BETWEEN_MARKERS) && (markerIndex = openingMarkers.indexOf(c0)) != -1) { + // marker found, skip until closing, while being aware of nested markers if opening and closing markers are distinct + int nestedMarkersCount = 0; + char openingMarker = c0; + char closingMarker = closingMarkers.charAt(markerIndex); + while (++i <= stopPosition && ((c0 = searchIn.charAt(i)) != closingMarker || nestedMarkersCount != 0)) { + if (c0 == openingMarker) { + nestedMarkersCount++; + } else if (c0 == closingMarker) { + nestedMarkersCount--; + } else if (searchMode.contains(SearchMode.ALLOW_BACKSLASH_ESCAPE) && c0 == '\\') { + i++; // next char is escaped, skip it + } + } + // reset lookahead + c1 = i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + + } else if (searchMode.contains(SearchMode.SKIP_BLOCK_COMMENTS) && c0 == '/' && c1 == '*') { + if (c2 != '!') { + // comments block found, skip until end of block ("*/") (backslash escape doesn't work on comments) + i++; // move to next char ('*') + while (++i <= stopPosition + && (searchIn.charAt(i) != '*' || (i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE) != '/')) { + // continue + } + i++; // move to next char ('/') + + } else { + // special non-comments block found, move to end of opening marker ("/*![12345]") + i++; // move to next char ('*') + i++; // move to next char ('!') + // check if a 5 digits MySQL version reference follows, if so skip them + int j = 1; + for (; j <= NON_COMMENTS_MYSQL_VERSION_REF_LENGTH; j++) { + if (i + j >= searchInLength || !Character.isDigit(searchIn.charAt(i + j))) { + break; + } + } + if (j == NON_COMMENTS_MYSQL_VERSION_REF_LENGTH) { + i += NON_COMMENTS_MYSQL_VERSION_REF_LENGTH; + } + } + // reset lookahead + c1 = i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + + } else if (searchMode.contains(SearchMode.SKIP_BLOCK_COMMENTS) && c0 == '*' && c1 == '/') { + // special non-comments block closing marker ("*/") found - assume that if we get it here it's because it + // belongs to a non-comments block ("/*!"), otherwise the query should be misspelled as nesting comments isn't allowed. + i++; // move to next char ('/') + // reset lookahead + c1 = c2; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + + } else if (searchMode.contains(SearchMode.SKIP_LINE_COMMENTS) + && ((c0 == '-' && c1 == '-' && (Character.isWhitespace(c2) || (dashDashCommentImmediateEnd = c2 == ';') || c2 == Character.MIN_VALUE)) + || c0 == '#')) { + if (dashDashCommentImmediateEnd) { + // comments line found but closed immediately by query delimiter marker + i++; // move to next char ('-') + i++; // move to next char (';') + // reset lookahead + c1 = i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE; + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + } else { + // comments line found, skip until eol (backslash escape doesn't work on comments) + while (++i <= stopPosition && (c0 = searchIn.charAt(i)) != '\n' && c0 != '\r') { + // continue + } + // reset lookahead + c1 = i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE; + if (c0 == '\r' && c1 == '\n') { + // \r\n sequence found + i++; // skip next char ('\n') + c1 = i + 1 < searchInLength ? searchIn.charAt(i + 1) : Character.MIN_VALUE; + } + c2 = i + 2 < searchInLength ? searchIn.charAt(i + 2) : Character.MIN_VALUE; + } + + } else if (!searchMode.contains(SearchMode.SKIP_WHITE_SPACE) || !Character.isWhitespace(c0)) { + return i; + } + } + + return -1; + } + + private static boolean isCharAtPosNotEqualIgnoreCase(String searchIn, int pos, char firstCharOfSearchForUc, char firstCharOfSearchForLc) { + return Character.toLowerCase(searchIn.charAt(pos)) != firstCharOfSearchForLc && Character.toUpperCase(searchIn.charAt(pos)) != firstCharOfSearchForUc; + } + + private static boolean isCharEqualIgnoreCase(char charToCompare, char compareToCharUC, char compareToCharLC) { + return Character.toLowerCase(charToCompare) == compareToCharLC || Character.toUpperCase(charToCompare) == compareToCharUC; + } + + /** + * Splits stringToSplit into a list, using the given delimiter + * + * @param stringToSplit + * the string to split + * @param delimiter + * the string to split on + * @param trim + * should the split strings be whitespace trimmed? + * + * @return the list of strings, split by delimiter + * + * @throws IllegalArgumentException + */ + public static List split(String stringToSplit, String delimiter, boolean trim) { + if (stringToSplit == null) { + return new ArrayList(); + } + + if (delimiter == null) { + throw new IllegalArgumentException(); + } + + StringTokenizer tokenizer = new StringTokenizer(stringToSplit, delimiter, false); + + List splitTokens = new ArrayList(tokenizer.countTokens()); + + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + + if (trim) { + token = token.trim(); + } + + splitTokens.add(token); + } + + return splitTokens; + } + + /** + * Splits stringToSplit into a list, using the given delimiter + * + * @param stringToSplit + * the string to split + * @param delimiter + * the string to split on + * @param trim + * should the split strings be whitespace trimmed? + * + * @return the list of strings, split by delimiter + * + * @throws IllegalArgumentException + */ + public static List split(String stringToSplit, String delimiter, String markers, String markerCloses, boolean trim) { + if (stringToSplit == null) { + return new ArrayList(); + } + + if (delimiter == null) { + throw new IllegalArgumentException(); + } + + int delimPos = 0; + int currentPos = 0; + + List splitTokens = new ArrayList(); + + while ((delimPos = indexOfIgnoreCase(currentPos, stringToSplit, delimiter, markers, markerCloses, SEARCH_MODE__MRK_COM_WS)) != -1) { + String token = stringToSplit.substring(currentPos, delimPos); + + if (trim) { + token = token.trim(); + } + + splitTokens.add(token); + currentPos = delimPos + 1; + } + + if (currentPos < stringToSplit.length()) { + String token = stringToSplit.substring(currentPos); + + if (trim) { + token = token.trim(); + } + + splitTokens.add(token); + } + + return splitTokens; + } + + private static boolean startsWith(byte[] dataFrom, String chars) { + int charsLength = chars.length(); + + if (dataFrom.length < charsLength) { + return false; + } + for (int i = 0; i < charsLength; i++) { + if (dataFrom[i] != chars.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Determines whether or not the string 'searchIn' contains the string + * 'searchFor', dis-regarding case starting at 'startAt' Shorthand for a + * String.regionMatch(...) + * + * @param searchIn + * the string to search in + * @param startAt + * the position to start at + * @param searchFor + * the string to search for + * + * @return whether searchIn starts with searchFor, ignoring case + */ + public static boolean startsWithIgnoreCase(String searchIn, int startAt, String searchFor) { + return searchIn.regionMatches(true, startAt, searchFor, 0, searchFor.length()); + } + + /** + * Determines whether or not the string 'searchIn' contains the string + * 'searchFor', dis-regarding case. Shorthand for a String.regionMatch(...) + * + * @param searchIn + * the string to search in + * @param searchFor + * the string to search for + * + * @return whether searchIn starts with searchFor, ignoring case + */ + public static boolean startsWithIgnoreCase(String searchIn, String searchFor) { + return startsWithIgnoreCase(searchIn, 0, searchFor); + } + + /** + * Determines whether or not the string 'searchIn' contains the string + * 'searchFor', disregarding case,leading whitespace and non-alphanumeric + * characters. + * + * @param searchIn + * the string to search in + * @param searchFor + * the string to search for + * + * @return true if the string starts with 'searchFor' ignoring whitespace + */ + public static boolean startsWithIgnoreCaseAndNonAlphaNumeric(String searchIn, String searchFor) { + if (searchIn == null) { + return searchFor == null; + } + + int beginPos = 0; + int inLength = searchIn.length(); + + for (; beginPos < inLength; beginPos++) { + char c = searchIn.charAt(beginPos); + if (Character.isLetterOrDigit(c)) { + break; + } + } + + return startsWithIgnoreCase(searchIn, beginPos, searchFor); + } + + /** + * Determines whether or not the string 'searchIn' contains the string + * 'searchFor', disregarding case and leading whitespace + * + * @param searchIn + * the string to search in + * @param searchFor + * the string to search for + * + * @return true if the string starts with 'searchFor' ignoring whitespace + */ + public static boolean startsWithIgnoreCaseAndWs(String searchIn, String searchFor) { + return startsWithIgnoreCaseAndWs(searchIn, searchFor, 0); + } + + /** + * Determines whether or not the string 'searchIn' contains the string + * 'searchFor', disregarding case and leading whitespace + * + * @param searchIn + * the string to search in + * @param searchFor + * the string to search for + * @param beginPos + * where to start searching + * + * @return true if the string starts with 'searchFor' ignoring whitespace + */ + + public static boolean startsWithIgnoreCaseAndWs(String searchIn, String searchFor, int beginPos) { + if (searchIn == null) { + return searchFor == null; + } + + int inLength = searchIn.length(); + + for (; beginPos < inLength; beginPos++) { + if (!Character.isWhitespace(searchIn.charAt(beginPos))) { + break; + } + } + + return startsWithIgnoreCase(searchIn, beginPos, searchFor); + } + + /** + * Determines whether or not the string 'searchIn' starts with one of the strings in 'searchFor', disregarding case + * and leading whitespace + * + * @param searchIn + * the string to search in + * @param searchFor + * the string array to search for + * + * @return the 'searchFor' array index that matched or -1 if none matches + */ + public static int startsWithIgnoreCaseAndWs(String searchIn, String[] searchFor) { + for (int i = 0; i < searchFor.length; i++) { + if (startsWithIgnoreCaseAndWs(searchIn, searchFor[i], 0)) { + return i; + } + } + return -1; + } + + /** + * @param bytesToStrip + * @param prefix + * @param suffix + */ + public static byte[] stripEnclosure(byte[] source, String prefix, String suffix) { + if (source.length >= prefix.length() + suffix.length() && startsWith(source, prefix) && endsWith(source, suffix)) { + + int totalToStrip = prefix.length() + suffix.length(); + int enclosedLength = source.length - totalToStrip; + byte[] enclosed = new byte[enclosedLength]; + + int startPos = prefix.length(); + int numToCopy = enclosed.length; + System.arraycopy(source, startPos, enclosed, 0, numToCopy); + + return enclosed; + } + return source; + } + + /** + * Returns the bytes as an ASCII String. + * + * @param buffer + * the bytes representing the string + * + * @return The ASCII String. + */ + public static String toAsciiString(byte[] buffer) { + return toAsciiString(buffer, 0, buffer.length); + } + + /** + * Returns the bytes as an ASCII String. + * + * @param buffer + * the bytes to convert + * @param startPos + * the position to start converting + * @param length + * the length of the string to convert + * + * @return the ASCII string + */ + public static String toAsciiString(byte[] buffer, int startPos, int length) { + char[] charArray = new char[length]; + int readpoint = startPos; + + for (int i = 0; i < length; i++) { + charArray[i] = (char) buffer[readpoint]; + readpoint++; + } + + return new String(charArray); + } + + /** + * Compares searchIn against searchForWildcard with wildcards (heavily + * borrowed from strings/ctype-simple.c in the server sources) + * + * @param searchIn + * the string to search in + * @param searchForWildcard + * the string to search for, using the 'standard' SQL wildcard + * chars of '%' and '_' + * + * @return WILD_COMPARE_MATCH_NO_WILD if matched, WILD_COMPARE_NO_MATCH if + * not matched with wildcard, WILD_COMPARE_MATCH_WITH_WILD if + * matched with wildcard + */ + public static int wildCompare(String searchIn, String searchForWildcard) { + if ((searchIn == null) || (searchForWildcard == null)) { + return WILD_COMPARE_NO_MATCH; + } + + if (searchForWildcard.equals("%")) { + + return WILD_COMPARE_MATCH_WITH_WILD; + } + + int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */ + + char wildcardMany = '%'; + char wildcardOne = '_'; + char wildcardEscape = '\\'; + + int searchForPos = 0; + int searchForEnd = searchForWildcard.length(); + + int searchInPos = 0; + int searchInEnd = searchIn.length(); + + while (searchForPos != searchForEnd) { + char wildstrChar = searchForWildcard.charAt(searchForPos); + + while ((searchForWildcard.charAt(searchForPos) != wildcardMany) && (wildstrChar != wildcardOne)) { + if ((searchForWildcard.charAt(searchForPos) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) { + searchForPos++; + } + + if ((searchInPos == searchInEnd) + || (Character.toUpperCase(searchForWildcard.charAt(searchForPos++)) != Character.toUpperCase(searchIn.charAt(searchInPos++)))) { + return WILD_COMPARE_MATCH_WITH_WILD; /* No match */ + } + + if (searchForPos == searchForEnd) { + return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD); /* Match if both are at end */ + } + + result = WILD_COMPARE_MATCH_WITH_WILD; /* Found an anchor char */ + } + + if (searchForWildcard.charAt(searchForPos) == wildcardOne) { + do { + if (searchInPos == searchInEnd) { /* Skip one char if possible */ + + return (result); + } + + searchInPos++; + } while ((++searchForPos < searchForEnd) && (searchForWildcard.charAt(searchForPos) == wildcardOne)); + + if (searchForPos == searchForEnd) { + break; + } + } + + if (searchForWildcard.charAt(searchForPos) == wildcardMany) { /* Found w_many */ + + char cmp; + + searchForPos++; + + /* Remove any '%' and '_' from the wild search string */ + for (; searchForPos != searchForEnd; searchForPos++) { + if (searchForWildcard.charAt(searchForPos) == wildcardMany) { + continue; + } + + if (searchForWildcard.charAt(searchForPos) == wildcardOne) { + if (searchInPos == searchInEnd) { + return (WILD_COMPARE_NO_MATCH); + } + + searchInPos++; + + continue; + } + + break; /* Not a wild character */ + } + + if (searchForPos == searchForEnd) { + return WILD_COMPARE_MATCH_NO_WILD; /* Ok if w_many is last */ + } + + if (searchInPos == searchInEnd) { + return WILD_COMPARE_NO_MATCH; + } + + if (((cmp = searchForWildcard.charAt(searchForPos)) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) { + cmp = searchForWildcard.charAt(++searchForPos); + } + + searchForPos++; + + do { + while ((searchInPos != searchInEnd) && (Character.toUpperCase(searchIn.charAt(searchInPos)) != Character.toUpperCase(cmp))) { + searchInPos++; + } + + if (searchInPos++ == searchInEnd) { + return WILD_COMPARE_NO_MATCH; + } + + { + int tmp = wildCompare(searchIn, searchForWildcard); + + if (tmp <= 0) { + return (tmp); + } + } + } while ((searchInPos != searchInEnd) && (searchForWildcard.charAt(0) != wildcardMany)); + + return WILD_COMPARE_NO_MATCH; + } + } + + return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD); + } + + static byte[] s2b(String s, MySQLConnection conn) throws SQLException { + if (s == null) { + return null; + } + + if ((conn != null) && conn.getUseUnicode()) { + try { + String encoding = conn.getEncoding(); + + if (encoding == null) { + return s.getBytes(); + } + + SingleByteCharsetConverter converter = conn.getCharsetConverter(encoding); + + if (converter != null) { + return converter.toBytes(s); + } + + return s.getBytes(encoding); + } catch (java.io.UnsupportedEncodingException E) { + return s.getBytes(); + } + } + + return s.getBytes(); + } + + public static int lastIndexOf(byte[] s, char c) { + if (s == null) { + return -1; + } + + for (int i = s.length - 1; i >= 0; i--) { + if (s[i] == c) { + return i; + } + } + + return -1; + } + + public static int indexOf(byte[] s, char c) { + if (s == null) { + return -1; + } + + int length = s.length; + + for (int i = 0; i < length; i++) { + if (s[i] == c) { + return i; + } + } + + return -1; + } + + public static boolean isNullOrEmpty(String toTest) { + return (toTest == null || toTest.length() == 0); + } + + /** + * Returns the given string, with comments removed + * + * @param src + * the source string + * @param stringOpens + * characters which delimit the "open" of a string + * @param stringCloses + * characters which delimit the "close" of a string, in + * counterpart order to stringOpens + * @param slashStarComments + * strip slash-star type "C" style comments + * @param slashSlashComments + * strip slash-slash C++ style comments to end-of-line + * @param hashComments + * strip #-style comments to end-of-line + * @param dashDashComments + * strip "--" style comments to end-of-line + * @return the input string with all comment-delimited data removed + */ + public static String stripComments(String src, String stringOpens, String stringCloses, boolean slashStarComments, boolean slashSlashComments, + boolean hashComments, boolean dashDashComments) { + if (src == null) { + return null; + } + + StringBuilder strBuilder = new StringBuilder(src.length()); + + // It's just more natural to deal with this as a stream when parsing..This code is currently only called when parsing the kind of metadata that + // developers are strongly recommended to cache anyways, so we're not worried about the _1_ extra object allocation if it cleans up the code + + StringReader sourceReader = new StringReader(src); + + int contextMarker = Character.MIN_VALUE; + boolean escaped = false; + int markerTypeFound = -1; + + int ind = 0; + + int currentChar = 0; + + try { + while ((currentChar = sourceReader.read()) != -1) { + + if (markerTypeFound != -1 && currentChar == stringCloses.charAt(markerTypeFound) && !escaped) { + contextMarker = Character.MIN_VALUE; + markerTypeFound = -1; + } else if ((ind = stringOpens.indexOf(currentChar)) != -1 && !escaped && contextMarker == Character.MIN_VALUE) { + markerTypeFound = ind; + contextMarker = currentChar; + } + + if (contextMarker == Character.MIN_VALUE && currentChar == '/' && (slashSlashComments || slashStarComments)) { + currentChar = sourceReader.read(); + if (currentChar == '*' && slashStarComments) { + int prevChar = 0; + while ((currentChar = sourceReader.read()) != '/' || prevChar != '*') { + if (currentChar == '\r') { + + currentChar = sourceReader.read(); + if (currentChar == '\n') { + currentChar = sourceReader.read(); + } + } else { + if (currentChar == '\n') { + + currentChar = sourceReader.read(); + } + } + if (currentChar < 0) { + break; + } + prevChar = currentChar; + } + continue; + } else if (currentChar == '/' && slashSlashComments) { + while ((currentChar = sourceReader.read()) != '\n' && currentChar != '\r' && currentChar >= 0) { + } + } + } else if (contextMarker == Character.MIN_VALUE && currentChar == '#' && hashComments) { + // Slurp up everything until the newline + while ((currentChar = sourceReader.read()) != '\n' && currentChar != '\r' && currentChar >= 0) { + } + } else if (contextMarker == Character.MIN_VALUE && currentChar == '-' && dashDashComments) { + currentChar = sourceReader.read(); + + if (currentChar == -1 || currentChar != '-') { + strBuilder.append('-'); + + if (currentChar != -1) { + strBuilder.append(currentChar); + } + + continue; + } + + // Slurp up everything until the newline + + while ((currentChar = sourceReader.read()) != '\n' && currentChar != '\r' && currentChar >= 0) { + } + } + + if (currentChar != -1) { + strBuilder.append((char) currentChar); + } + } + } catch (IOException ioEx) { + // we'll never see this from a StringReader + } + + return strBuilder.toString(); + } + + /** + * Next two functions are to help DBMD check if + * the given string is in form of database.name and return it + * as "database";"name" with comments removed. + * If string is NULL or wildcard (%), returns null and exits. + * + * First, we sanitize... + * + * @param src + * the source string + * @return the input string with all comment-delimited data removed + */ + public static String sanitizeProcOrFuncName(String src) { + if ((src == null) || (src.equals("%"))) { + return null; + } + + return src; + } + + /** + * Next we check if there is anything to split. If so + * we return result in form of "database";"name" + * If string is NULL or wildcard (%), returns null and exits. + * + * @param src + * the source string + * @param cat + * Catalog, if available + * @param quotId + * quoteId as defined on server + * @param isNoBslashEscSet + * Is our connection in BackSlashEscape mode + * @return the input string with all comment-delimited data removed + */ + public static List splitDBdotName(String src, String cat, String quotId, boolean isNoBslashEscSet) { + if ((src == null) || (src.equals("%"))) { + return new ArrayList(); + } + + boolean isQuoted = StringUtils.indexOfIgnoreCase(0, src, quotId) > -1; + + String retval = src; + String tmpCat = cat; + //I.e., what if database is named `MyDatabase 1.0.0`... thus trueDotIndex + int trueDotIndex = -1; + if (!" ".equals(quotId)) { + //Presumably, if there is a database name attached and it contains dots, then it should be quoted so we first check for that + if (isQuoted) { + trueDotIndex = StringUtils.indexOfIgnoreCase(0, retval, quotId + "." + quotId); + } else { + // NOT quoted, fetch first DOT + // ex: cStmt = this.conn.prepareCall("{call bug57022.procbug57022(?, ?)}"); + trueDotIndex = StringUtils.indexOfIgnoreCase(0, retval, "."); + } + } else { + trueDotIndex = retval.indexOf("."); + } + + List retTokens = new ArrayList(2); + + if (trueDotIndex != -1) { + //There is a catalog attached + if (isQuoted) { + tmpCat = StringUtils.toString(StringUtils.stripEnclosure(retval.substring(0, trueDotIndex + 1).getBytes(), quotId, quotId)); + if (StringUtils.startsWithIgnoreCaseAndWs(tmpCat, quotId)) { + tmpCat = tmpCat.substring(1, tmpCat.length() - 1); + } + + retval = retval.substring(trueDotIndex + 2); + retval = StringUtils.toString(StringUtils.stripEnclosure(retval.getBytes(), quotId, quotId)); + } else { + //NOT quoted, adjust indexOf + tmpCat = retval.substring(0, trueDotIndex); + retval = retval.substring(trueDotIndex + 1); + } + } else { + //No catalog attached, strip retval and return + retval = StringUtils.toString(StringUtils.stripEnclosure(retval.getBytes(), quotId, quotId)); + } + + retTokens.add(tmpCat); + retTokens.add(retval); + return retTokens; + } + + public static boolean isEmptyOrWhitespaceOnly(String str) { + if (str == null || str.length() == 0) { + return true; + } + + int length = str.length(); + + for (int i = 0; i < length; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + + return true; + } + + public static String escapeQuote(String src, String quotChar) { + if (src == null) { + return null; + } + + src = StringUtils.toString(stripEnclosure(src.getBytes(), quotChar, quotChar)); + + int lastNdx = src.indexOf(quotChar); + String tmpSrc; + String tmpRest; + + tmpSrc = src.substring(0, lastNdx); + tmpSrc = tmpSrc + quotChar + quotChar; + + tmpRest = src.substring(lastNdx + 1, src.length()); + + lastNdx = tmpRest.indexOf(quotChar); + while (lastNdx > -1) { + + tmpSrc = tmpSrc + tmpRest.substring(0, lastNdx); + tmpSrc = tmpSrc + quotChar + quotChar; + tmpRest = tmpRest.substring(lastNdx + 1, tmpRest.length()); + + lastNdx = tmpRest.indexOf(quotChar); + } + + tmpSrc = tmpSrc + tmpRest; + src = tmpSrc; + + return src; + } + + /** + * Surrounds identifier with quoteChar and duplicates these symbols inside the identifier. + * + * @param quoteChar + * ` or " + * @param identifier + * in pedantic mode (connection property pedantic=true) identifier is treated as unquoted + * (as it is stored in the database) even if it starts and ends with quoteChar; + * in non-pedantic mode if identifier starts and ends with quoteChar method treats it as already quoted and doesn't modify. + * @param isPedantic + * are we in pedantic mode + * + * @return + * With quoteChar="`":
    + *
      + *
    • null -> null
    • + *
    • abc -> `abc`
    • + *
    • ab`c -> `ab``c`
    • + *
    • ab"c -> `ab"c`
    • + *
    • `ab``c` -> `ab``c` in non-pedantic mode or ```ab````c``` in pedantic mode
    • + *
    + * With quoteChar="\"":
    + *
      + *
    • null -> null
    • + *
    • abc -> "abc"
    • + *
    • ab`c -> "ab`c"
    • + *
    • ab"c -> "ab""c"
    • + *
    • "ab""c" -> "ab""c" in non-pedantic mode or """ab""""c""" in pedantic mode
    • + *
    + */ + public static String quoteIdentifier(String identifier, String quoteChar, boolean isPedantic) { + if (identifier == null) { + return null; + } + + identifier = identifier.trim(); + + int quoteCharLength = quoteChar.length(); + if (quoteCharLength == 0 || " ".equals(quoteChar)) { + return identifier; + } + + // Check if the identifier is correctly quoted and if quotes within are correctly escaped. If not, quote and escape it. + if (!isPedantic && identifier.startsWith(quoteChar) && identifier.endsWith(quoteChar)) { + // Trim outermost quotes from the identifier. + String identifierQuoteTrimmed = identifier.substring(quoteCharLength, identifier.length() - quoteCharLength); + + // Check for pairs of quotes. + int quoteCharPos = identifierQuoteTrimmed.indexOf(quoteChar); + while (quoteCharPos >= 0) { + int quoteCharNextExpectedPos = quoteCharPos + quoteCharLength; + int quoteCharNextPosition = identifierQuoteTrimmed.indexOf(quoteChar, quoteCharNextExpectedPos); + + if (quoteCharNextPosition == quoteCharNextExpectedPos) { + quoteCharPos = identifierQuoteTrimmed.indexOf(quoteChar, quoteCharNextPosition + quoteCharLength); + } else { + // Not a pair of quotes! + break; + } + } + + if (quoteCharPos < 0) { + return identifier; + } + } + + return quoteChar + identifier.replaceAll(quoteChar, quoteChar + quoteChar) + quoteChar; + } + + /** + * Surrounds identifier with "`" and duplicates these symbols inside the identifier. + * + * @param identifier + * in pedantic mode (connection property pedantic=true) identifier is treated as unquoted + * (as it is stored in the database) even if it starts and ends with "`"; + * in non-pedantic mode if identifier starts and ends with "`" method treats it as already quoted and doesn't modify. + * @param isPedantic + * are we in pedantic mode + * + * @return + *
      + *
    • null -> null
    • + *
    • abc -> `abc`
    • + *
    • ab`c -> `ab``c`
    • + *
    • ab"c -> `ab"c`
    • + *
    • `ab``c` -> `ab``c` in non-pedantic mode or ```ab````c``` in pedantic mode
    • + *
    + */ + public static String quoteIdentifier(String identifier, boolean isPedantic) { + return quoteIdentifier(identifier, "`", isPedantic); + } + + /** + * Trims identifier, removes quote chars from first and last positions + * and replaces double occurrences of quote char from entire identifier, + * i.e converts quoted identifier into form as it is stored in database. + * + * @param identifier + * @param quoteChar + * ` or " + * @return + *
      + *
    • null -> null
    • + *
    • abc -> abc
    • + *
    • `abc` -> abc
    • + *
    • `ab``c` -> ab`c
    • + *
    • `"ab`c"` -> "ab`c"
    • + *
    • `ab"c` -> ab"c
    • + *
    • "abc" -> abc
    • + *
    • "`ab""c`" -> `ab"c`
    • + *
    • "ab`c" -> ab`c
    • + *
    + */ + public static String unQuoteIdentifier(String identifier, String quoteChar) { + if (identifier == null) { + return null; + } + + identifier = identifier.trim(); + + int quoteCharLength = quoteChar.length(); + if (quoteCharLength == 0 || " ".equals(quoteChar)) { + return identifier; + } + + // Check if the identifier is really quoted or if it simply contains quote chars in it (assuming that the value is a valid identifier). + if (identifier.startsWith(quoteChar) && identifier.endsWith(quoteChar)) { + // Trim outermost quotes from the identifier. + String identifierQuoteTrimmed = identifier.substring(quoteCharLength, identifier.length() - quoteCharLength); + + // Check for pairs of quotes. + int quoteCharPos = identifierQuoteTrimmed.indexOf(quoteChar); + while (quoteCharPos >= 0) { + int quoteCharNextExpectedPos = quoteCharPos + quoteCharLength; + int quoteCharNextPosition = identifierQuoteTrimmed.indexOf(quoteChar, quoteCharNextExpectedPos); + + if (quoteCharNextPosition == quoteCharNextExpectedPos) { + quoteCharPos = identifierQuoteTrimmed.indexOf(quoteChar, quoteCharNextPosition + quoteCharLength); + } else { + // Not a pair of quotes! Return as it is... + return identifier; + } + } + + return identifier.substring(quoteCharLength, (identifier.length() - quoteCharLength)).replaceAll(quoteChar + quoteChar, quoteChar); + } + + return identifier; + } + + public static int indexOfQuoteDoubleAware(String searchIn, String quoteChar, int startFrom) { + if (searchIn == null || quoteChar == null || quoteChar.length() == 0 || startFrom > searchIn.length()) { + return -1; + } + + int lastIndex = searchIn.length() - 1; + + int beginPos = startFrom; + int pos = -1; + + boolean next = true; + while (next) { + pos = searchIn.indexOf(quoteChar, beginPos); + if (pos == -1 || pos == lastIndex || !searchIn.startsWith(quoteChar, pos + 1)) { + next = false; + } else { + beginPos = pos + 2; + } + } + + return pos; + } + + // The following methods all exist because of the Java bug + // + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6790402 + // + // which has been observed by users and reported as MySQL Bug#61105 + // + // We can turn around and replace them with their java.lang.String equivalents if/when that bug is ever fixed. + + public static String toString(byte[] value, int offset, int length, String encoding) throws UnsupportedEncodingException { + Charset cs = findCharset(encoding); + + return cs.decode(ByteBuffer.wrap(value, offset, length)).toString(); + } + + public static String toString(byte[] value, String encoding) throws UnsupportedEncodingException { + Charset cs = findCharset(encoding); + + return cs.decode(ByteBuffer.wrap(value)).toString(); + } + + public static String toString(byte[] value, int offset, int length) { + try { + Charset cs = findCharset(platformEncoding); + + return cs.decode(ByteBuffer.wrap(value, offset, length)).toString(); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static String toString(byte[] value) { + try { + Charset cs = findCharset(platformEncoding); + + return cs.decode(ByteBuffer.wrap(value)).toString(); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static byte[] getBytes(char[] value) { + try { + return getBytes(value, 0, value.length, platformEncoding); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static byte[] getBytes(char[] value, int offset, int length) { + try { + return getBytes(value, offset, length, platformEncoding); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static byte[] getBytes(char[] value, String encoding) throws UnsupportedEncodingException { + return getBytes(value, 0, value.length, encoding); + } + + public static byte[] getBytes(char[] value, int offset, int length, String encoding) throws UnsupportedEncodingException { + Charset cs = findCharset(encoding); + + ByteBuffer buf = cs.encode(CharBuffer.wrap(value, offset, length)); + + // can't simply .array() this to get the bytes especially with variable-length charsets the buffer is sometimes larger than the actual encoded data + int encodedLen = buf.limit(); + byte[] asBytes = new byte[encodedLen]; + buf.get(asBytes, 0, encodedLen); + + return asBytes; + } + + public static byte[] getBytes(String value) { + try { + return getBytes(value, 0, value.length(), platformEncoding); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static byte[] getBytes(String value, int offset, int length) { + try { + return getBytes(value, offset, length, platformEncoding); + } catch (UnsupportedEncodingException e) { + // can't happen, emulating new String(byte[]) + } + + return null; + } + + public static byte[] getBytes(String value, String encoding) throws UnsupportedEncodingException { + return getBytes(value, 0, value.length(), encoding); + } + + public static byte[] getBytes(String value, int offset, int length, String encoding) throws UnsupportedEncodingException { + // Some CharsetEncoders (e.g. CP942, CP943, CP948, CP950, CP1381, CP1383, x-COMPOUND_TEXT or ISO-2022-JP) can't + // handle correctly when encoding directly from its methods while calling the encoder from String object works + // just fine. Most of these problems occur only in Java 1.5. + // CharsetEncoder#encode() may be used in Java 1.6+ but only the method that receives a char[] as argument as + // the one that receives a String argument doesn't always behaves correctly. + if (!Util.isJdbc4()) { + if (offset != 0 || length != value.length()) { + return value.substring(offset, offset + length).getBytes(encoding); + } + return value.getBytes(encoding); + } + + Charset cs = findCharset(encoding); + + ByteBuffer buf = cs.encode(CharBuffer.wrap(value.toCharArray(), offset, length)); + + // can't simply .array() this to get the bytes especially with variable-length charsets the buffer is sometimes larger than the actual encoded data + int encodedLen = buf.limit(); + byte[] asBytes = new byte[encodedLen]; + buf.get(asBytes, 0, encodedLen); + + return asBytes; + } + + public static final boolean isValidIdChar(char c) { + return VALID_ID_CHARS.indexOf(c) != -1; + } + + private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + public static void appendAsHex(StringBuilder builder, byte[] bytes) { + builder.append("0x"); + for (byte b : bytes) { + builder.append(HEX_DIGITS[(b >>> 4) & 0xF]).append(HEX_DIGITS[b & 0xF]); + } + } + + public static void appendAsHex(StringBuilder builder, int value) { + if (value == 0) { + builder.append("0x0"); + return; + } + + int shift = 32; + byte nibble; + boolean nonZeroFound = false; + + builder.append("0x"); + do { + shift -= 4; + nibble = (byte) ((value >>> shift) & 0xF); + if (nonZeroFound) { + builder.append(HEX_DIGITS[nibble]); + } else if (nibble != 0) { + builder.append(HEX_DIGITS[nibble]); + nonZeroFound = true; + } + } while (shift != 0); + } + + public static byte[] getBytesNullTerminated(String value, String encoding) throws UnsupportedEncodingException { + Charset cs = findCharset(encoding); + + ByteBuffer buf = cs.encode(value); + + int encodedLen = buf.limit(); + byte[] asBytes = new byte[encodedLen + 1]; + buf.get(asBytes, 0, encodedLen); + asBytes[encodedLen] = 0; + + return asBytes; + } + + /** + * Checks is the CharSequence contains digits only. No leading sign and thousands or decimal separators are allowed. + * + * @param cs + * The CharSequence to check. + * @return + * {@code true} if the CharSequence not empty and contains only digits, {@code false} otherwise. + */ + public static boolean isStrictlyNumeric(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + for (int i = 0; i < cs.length(); i++) { + if (!Character.isDigit(cs.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeUtil.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeUtil.java new file mode 100644 index 0000000..154b1d1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeUtil.java @@ -0,0 +1,544 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Properties; +import java.util.TimeZone; + +/** + * Timezone conversion routines and other time related methods + */ +public class TimeUtil { + static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT"); + + // cache this ourselves, as the method call is statically-synchronized in all but JDK6! + private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); + + // Mappings from TimeZone identifications (prefixed by type: Windows, TZ name, MetaZone, TZ alias, ...), to standard TimeZone Ids + private static final String TIME_ZONE_MAPPINGS_RESOURCE = "/com/mysql/jdbc/TimeZoneMapping.properties"; + + private static Properties timeZoneMappings = null; + + protected final static Method systemNanoTimeMethod; + + static { + Method aMethod; + + try { + aMethod = System.class.getMethod("nanoTime", (Class[]) null); + } catch (SecurityException e) { + aMethod = null; + } catch (NoSuchMethodException e) { + aMethod = null; + } + + systemNanoTimeMethod = aMethod; + } + + public static boolean nanoTimeAvailable() { + return systemNanoTimeMethod != null; + } + + public static final TimeZone getDefaultTimeZone(boolean useCache) { + return (TimeZone) (useCache ? DEFAULT_TIMEZONE.clone() : TimeZone.getDefault().clone()); + } + + public static long getCurrentTimeNanosOrMillis() { + if (systemNanoTimeMethod != null) { + try { + return ((Long) systemNanoTimeMethod.invoke(null, (Object[]) null)).longValue(); + } catch (IllegalArgumentException e) { + // ignore - fall through to currentTimeMillis() + } catch (IllegalAccessException e) { + // ignore - fall through to currentTimeMillis() + } catch (InvocationTargetException e) { + // ignore - fall through to currentTimeMillis() + } + } + + return System.currentTimeMillis(); + } + + /** + * Change the given times from one timezone to another + * + * @param conn + * the current connection to the MySQL server + * @param t + * the times to change + * @param fromTz + * the timezone to change from + * @param toTz + * the timezone to change to + * + * @return the times changed to the timezone 'toTz' + */ + public static Time changeTimezone(MySQLConnection conn, Calendar sessionCalendar, Calendar targetCalendar, Time t, TimeZone fromTz, TimeZone toTz, + boolean rollForward) { + if ((conn != null)) { + if (conn.getUseTimezone() && !conn.getNoTimezoneConversionForTimeType()) { + // Convert the timestamp from GMT to the server's timezone + Calendar fromCal = Calendar.getInstance(fromTz); + fromCal.setTime(t); + + int fromOffset = fromCal.get(Calendar.ZONE_OFFSET) + fromCal.get(Calendar.DST_OFFSET); + Calendar toCal = Calendar.getInstance(toTz); + toCal.setTime(t); + + int toOffset = toCal.get(Calendar.ZONE_OFFSET) + toCal.get(Calendar.DST_OFFSET); + int offsetDiff = fromOffset - toOffset; + long toTime = toCal.getTime().getTime(); + + if (rollForward) { + toTime += offsetDiff; + } else { + toTime -= offsetDiff; + } + + Time changedTime = new Time(toTime); + + return changedTime; + } else if (conn.getUseJDBCCompliantTimezoneShift()) { + if (targetCalendar != null) { + + Time adjustedTime = new Time(jdbcCompliantZoneShift(sessionCalendar, targetCalendar, t)); + + return adjustedTime; + } + } + } + + return t; + } + + /** + * Change the given timestamp from one timezone to another + * + * @param conn + * the current connection to the MySQL server + * @param tstamp + * the timestamp to change + * @param fromTz + * the timezone to change from + * @param toTz + * the timezone to change to + * + * @return the timestamp changed to the timezone 'toTz' + */ + public static Timestamp changeTimezone(MySQLConnection conn, Calendar sessionCalendar, Calendar targetCalendar, Timestamp tstamp, TimeZone fromTz, + TimeZone toTz, boolean rollForward) { + if ((conn != null)) { + if (conn.getUseTimezone()) { + // Convert the timestamp from GMT to the server's timezone + Calendar fromCal = Calendar.getInstance(fromTz); + fromCal.setTime(tstamp); + + int fromOffset = fromCal.get(Calendar.ZONE_OFFSET) + fromCal.get(Calendar.DST_OFFSET); + Calendar toCal = Calendar.getInstance(toTz); + toCal.setTime(tstamp); + + int toOffset = toCal.get(Calendar.ZONE_OFFSET) + toCal.get(Calendar.DST_OFFSET); + int offsetDiff = fromOffset - toOffset; + long toTime = toCal.getTime().getTime(); + + if (rollForward) { + toTime += offsetDiff; + } else { + toTime -= offsetDiff; + } + + Timestamp changedTimestamp = new Timestamp(toTime); + + return changedTimestamp; + } else if (conn.getUseJDBCCompliantTimezoneShift()) { + if (targetCalendar != null) { + + Timestamp adjustedTimestamp = new Timestamp(jdbcCompliantZoneShift(sessionCalendar, targetCalendar, tstamp)); + + adjustedTimestamp.setNanos(tstamp.getNanos()); + + return adjustedTimestamp; + } + } + } + + return tstamp; + } + + private static long jdbcCompliantZoneShift(Calendar sessionCalendar, Calendar targetCalendar, java.util.Date dt) { + if (sessionCalendar == null) { + sessionCalendar = new GregorianCalendar(); + } + + synchronized (sessionCalendar) { + // JDBC spec is not clear whether or not this calendar should be immutable, so let's treat it like it is, for safety + + java.util.Date origCalDate = targetCalendar.getTime(); + java.util.Date origSessionDate = sessionCalendar.getTime(); + + try { + sessionCalendar.setTime(dt); + + targetCalendar.set(Calendar.YEAR, sessionCalendar.get(Calendar.YEAR)); + targetCalendar.set(Calendar.MONTH, sessionCalendar.get(Calendar.MONTH)); + targetCalendar.set(Calendar.DAY_OF_MONTH, sessionCalendar.get(Calendar.DAY_OF_MONTH)); + + targetCalendar.set(Calendar.HOUR_OF_DAY, sessionCalendar.get(Calendar.HOUR_OF_DAY)); + targetCalendar.set(Calendar.MINUTE, sessionCalendar.get(Calendar.MINUTE)); + targetCalendar.set(Calendar.SECOND, sessionCalendar.get(Calendar.SECOND)); + targetCalendar.set(Calendar.MILLISECOND, sessionCalendar.get(Calendar.MILLISECOND)); + + return targetCalendar.getTime().getTime(); + + } finally { + sessionCalendar.setTime(origSessionDate); + targetCalendar.setTime(origCalDate); + } + } + } + + final static Date fastDateCreate(boolean useGmtConversion, Calendar gmtCalIfNeeded, Calendar cal, int year, int month, int day) { + + Calendar dateCal = cal; + + if (useGmtConversion) { + + if (gmtCalIfNeeded == null) { + gmtCalIfNeeded = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + } + + dateCal = gmtCalIfNeeded; + } + + synchronized (dateCal) { + java.util.Date origCalDate = dateCal.getTime(); + try { + dateCal.clear(); + dateCal.set(Calendar.MILLISECOND, 0); + + // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month???? + dateCal.set(year, month - 1, day, 0, 0, 0); + + long dateAsMillis = dateCal.getTimeInMillis(); + + return new Date(dateAsMillis); + } finally { + dateCal.setTime(origCalDate); + } + } + + } + + final static Date fastDateCreate(int year, int month, int day, Calendar targetCalendar) { + + Calendar dateCal = (targetCalendar == null) ? new GregorianCalendar() : targetCalendar; + + synchronized (dateCal) { + java.util.Date origCalDate = dateCal.getTime(); + try { + dateCal.clear(); + + // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month???? + dateCal.set(year, month - 1, day, 0, 0, 0); + dateCal.set(Calendar.MILLISECOND, 0); + + long dateAsMillis = dateCal.getTimeInMillis(); + + return new Date(dateAsMillis); + } finally { + dateCal.setTime(origCalDate); + } + } + } + + final static Time fastTimeCreate(Calendar cal, int hour, int minute, int second, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (hour < 0 || hour > 24) { + throw SQLError.createSQLException( + "Illegal hour value '" + hour + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + if (minute < 0 || minute > 59) { + throw SQLError.createSQLException( + "Illegal minute value '" + minute + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + if (second < 0 || second > 59) { + throw SQLError.createSQLException( + "Illegal minute value '" + second + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + synchronized (cal) { + java.util.Date origCalDate = cal.getTime(); + try { + cal.clear(); + + // Set 'date' to epoch of Jan 1, 1970 + cal.set(1970, 0, 1, hour, minute, second); + + long timeAsMillis = cal.getTimeInMillis(); + + return new Time(timeAsMillis); + } finally { + cal.setTime(origCalDate); + } + } + } + + final static Time fastTimeCreate(int hour, int minute, int second, Calendar targetCalendar, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (hour < 0 || hour > 23) { + throw SQLError.createSQLException( + "Illegal hour value '" + hour + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + if (minute < 0 || minute > 59) { + throw SQLError.createSQLException( + "Illegal minute value '" + minute + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + if (second < 0 || second > 59) { + throw SQLError.createSQLException( + "Illegal minute value '" + second + "' for java.sql.Time type in value '" + timeFormattedString(hour, minute, second) + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + Calendar cal = (targetCalendar == null) ? new GregorianCalendar() : targetCalendar; + + synchronized (cal) { + java.util.Date origCalDate = cal.getTime(); + try { + cal.clear(); + + // Set 'date' to epoch of Jan 1, 1970 + cal.set(1970, 0, 1, hour, minute, second); + + long timeAsMillis = cal.getTimeInMillis(); + + return new Time(timeAsMillis); + } finally { + cal.setTime(origCalDate); + } + } + } + + final static Timestamp fastTimestampCreate(boolean useGmtConversion, Calendar gmtCalIfNeeded, Calendar cal, int year, int month, int day, int hour, + int minute, int seconds, int secondsPart) { + + synchronized (cal) { + java.util.Date origCalDate = cal.getTime(); + try { + cal.clear(); + + // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month???? + cal.set(year, month - 1, day, hour, minute, seconds); + + int offsetDiff = 0; + + if (useGmtConversion) { + int fromOffset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); + + if (gmtCalIfNeeded == null) { + gmtCalIfNeeded = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + } + gmtCalIfNeeded.clear(); + + gmtCalIfNeeded.setTimeInMillis(cal.getTimeInMillis()); + + int toOffset = gmtCalIfNeeded.get(Calendar.ZONE_OFFSET) + gmtCalIfNeeded.get(Calendar.DST_OFFSET); + offsetDiff = fromOffset - toOffset; + } + + if (secondsPart != 0) { + cal.set(Calendar.MILLISECOND, secondsPart / 1000000); + } + + long tsAsMillis = cal.getTimeInMillis(); + + Timestamp ts = new Timestamp(tsAsMillis + offsetDiff); + + ts.setNanos(secondsPart); + + return ts; + } finally { + cal.setTime(origCalDate); + } + } + } + + final static Timestamp fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) { + Calendar cal = (tz == null) ? new GregorianCalendar() : new GregorianCalendar(tz); + cal.clear(); + + // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month???? + cal.set(year, month - 1, day, hour, minute, seconds); + + long tsAsMillis = cal.getTimeInMillis(); + + Timestamp ts = new Timestamp(tsAsMillis); + ts.setNanos(secondsPart); + + return ts; + } + + /** + * Returns the 'official' Java timezone name for the given timezone + * + * @param timezoneStr + * the 'common' timezone name + * + * @return the Java timezone name for the given timezone + * @throws SQLException + * + * @throws IllegalArgumentException + */ + public static String getCanonicalTimezone(String timezoneStr, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (timezoneStr == null) { + return null; + } + + timezoneStr = timezoneStr.trim(); + + // handle '+/-hh:mm' form ... + if (timezoneStr.length() > 2) { + if ((timezoneStr.charAt(0) == '+' || timezoneStr.charAt(0) == '-') && Character.isDigit(timezoneStr.charAt(1))) { + return "GMT" + timezoneStr; + } + } + + synchronized (TimeUtil.class) { + if (timeZoneMappings == null) { + loadTimeZoneMappings(exceptionInterceptor); + } + } + + String canonicalTz; + if ((canonicalTz = timeZoneMappings.getProperty(timezoneStr)) != null) { + return canonicalTz; + } + + throw SQLError.createSQLException(Messages.getString("TimeUtil.UnrecognizedTimezoneId", new Object[] { timezoneStr }), + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, exceptionInterceptor); + } + + // we could use SimpleDateFormat, but it won't work when the time values are out-of-bounds, and we're using this for error messages for exactly that case + + private static String timeFormattedString(int hours, int minutes, int seconds) { + StringBuilder buf = new StringBuilder(8); + if (hours < 10) { + buf.append("0"); + } + + buf.append(hours); + buf.append(":"); + + if (minutes < 10) { + buf.append("0"); + } + + buf.append(minutes); + buf.append(":"); + + if (seconds < 10) { + buf.append("0"); + } + + buf.append(seconds); + + return buf.toString(); + } + + public static String formatNanos(int nanos, boolean serverSupportsFracSecs, boolean usingMicros) { + + // get only last 9 digits + if (nanos > 999999999) { + nanos %= 100000000; + } + + if (usingMicros) { + nanos /= 1000; + } + + if (!serverSupportsFracSecs || nanos == 0) { + return "0"; + } + + final int digitCount = usingMicros ? 6 : 9; + + String nanosString = Integer.toString(nanos); + final String zeroPadding = usingMicros ? "000000" : "000000000"; + + nanosString = zeroPadding.substring(0, (digitCount - nanosString.length())) + nanosString; + + int pos = digitCount - 1; // the end, we're padded to the end by the code above + + while (nanosString.charAt(pos) == '0') { + pos--; + } + + nanosString = nanosString.substring(0, pos + 1); + + return nanosString; + } + + /** + * Loads a properties file that contains all kinds of time zone mappings. + * + * @param exceptionInterceptor + * @throws SQLException + */ + private static void loadTimeZoneMappings(ExceptionInterceptor exceptionInterceptor) throws SQLException { + timeZoneMappings = new Properties(); + try { + timeZoneMappings.load(TimeUtil.class.getResourceAsStream(TIME_ZONE_MAPPINGS_RESOURCE)); + } catch (IOException e) { + throw SQLError.createSQLException(Messages.getString("TimeUtil.LoadTimeZoneMappingError"), SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, + exceptionInterceptor); + } + // bridge all Time Zone ids known by Java + for (String tz : TimeZone.getAvailableIDs()) { + if (!timeZoneMappings.containsKey(tz)) { + timeZoneMappings.put(tz, tz); + } + } + } + + public static Timestamp truncateFractionalSeconds(Timestamp timestamp) { + Timestamp truncatedTimestamp = new Timestamp(timestamp.getTime()); + truncatedTimestamp.setNanos(0); + return truncatedTimestamp; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeZoneMapping.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeZoneMapping.properties new file mode 100644 index 0000000..e5d055b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/TimeZoneMapping.properties @@ -0,0 +1,531 @@ +#Windows Zones +#Mon Sep 28 16:41:59 WEST 2015 +AUS\ Central\ Daylight\ Time=Australia/Darwin +AUS\ Central\ Standard\ Time=Australia/Darwin +AUS\ Eastern\ Daylight\ Time=Australia/Sydney +AUS\ Eastern\ Standard\ Time=Australia/Sydney +Afghanistan\ Daylight\ Time=Asia/Kabul +Afghanistan\ Standard\ Time=Asia/Kabul +Alaskan\ Daylight\ Time=America/Anchorage +Alaskan\ Standard\ Time=America/Anchorage +Arab\ Daylight\ Time=Asia/Riyadh +Arab\ Standard\ Time=Asia/Riyadh +Arabian\ Daylight\ Time=Asia/Dubai +Arabian\ Standard\ Time=Asia/Dubai +Arabic\ Daylight\ Time=Asia/Baghdad +Arabic\ Standard\ Time=Asia/Baghdad +Argentina\ Daylight\ Time=America/Buenos_Aires +Argentina\ Standard\ Time=America/Buenos_Aires +Atlantic\ Daylight\ Time=America/Halifax +Atlantic\ Standard\ Time=America/Halifax +Azerbaijan\ Daylight\ Time=Asia/Baku +Azerbaijan\ Standard\ Time=Asia/Baku +Azores\ Daylight\ Time=Atlantic/Azores +Azores\ Standard\ Time=Atlantic/Azores +Bahia\ Daylight\ Time=America/Bahia +Bahia\ Standard\ Time=America/Bahia +Bangladesh\ Daylight\ Time=Asia/Dhaka +Bangladesh\ Standard\ Time=Asia/Dhaka +Belarus\ Daylight\ Time=Europe/Minsk +Belarus\ Standard\ Time=Europe/Minsk +Canada\ Central\ Daylight\ Time=America/Regina +Canada\ Central\ Standard\ Time=America/Regina +Cape\ Verde\ Daylight\ Time=Atlantic/Cape_Verde +Cape\ Verde\ Standard\ Time=Atlantic/Cape_Verde +Caucasus\ Daylight\ Time=Asia/Yerevan +Caucasus\ Standard\ Time=Asia/Yerevan +Cen.\ Australia\ Daylight\ Time=Australia/Adelaide +Cen.\ Australia\ Standard\ Time=Australia/Adelaide +Central\ America\ Daylight\ Time=America/Guatemala +Central\ America\ Standard\ Time=America/Guatemala +Central\ Asia\ Daylight\ Time=Asia/Almaty +Central\ Asia\ Standard\ Time=Asia/Almaty +Central\ Brazilian\ Daylight\ Time=America/Cuiaba +Central\ Brazilian\ Standard\ Time=America/Cuiaba +Central\ Daylight\ Time=America/Chicago +Central\ Daylight\ Time\ (Mexico)=America/Mexico_City +Central\ Europe\ Daylight\ Time=Europe/Budapest +Central\ Europe\ Standard\ Time=Europe/Budapest +Central\ European\ Daylight\ Time=Europe/Warsaw +Central\ European\ Standard\ Time=Europe/Warsaw +Central\ Pacific\ Daylight\ Time=Pacific/Guadalcanal +Central\ Pacific\ Standard\ Time=Pacific/Guadalcanal +Central\ Standard\ Time=America/Chicago +Central\ Standard\ Time\ (Mexico)=America/Mexico_City +China\ Daylight\ Time=Asia/Shanghai +China\ Standard\ Time=Asia/Shanghai +Dateline\ Daylight\ Time=Etc/GMT+12 +Dateline\ Standard\ Time=Etc/GMT+12 +E.\ Africa\ Daylight\ Time=Africa/Nairobi +E.\ Africa\ Standard\ Time=Africa/Nairobi +E.\ Australia\ Daylight\ Time=Australia/Brisbane +E.\ Australia\ Standard\ Time=Australia/Brisbane +E.\ South\ America\ Daylight\ Time=America/Sao_Paulo +E.\ South\ America\ Standard\ Time=America/Sao_Paulo +Eastern\ Daylight\ Time=America/New_York +Eastern\ Daylight\ Time\ (Mexico)=America/Cancun +Eastern\ Standard\ Time=America/New_York +Eastern\ Standard\ Time\ (Mexico)=America/Cancun +Egypt\ Daylight\ Time=Africa/Cairo +Egypt\ Standard\ Time=Africa/Cairo +Ekaterinburg\ Daylight\ Time=Asia/Yekaterinburg +Ekaterinburg\ Standard\ Time=Asia/Yekaterinburg +FLE\ Daylight\ Time=Europe/Kiev +FLE\ Standard\ Time=Europe/Kiev +Fiji\ Daylight\ Time=Pacific/Fiji +Fiji\ Standard\ Time=Pacific/Fiji +GMT\ Daylight\ Time=Europe/London +GMT\ Standard\ Time=Europe/London +GTB\ Daylight\ Time=Europe/Bucharest +GTB\ Standard\ Time=Europe/Bucharest +Georgian\ Daylight\ Time=Asia/Tbilisi +Georgian\ Standard\ Time=Asia/Tbilisi +Greenland\ Daylight\ Time=America/Godthab +Greenland\ Standard\ Time=America/Godthab +Greenwich\ Daylight\ Time=Atlantic/Reykjavik +Greenwich\ Standard\ Time=Atlantic/Reykjavik +Hawaiian\ Daylight\ Time=Pacific/Honolulu +Hawaiian\ Standard\ Time=Pacific/Honolulu +India\ Daylight\ Time=Asia/Calcutta +India\ Standard\ Time=Asia/Calcutta +Iran\ Daylight\ Time=Asia/Tehran +Iran\ Standard\ Time=Asia/Tehran +Israel\ Daylight\ Time=Asia/Jerusalem +Israel\ Standard\ Time=Asia/Jerusalem +Jordan\ Daylight\ Time=Asia/Amman +Jordan\ Standard\ Time=Asia/Amman +Kaliningrad\ Daylight\ Time=Europe/Kaliningrad +Kaliningrad\ Standard\ Time=Europe/Kaliningrad +Korea\ Daylight\ Time=Asia/Seoul +Korea\ Standard\ Time=Asia/Seoul +Libya\ Daylight\ Time=Africa/Tripoli +Libya\ Standard\ Time=Africa/Tripoli +Line\ Islands\ Daylight\ Time=Pacific/Kiritimati +Line\ Islands\ Standard\ Time=Pacific/Kiritimati +Magadan\ Daylight\ Time=Asia/Magadan +Magadan\ Standard\ Time=Asia/Magadan +Mauritius\ Daylight\ Time=Indian/Mauritius +Mauritius\ Standard\ Time=Indian/Mauritius +Middle\ East\ Daylight\ Time=Asia/Beirut +Middle\ East\ Standard\ Time=Asia/Beirut +Montevideo\ Daylight\ Time=America/Montevideo +Montevideo\ Standard\ Time=America/Montevideo +Morocco\ Daylight\ Time=Africa/Casablanca +Morocco\ Standard\ Time=Africa/Casablanca +Mountain\ Daylight\ Time=America/Denver +Mountain\ Daylight\ Time\ (Mexico)=America/Chihuahua +Mountain\ Standard\ Time=America/Denver +Mountain\ Standard\ Time\ (Mexico)=America/Chihuahua +Myanmar\ Daylight\ Time=Asia/Rangoon +Myanmar\ Standard\ Time=Asia/Rangoon +N.\ Central\ Asia\ Daylight\ Time=Asia/Novosibirsk +N.\ Central\ Asia\ Standard\ Time=Asia/Novosibirsk +Namibia\ Daylight\ Time=Africa/Windhoek +Namibia\ Standard\ Time=Africa/Windhoek +Nepal\ Daylight\ Time=Asia/Katmandu +Nepal\ Standard\ Time=Asia/Katmandu +New\ Zealand\ Daylight\ Time=Pacific/Auckland +New\ Zealand\ Standard\ Time=Pacific/Auckland +Newfoundland\ Daylight\ Time=America/St_Johns +Newfoundland\ Standard\ Time=America/St_Johns +North\ Asia\ Daylight\ Time=Asia/Krasnoyarsk +North\ Asia\ East\ Daylight\ Time=Asia/Irkutsk +North\ Asia\ East\ Standard\ Time=Asia/Irkutsk +North\ Asia\ Standard\ Time=Asia/Krasnoyarsk +Pacific\ Daylight\ Time=America/Los_Angeles +Pacific\ Daylight\ Time\ (Mexico)=America/Santa_Isabel +Pacific\ SA\ Daylight\ Time=America/Santiago +Pacific\ SA\ Standard\ Time=America/Santiago +Pacific\ Standard\ Time=America/Los_Angeles +Pacific\ Standard\ Time\ (Mexico)=America/Santa_Isabel +Pakistan\ Daylight\ Time=Asia/Karachi +Pakistan\ Standard\ Time=Asia/Karachi +Paraguay\ Daylight\ Time=America/Asuncion +Paraguay\ Standard\ Time=America/Asuncion +Romance\ Daylight\ Time=Europe/Paris +Romance\ Standard\ Time=Europe/Paris +Russia\ Time\ Zone\ 10=Asia/Srednekolymsk +Russia\ Time\ Zone\ 11=Asia/Kamchatka +Russia\ Time\ Zone\ 3=Europe/Samara +Russian\ Daylight\ Time=Europe/Moscow +Russian\ Standard\ Time=Europe/Moscow +SA\ Eastern\ Daylight\ Time=America/Cayenne +SA\ Eastern\ Standard\ Time=America/Cayenne +SA\ Pacific\ Daylight\ Time=America/Bogota +SA\ Pacific\ Standard\ Time=America/Bogota +SA\ Western\ Daylight\ Time=America/La_Paz +SA\ Western\ Standard\ Time=America/La_Paz +SE\ Asia\ Daylight\ Time=Asia/Bangkok +SE\ Asia\ Standard\ Time=Asia/Bangkok +Samoa\ Daylight\ Time=Pacific/Apia +Samoa\ Standard\ Time=Pacific/Apia +Singapore\ Daylight\ Time=Asia/Singapore +Singapore\ Standard\ Time=Asia/Singapore +South\ Africa\ Daylight\ Time=Africa/Johannesburg +South\ Africa\ Standard\ Time=Africa/Johannesburg +Sri\ Lanka\ Daylight\ Time=Asia/Colombo +Sri\ Lanka\ Standard\ Time=Asia/Colombo +Syria\ Daylight\ Time=Asia/Damascus +Syria\ Standard\ Time=Asia/Damascus +Taipei\ Daylight\ Time=Asia/Taipei +Taipei\ Standard\ Time=Asia/Taipei +Tasmania\ Daylight\ Time=Australia/Hobart +Tasmania\ Standard\ Time=Australia/Hobart +Tokyo\ Daylight\ Time=Asia/Tokyo +Tokyo\ Standard\ Time=Asia/Tokyo +Tonga\ Daylight\ Time=Pacific/Tongatapu +Tonga\ Standard\ Time=Pacific/Tongatapu +Turkey\ Daylight\ Time=Europe/Istanbul +Turkey\ Standard\ Time=Europe/Istanbul +US\ Eastern\ Daylight\ Time=America/Indianapolis +US\ Eastern\ Standard\ Time=America/Indianapolis +US\ Mountain\ Daylight\ Time=America/Phoenix +US\ Mountain\ Standard\ Time=America/Phoenix +UTC=Etc/GMT +UTC+12=Etc/GMT-12 +UTC-02=Etc/GMT+2 +UTC-11=Etc/GMT+11 +Ulaanbaatar\ Daylight\ Time=Asia/Ulaanbaatar +Ulaanbaatar\ Standard\ Time=Asia/Ulaanbaatar +Venezuela\ Daylight\ Time=America/Caracas +Venezuela\ Standard\ Time=America/Caracas +Vladivostok\ Daylight\ Time=Asia/Vladivostok +Vladivostok\ Standard\ Time=Asia/Vladivostok +W.\ Australia\ Daylight\ Time=Australia/Perth +W.\ Australia\ Standard\ Time=Australia/Perth +W.\ Central\ Africa\ Daylight\ Time=Africa/Lagos +W.\ Central\ Africa\ Standard\ Time=Africa/Lagos +W.\ Europe\ Daylight\ Time=Europe/Berlin +W.\ Europe\ Standard\ Time=Europe/Berlin +West\ Asia\ Daylight\ Time=Asia/Tashkent +West\ Asia\ Standard\ Time=Asia/Tashkent +West\ Pacific\ Daylight\ Time=Pacific/Port_Moresby +West\ Pacific\ Standard\ Time=Pacific/Port_Moresby +Yakutsk\ Daylight\ Time=Asia/Yakutsk +Yakutsk\ Standard\ Time=Asia/Yakutsk +#Linked Time Zones alias +#Mon Sep 28 16:41:59 WEST 2015 +Africa/Addis_Ababa=Africa/Nairobi +Africa/Asmara=Africa/Nairobi +Africa/Asmera=Africa/Nairobi +Africa/Bamako=Africa/Abidjan +Africa/Bangui=Africa/Lagos +Africa/Banjul=Africa/Abidjan +Africa/Blantyre=Africa/Maputo +Africa/Brazzaville=Africa/Lagos +Africa/Bujumbura=Africa/Maputo +Africa/Conakry=Africa/Abidjan +Africa/Dakar=Africa/Abidjan +Africa/Dar_es_Salaam=Africa/Nairobi +Africa/Djibouti=Africa/Nairobi +Africa/Douala=Africa/Lagos +Africa/Freetown=Africa/Abidjan +Africa/Gaborone=Africa/Maputo +Africa/Harare=Africa/Maputo +Africa/Juba=Africa/Khartoum +Africa/Kampala=Africa/Nairobi +Africa/Kigali=Africa/Maputo +Africa/Kinshasa=Africa/Lagos +Africa/Libreville=Africa/Lagos +Africa/Lome=Africa/Abidjan +Africa/Luanda=Africa/Lagos +Africa/Lubumbashi=Africa/Maputo +Africa/Lusaka=Africa/Maputo +Africa/Malabo=Africa/Lagos +Africa/Maseru=Africa/Johannesburg +Africa/Mbabane=Africa/Johannesburg +Africa/Mogadishu=Africa/Nairobi +Africa/Niamey=Africa/Lagos +Africa/Nouakchott=Africa/Abidjan +Africa/Ouagadougou=Africa/Abidjan +Africa/Porto-Novo=Africa/Lagos +Africa/Sao_Tome=Africa/Abidjan +Africa/Timbuktu=Africa/Abidjan +America/Anguilla=America/Port_of_Spain +America/Antigua=America/Port_of_Spain +America/Argentina/ComodRivadavia=America/Argentina/Catamarca +America/Aruba=America/Curacao +America/Atka=America/Adak +America/Buenos_Aires=America/Argentina/Buenos_Aires +America/Catamarca=America/Argentina/Catamarca +America/Coral_Harbour=America/Atikokan +America/Cordoba=America/Argentina/Cordoba +America/Dominica=America/Port_of_Spain +America/Ensenada=America/Tijuana +America/Fort_Wayne=America/Indiana/Indianapolis +America/Grenada=America/Port_of_Spain +America/Guadeloupe=America/Port_of_Spain +America/Indianapolis=America/Indiana/Indianapolis +America/Jujuy=America/Argentina/Jujuy +America/Knox_IN=America/Indiana/Knox +America/Kralendijk=America/Curacao +America/Louisville=America/Kentucky/Louisville +America/Lower_Princes=America/Curacao +America/Marigot=America/Port_of_Spain +America/Mendoza=America/Argentina/Mendoza +America/Montreal=America/Toronto +America/Montserrat=America/Port_of_Spain +America/Porto_Acre=America/Rio_Branco +America/Rosario=America/Argentina/Cordoba +America/Shiprock=America/Denver +America/St_Barthelemy=America/Port_of_Spain +America/St_Kitts=America/Port_of_Spain +America/St_Lucia=America/Port_of_Spain +America/St_Thomas=America/Port_of_Spain +America/St_Vincent=America/Port_of_Spain +America/Tortola=America/Port_of_Spain +America/Virgin=America/Port_of_Spain +Antarctica/McMurdo=Pacific/Auckland +Antarctica/South_Pole=Pacific/Auckland +Arctic/Longyearbyen=Europe/Oslo +Asia/Aden=Asia/Riyadh +Asia/Ashkhabad=Asia/Ashgabat +Asia/Bahrain=Asia/Qatar +Asia/Calcutta=Asia/Kolkata +Asia/Chongqing=Asia/Shanghai +Asia/Chungking=Asia/Shanghai +Asia/Dacca=Asia/Dhaka +Asia/Harbin=Asia/Shanghai +Asia/Istanbul=Europe/Istanbul +Asia/Kashgar=Asia/Urumqi +Asia/Katmandu=Asia/Kathmandu +Asia/Kuwait=Asia/Riyadh +Asia/Macao=Asia/Macau +Asia/Muscat=Asia/Dubai +Asia/Phnom_Penh=Asia/Bangkok +Asia/Saigon=Asia/Ho_Chi_Minh +Asia/Tel_Aviv=Asia/Jerusalem +Asia/Thimbu=Asia/Thimphu +Asia/Ujung_Pandang=Asia/Makassar +Asia/Ulan_Bator=Asia/Ulaanbaatar +Asia/Vientiane=Asia/Bangkok +Atlantic/Faeroe=Atlantic/Faroe +Atlantic/Jan_Mayen=Europe/Oslo +Atlantic/St_Helena=Africa/Abidjan +Australia/ACT=Australia/Sydney +Australia/Canberra=Australia/Sydney +Australia/LHI=Australia/Lord_Howe +Australia/NSW=Australia/Sydney +Australia/North=Australia/Darwin +Australia/Queensland=Australia/Brisbane +Australia/South=Australia/Adelaide +Australia/Tasmania=Australia/Hobart +Australia/Victoria=Australia/Melbourne +Australia/West=Australia/Perth +Australia/Yancowinna=Australia/Broken_Hill +Brazil/Acre=America/Rio_Branco +Brazil/DeNoronha=America/Noronha +Brazil/East=America/Sao_Paulo +Brazil/West=America/Manaus +Canada/Atlantic=America/Halifax +Canada/Central=America/Winnipeg +Canada/East-Saskatchewan=America/Regina +Canada/Eastern=America/Toronto +Canada/Mountain=America/Edmonton +Canada/Newfoundland=America/St_Johns +Canada/Pacific=America/Vancouver +Canada/Saskatchewan=America/Regina +Canada/Yukon=America/Whitehorse +Chile/Continental=America/Santiago +Chile/EasterIsland=Pacific/Easter +Cuba=America/Havana +Egypt=Africa/Cairo +Eire=Europe/Dublin +Europe/Belfast=Europe/London +Europe/Bratislava=Europe/Prague +Europe/Busingen=Europe/Zurich +Europe/Guernsey=Europe/London +Europe/Isle_of_Man=Europe/London +Europe/Jersey=Europe/London +Europe/Ljubljana=Europe/Belgrade +Europe/Mariehamn=Europe/Helsinki +Europe/Nicosia=Asia/Nicosia +Europe/Podgorica=Europe/Belgrade +Europe/San_Marino=Europe/Rome +Europe/Sarajevo=Europe/Belgrade +Europe/Skopje=Europe/Belgrade +Europe/Tiraspol=Europe/Chisinau +Europe/Vaduz=Europe/Zurich +Europe/Vatican=Europe/Rome +Europe/Zagreb=Europe/Belgrade +GB=Europe/London +GB-Eire=Europe/London +GMT+0=Etc/GMT +GMT-0=Etc/GMT +GMT0=Etc/GMT +Greenwich=Etc/GMT +Hongkong=Asia/Hong_Kong +Iceland=Atlantic/Reykjavik +Indian/Antananarivo=Africa/Nairobi +Indian/Comoro=Africa/Nairobi +Indian/Mayotte=Africa/Nairobi +Iran=Asia/Tehran +Israel=Asia/Jerusalem +Jamaica=America/Jamaica +Japan=Asia/Tokyo +Kwajalein=Pacific/Kwajalein +Libya=Africa/Tripoli +Mexico/BajaNorte=America/Tijuana +Mexico/BajaSur=America/Mazatlan +Mexico/General=America/Mexico_City +NZ=Pacific/Auckland +NZ-CHAT=Pacific/Chatham +Navajo=America/Denver +PRC=Asia/Shanghai +Pacific/Johnston=Pacific/Honolulu +Pacific/Midway=Pacific/Pago_Pago +Pacific/Ponape=Pacific/Pohnpei +Pacific/Saipan=Pacific/Guam +Pacific/Samoa=Pacific/Pago_Pago +Pacific/Truk=Pacific/Chuuk +Pacific/Yap=Pacific/Chuuk +Poland=Europe/Warsaw +Portugal=Europe/Lisbon +ROC=Asia/Taipei +ROK=Asia/Seoul +Singapore=Asia/Singapore +Turkey=Europe/Istanbul +UCT=Etc/UCT +US/Alaska=America/Anchorage +US/Aleutian=America/Adak +US/Arizona=America/Phoenix +US/Central=America/Chicago +US/East-Indiana=America/Indiana/Indianapolis +US/Eastern=America/New_York +US/Hawaii=Pacific/Honolulu +US/Indiana-Starke=America/Indiana/Knox +US/Michigan=America/Detroit +US/Mountain=America/Denver +US/Pacific=America/Los_Angeles +US/Pacific-New=America/Los_Angeles +US/Samoa=Pacific/Pago_Pago +Universal=Etc/UTC +W-SU=Europe/Moscow +Zulu=Etc/UTC +#Standard (IANA) abbreviations +#Mon Sep 28 16:41:59 WEST 2015 +ACWST=Australia/Eucla +AFT=Asia/Kabul +ALMT=Asia/Almaty +ANAT=Asia/Anadyr +AZOST=Atlantic/Azores +AZOT=Atlantic/Azores +AZST=Asia/Baku +AZT=Asia/Baku +BDT=Asia/Dhaka +BNT=Asia/Brunei +BOT=America/La_Paz +BRST=America/Sao_Paulo +BTT=Asia/Thimphu +CAT=Africa/Maputo +CCT=Indian/Cocos +CHADT=Pacific/Chatham +CHAST=Pacific/Chatham +CHOST=Asia/Choibalsan +CHOT=Asia/Choibalsan +CHUT=Pacific/Chuuk +CKT=Pacific/Rarotonga +COT=America/Bogota +CVT=Atlantic/Cape_Verde +CXT=Indian/Christmas +ChST=Pacific/Guam +DAVT=Antarctica/Davis +DDUT=Antarctica/DumontDUrville +EAST=Pacific/Easter +ECT=America/Guayaquil +EGST=America/Scoresbysund +EGT=America/Scoresbysund +FJST=Pacific/Fiji +FJT=Pacific/Fiji +FKST=Atlantic/Stanley +FNT=America/Noronha +GALT=Pacific/Galapagos +GAMT=Pacific/Gambier +GET=Asia/Tbilisi +GFT=America/Cayenne +GILT=Pacific/Tarawa +GYT=America/Guyana +HDT=America/Adak +HKT=Asia/Hong_Kong +HOVST=Asia/Hovd +HOVT=Asia/Hovd +IDT=Asia/Jerusalem +IOT=Indian/Chagos +IRST=Asia/Tehran +JST=Asia/Tokyo +KGT=Asia/Bishkek +KOST=Pacific/Kosrae +LHDT=Australia/Lord_Howe +LHST=Australia/Lord_Howe +LINT=Pacific/Kiritimati +MAGT=Asia/Magadan +MART=Pacific/Marquesas +MAWT=Antarctica/Mawson +MEST=MET +MET=MET +MIST=Antarctica/Macquarie +MMT=Asia/Rangoon +MUT=Indian/Mauritius +MVT=Indian/Maldives +NCT=Pacific/Noumea +NDT=America/St_Johns +NFT=Pacific/Norfolk +NOVT=Asia/Novosibirsk +NPT=Asia/Kathmandu +NRT=Pacific/Nauru +NST=America/St_Johns +NUT=Pacific/Niue +NZDT=Pacific/Auckland +NZST=Pacific/Auckland +OMST=Asia/Omsk +ORAT=Asia/Oral +PET=America/Lima +PETT=Asia/Kamchatka +PGT=Pacific/Port_Moresby +PHOT=Pacific/Enderbury +PHT=Asia/Manila +PKT=Asia/Karachi +PMDT=America/Miquelon +PMST=America/Miquelon +PONT=Pacific/Pohnpei +PWT=Pacific/Palau +PYST=America/Asuncion +PYT=America/Asuncion +QYZT=Asia/Qyzylorda +RET=Indian/Reunion +ROTT=Antarctica/Rothera +SAKT=Asia/Sakhalin +SAMT=Europe/Samara +SAST=Africa/Johannesburg +SBT=Pacific/Guadalcanal +SCT=Indian/Mahe +SGT=Asia/Singapore +SRET=Asia/Srednekolymsk +SRT=America/Paramaribo +SST=Pacific/Pago_Pago +SYOT=Antarctica/Syowa +TAHT=Pacific/Tahiti +TFT=Indian/Kerguelen +TJT=Asia/Dushanbe +TKT=Pacific/Fakaofo +TLT=Asia/Dili +TMT=Asia/Ashgabat +TOT=Pacific/Tongatapu +TVT=Pacific/Funafuti +ULAST=Asia/Ulaanbaatar +ULAT=Asia/Ulaanbaatar +UYT=America/Montevideo +VET=America/Caracas +VOST=Antarctica/Vostok +VUT=Pacific/Efate +WAKT=Pacific/Wake +WAST=Africa/Windhoek +WFT=Pacific/Wallis +WGST=America/Godthab +WGT=America/Godthab +WIT=Asia/Jayapura +WITA=Asia/Makassar +WSDT=Pacific/Apia +WSST=Pacific/Apia +XJT=Asia/Urumqi +YEKT=Asia/Yekaterinburg diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/UpdatableResultSet.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/UpdatableResultSet.java new file mode 100644 index 0000000..0564866 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/UpdatableResultSet.java @@ -0,0 +1,2560 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.math.BigDecimal; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * A result set that is updatable. + */ +public class UpdatableResultSet extends ResultSetImpl { + /** Marker for 'stream' data when doing INSERT rows */ + final static byte[] STREAM_DATA_MARKER = StringUtils.getBytes("** STREAM DATA **"); + + protected SingleByteCharsetConverter charConverter; + + private String charEncoding; + + /** What is the default value for the column? */ + private byte[][] defaultColumnValue; + + /** PreparedStatement used to delete data */ + private com.mysql.jdbc.PreparedStatement deleter = null; + + private String deleteSQL = null; + + private boolean initializedCharConverter = false; + + /** PreparedStatement used to insert data */ + protected com.mysql.jdbc.PreparedStatement inserter = null; + + private String insertSQL = null; + + /** Is this result set updateable? */ + private boolean isUpdatable = false; + + /** Reason the result set is not updatable */ + private String notUpdatableReason = null; + + /** List of primary keys */ + private List primaryKeyIndicies = null; + + private String qualifiedAndQuotedTableName; + + private String quotedIdChar = null; + + /** PreparedStatement used to refresh data */ + private com.mysql.jdbc.PreparedStatement refresher; + + private String refreshSQL = null; + + /** The binary data for the 'current' row */ + private ResultSetRow savedCurrentRow; + + /** PreparedStatement used to delete data */ + protected com.mysql.jdbc.PreparedStatement updater = null; + + /** SQL for in-place modifcation */ + private String updateSQL = null; + + private boolean populateInserterWithDefaultValues = false; + + private Map>> databasesUsedToTablesUsed = null; + + /** + * Creates a new ResultSet object. + * + * @param catalog + * the database in use when we were created + * @param fields + * an array of Field objects (basically, the ResultSet MetaData) + * @param tuples + * actual row data + * @param conn + * the Connection that created us. + * @param creatorStmt + * + * @throws SQLException + */ + protected UpdatableResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException { + super(catalog, fields, tuples, conn, creatorStmt); + checkUpdatability(); + this.populateInserterWithDefaultValues = this.connection.getPopulateInsertRowWithDefaultValues(); + } + + /** + * JDBC 2.0 + * + *

    + * Move to an absolute row number in the result set. + *

    + * + *

    + * If row is positive, moves to an absolute row with respect to the beginning of the result set. The first row is row 1, the second is row 2, etc. + *

    + * + *

    + * If row is negative, moves to an absolute row position with respect to the end of result set. For example, calling absolute(-1) positions the cursor on + * the last row, absolute(-2) indicates the next-to-last row, etc. + *

    + * + *

    + * An attempt to position the cursor beyond the first/last row in the result set, leaves the cursor before/after the first/last row, respectively. + *

    + * + *

    + * Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last(). + *

    + * + * @param row + * + * @return true if on the result set, false if off. + * + * @exception SQLException + * if a database-access error occurs, or row is 0, or result + * set type is TYPE_FORWARD_ONLY. + */ + @Override + public synchronized boolean absolute(int row) throws SQLException { + return super.absolute(row); + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the end of the result set, just after the last row. Has no effect if the result set contains no rows. + *

    + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + @Override + public synchronized void afterLast() throws SQLException { + super.afterLast(); + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the front of the result set, just before the first row. Has no effect if the result set contains no rows. + *

    + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY + */ + @Override + public synchronized void beforeFirst() throws SQLException { + super.beforeFirst(); + } + + /** + * JDBC 2.0 The cancelRowUpdates() method may be called after calling an + * updateXXX() method(s) and before calling updateRow() to rollback the + * updates made to a row. If no updates have been made or updateRow() has + * already been called, then this method has no effect. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + */ + @Override + public synchronized void cancelRowUpdates() throws SQLException { + checkClosed(); + + if (this.doingUpdates) { + this.doingUpdates = false; + this.updater.clearParameters(); + } + } + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.ResultSet#checkRowPos() + */ + @Override + protected synchronized void checkRowPos() throws SQLException { + checkClosed(); + + if (!this.onInsertRow) { + super.checkRowPos(); + } + } + + /** + * Is this ResultSet updateable? + * + * @throws SQLException + */ + protected void checkUpdatability() throws SQLException { + try { + if (this.fields == null) { + // we've been created to be populated with cached metadata, and we don't have the metadata yet, we'll be called again by + // Connection.initializeResultsMetadataFromCache() when the metadata has been made available + + return; + } + + String singleTableName = null; + String catalogName = null; + + int primaryKeyCount = 0; + + // We can only do this if we know that there is a currently selected database, or if we're talking to a > 4.1 version of MySQL server (as it returns + // database names in field info) + if ((this.catalog == null) || (this.catalog.length() == 0)) { + this.catalog = this.fields[0].getDatabaseName(); + + if ((this.catalog == null) || (this.catalog.length() == 0)) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.43"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + if (this.fields.length > 0) { + singleTableName = this.fields[0].getOriginalTableName(); + catalogName = this.fields[0].getDatabaseName(); + + if (singleTableName == null) { + singleTableName = this.fields[0].getTableName(); + catalogName = this.catalog; + } + + if (singleTableName != null && singleTableName.length() == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + if (this.fields[0].isPrimaryKey()) { + primaryKeyCount++; + } + + // + // References only one table? + // + for (int i = 1; i < this.fields.length; i++) { + String otherTableName = this.fields[i].getOriginalTableName(); + String otherCatalogName = this.fields[i].getDatabaseName(); + + if (otherTableName == null) { + otherTableName = this.fields[i].getTableName(); + otherCatalogName = this.catalog; + } + + if (otherTableName != null && otherTableName.length() == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + if ((singleTableName == null) || !otherTableName.equals(singleTableName)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.0"); + + return; + } + + // Can't reference more than one database + if ((catalogName == null) || !otherCatalogName.equals(catalogName)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.1"); + + return; + } + + if (this.fields[i].isPrimaryKey()) { + primaryKeyCount++; + } + } + + if ((singleTableName == null) || (singleTableName.length() == 0)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.2"); + + return; + } + } else { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + if (this.connection.getStrictUpdates()) { + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + + java.sql.ResultSet rs = null; + HashMap primaryKeyNames = new HashMap(); + + try { + rs = dbmd.getPrimaryKeys(catalogName, null, singleTableName); + + while (rs.next()) { + String keyName = rs.getString(4); + keyName = keyName.toUpperCase(); + primaryKeyNames.put(keyName, keyName); + } + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + AssertionFailedException.shouldNotHappen(ex); + } + + rs = null; + } + } + + int existingPrimaryKeysCount = primaryKeyNames.size(); + + if (existingPrimaryKeysCount == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.5"); + + return; // we can't update tables w/o keys + } + + // + // Contains all primary keys? + // + for (int i = 0; i < this.fields.length; i++) { + if (this.fields[i].isPrimaryKey()) { + String columnNameUC = this.fields[i].getName().toUpperCase(); + + if (primaryKeyNames.remove(columnNameUC) == null) { + // try original name + String originalName = this.fields[i].getOriginalName(); + + if (originalName != null) { + if (primaryKeyNames.remove(originalName.toUpperCase()) == null) { + // we don't know about this key, so give up :( + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.6", new Object[] { originalName }); + + return; + } + } + } + } + } + + this.isUpdatable = primaryKeyNames.isEmpty(); + + if (!this.isUpdatable) { + if (existingPrimaryKeysCount > 1) { + this.notUpdatableReason = Messages.getString("NotUpdatableReason.7"); + } else { + this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); + } + + return; + } + } + + // + // Must have at least one primary key + // + if (primaryKeyCount == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); + + return; + } + + this.isUpdatable = true; + this.notUpdatableReason = null; + + return; + } catch (SQLException sqlEx) { + this.isUpdatable = false; + this.notUpdatableReason = sqlEx.getMessage(); + } + } + + /** + * JDBC 2.0 Delete the current row from the result set and the underlying + * database. Cannot be called when on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws SQLException + * if the ResultSet is not updatable or some other error occurs + */ + @Override + public synchronized void deleteRow() throws SQLException { + checkClosed(); + + if (!this.isUpdatable) { + throw new NotUpdatable(this.notUpdatableReason); + } + + if (this.onInsertRow) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.1"), getExceptionInterceptor()); + } else if (this.rowData.size() == 0) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.2"), getExceptionInterceptor()); + } else if (isBeforeFirst()) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.3"), getExceptionInterceptor()); + } else if (isAfterLast()) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.4"), getExceptionInterceptor()); + } + + if (this.deleter == null) { + if (this.deleteSQL == null) { + generateStatements(); + } + + this.deleter = (PreparedStatement) this.connection.clientPrepareStatement(this.deleteSQL); + } + + this.deleter.clearParameters(); + + int numKeys = this.primaryKeyIndicies.size(); + + if (numKeys == 1) { + int index = this.primaryKeyIndicies.get(0).intValue(); + this.setParamValue(this.deleter, 1, this.thisRow, index, this.fields[index].getSQLType()); + } else { + for (int i = 0; i < numKeys; i++) { + int index = this.primaryKeyIndicies.get(i).intValue(); + this.setParamValue(this.deleter, i + 1, this.thisRow, index, this.fields[index].getSQLType()); + + } + } + + this.deleter.executeUpdate(); + this.rowData.removeRow(this.rowData.getCurrentRowNumber()); + + // position on previous row - Bug#27431 + previous(); + + } + + private synchronized void setParamValue(PreparedStatement ps, int psIdx, ResultSetRow row, int rsIdx, int sqlType) throws SQLException { + + byte[] val = row.getColumnValue(rsIdx); + if (val == null) { + ps.setNull(psIdx, Types.NULL); + return; + } + switch (sqlType) { + case Types.NULL: + ps.setNull(psIdx, Types.NULL); + break; + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + ps.setInt(psIdx, row.getInt(rsIdx)); + break; + case Types.BIGINT: + ps.setLong(psIdx, row.getLong(rsIdx)); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.DECIMAL: + case Types.NUMERIC: + ps.setString(psIdx, row.getString(rsIdx, this.charEncoding, this.connection)); + break; + case Types.DATE: + ps.setDate(psIdx, row.getDateFast(rsIdx, this.connection, this, this.fastDefaultCal), this.fastDefaultCal); + break; + case Types.TIMESTAMP: + ps.setTimestamp(psIdx, row.getTimestampFast(rsIdx, this.fastDefaultCal, this.connection.getDefaultTimeZone(), false, this.connection, this)); + break; + case Types.TIME: + ps.setTime(psIdx, row.getTimeFast(rsIdx, this.fastDefaultCal, this.connection.getDefaultTimeZone(), false, this.connection, this)); + break; + case Types.FLOAT: + case Types.DOUBLE: + case Types.REAL: + case Types.BOOLEAN: + ps.setBytesNoEscapeNoQuotes(psIdx, val); + break; + /* + * default, but also explicitly for following types: + * case Types.BINARY: + * case Types.BLOB: + */ + default: + ps.setBytes(psIdx, val); + break; + } + + } + + private synchronized void extractDefaultValues() throws SQLException { + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + this.defaultColumnValue = new byte[this.fields.length][]; + + java.sql.ResultSet columnsResultSet = null; + + for (Map.Entry>> dbEntry : this.databasesUsedToTablesUsed.entrySet()) { + //String databaseName = dbEntry.getKey().toString(); + for (Map.Entry> tableEntry : dbEntry.getValue().entrySet()) { + String tableName = tableEntry.getKey(); + Map columnNamesToIndices = tableEntry.getValue(); + + try { + columnsResultSet = dbmd.getColumns(this.catalog, null, tableName, "%"); + + while (columnsResultSet.next()) { + String columnName = columnsResultSet.getString("COLUMN_NAME"); + byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); + + if (columnNamesToIndices.containsKey(columnName)) { + int localColumnIndex = columnNamesToIndices.get(columnName).intValue(); + + this.defaultColumnValue[localColumnIndex] = defaultValue; + } // else assert? + } + } finally { + if (columnsResultSet != null) { + columnsResultSet.close(); + + columnsResultSet = null; + } + } + } + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the first row in the result set. + *

    + * + * @return true if on a valid row, false if no rows in the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + @Override + public synchronized boolean first() throws SQLException { + return super.first(); + } + + /** + * Figure out whether or not this ResultSet is updateable, and if so, + * generate the PreparedStatements to support updates. + * + * @throws SQLException + * @throws NotUpdatable + */ + protected synchronized void generateStatements() throws SQLException { + if (!this.isUpdatable) { + this.doingUpdates = false; + this.onInsertRow = false; + + throw new NotUpdatable(this.notUpdatableReason); + } + + String quotedId = getQuotedIdChar(); + + Map tableNamesSoFar = null; + + if (this.connection.lowerCaseTableNames()) { + tableNamesSoFar = new TreeMap(String.CASE_INSENSITIVE_ORDER); + this.databasesUsedToTablesUsed = new TreeMap>>(String.CASE_INSENSITIVE_ORDER); + } else { + tableNamesSoFar = new TreeMap(); + this.databasesUsedToTablesUsed = new TreeMap>>(); + } + + this.primaryKeyIndicies = new ArrayList(); + + StringBuilder fieldValues = new StringBuilder(); + StringBuilder keyValues = new StringBuilder(); + StringBuilder columnNames = new StringBuilder(); + StringBuilder insertPlaceHolders = new StringBuilder(); + StringBuilder allTablesBuf = new StringBuilder(); + Map columnIndicesToTable = new HashMap(); + + boolean firstTime = true; + boolean keysFirstTime = true; + + String equalsStr = this.connection.versionMeetsMinimum(3, 23, 0) ? "<=>" : "="; + + for (int i = 0; i < this.fields.length; i++) { + StringBuilder tableNameBuffer = new StringBuilder(); + Map updColumnNameToIndex = null; + + // FIXME: What about no table? + if (this.fields[i].getOriginalTableName() != null) { + + String databaseName = this.fields[i].getDatabaseName(); + + if ((databaseName != null) && (databaseName.length() > 0)) { + tableNameBuffer.append(quotedId); + tableNameBuffer.append(databaseName); + tableNameBuffer.append(quotedId); + tableNameBuffer.append('.'); + } + + String tableOnlyName = this.fields[i].getOriginalTableName(); + + tableNameBuffer.append(quotedId); + tableNameBuffer.append(tableOnlyName); + tableNameBuffer.append(quotedId); + + String fqTableName = tableNameBuffer.toString(); + + if (!tableNamesSoFar.containsKey(fqTableName)) { + if (!tableNamesSoFar.isEmpty()) { + allTablesBuf.append(','); + } + + allTablesBuf.append(fqTableName); + tableNamesSoFar.put(fqTableName, fqTableName); + } + + columnIndicesToTable.put(Integer.valueOf(i), fqTableName); + + updColumnNameToIndex = getColumnsToIndexMapForTableAndDB(databaseName, tableOnlyName); + } else { + String tableOnlyName = this.fields[i].getTableName(); + + if (tableOnlyName != null) { + tableNameBuffer.append(quotedId); + tableNameBuffer.append(tableOnlyName); + tableNameBuffer.append(quotedId); + + String fqTableName = tableNameBuffer.toString(); + + if (!tableNamesSoFar.containsKey(fqTableName)) { + if (!tableNamesSoFar.isEmpty()) { + allTablesBuf.append(','); + } + + allTablesBuf.append(fqTableName); + tableNamesSoFar.put(fqTableName, fqTableName); + } + + columnIndicesToTable.put(Integer.valueOf(i), fqTableName); + + updColumnNameToIndex = getColumnsToIndexMapForTableAndDB(this.catalog, tableOnlyName); + } + } + + String originalColumnName = this.fields[i].getOriginalName(); + String columnName = null; + + if (this.connection.getIO().hasLongColumnInfo() && (originalColumnName != null) && (originalColumnName.length() > 0)) { + columnName = originalColumnName; + } else { + columnName = this.fields[i].getName(); + } + + if (updColumnNameToIndex != null && columnName != null) { + updColumnNameToIndex.put(columnName, Integer.valueOf(i)); + } + + String originalTableName = this.fields[i].getOriginalTableName(); + String tableName = null; + + if (this.connection.getIO().hasLongColumnInfo() && (originalTableName != null) && (originalTableName.length() > 0)) { + tableName = originalTableName; + } else { + tableName = this.fields[i].getTableName(); + } + + StringBuilder fqcnBuf = new StringBuilder(); + String databaseName = this.fields[i].getDatabaseName(); + + if (databaseName != null && databaseName.length() > 0) { + fqcnBuf.append(quotedId); + fqcnBuf.append(databaseName); + fqcnBuf.append(quotedId); + fqcnBuf.append('.'); + } + + fqcnBuf.append(quotedId); + fqcnBuf.append(tableName); + fqcnBuf.append(quotedId); + fqcnBuf.append('.'); + fqcnBuf.append(quotedId); + fqcnBuf.append(columnName); + fqcnBuf.append(quotedId); + + String qualifiedColumnName = fqcnBuf.toString(); + + if (this.fields[i].isPrimaryKey()) { + this.primaryKeyIndicies.add(Integer.valueOf(i)); + + if (!keysFirstTime) { + keyValues.append(" AND "); + } else { + keysFirstTime = false; + } + + keyValues.append(qualifiedColumnName); + keyValues.append(equalsStr); + keyValues.append("?"); + } + + if (firstTime) { + firstTime = false; + fieldValues.append("SET "); + } else { + fieldValues.append(","); + columnNames.append(","); + insertPlaceHolders.append(","); + } + + insertPlaceHolders.append("?"); + + columnNames.append(qualifiedColumnName); + + fieldValues.append(qualifiedColumnName); + fieldValues.append("=?"); + } + + this.qualifiedAndQuotedTableName = allTablesBuf.toString(); + + this.updateSQL = "UPDATE " + this.qualifiedAndQuotedTableName + " " + fieldValues.toString() + " WHERE " + keyValues.toString(); + this.insertSQL = "INSERT INTO " + this.qualifiedAndQuotedTableName + " (" + columnNames.toString() + ") VALUES (" + insertPlaceHolders.toString() + ")"; + this.refreshSQL = "SELECT " + columnNames.toString() + " FROM " + this.qualifiedAndQuotedTableName + " WHERE " + keyValues.toString(); + this.deleteSQL = "DELETE FROM " + this.qualifiedAndQuotedTableName + " WHERE " + keyValues.toString(); + } + + private Map getColumnsToIndexMapForTableAndDB(String databaseName, String tableName) { + Map nameToIndex; + Map> tablesUsedToColumnsMap = this.databasesUsedToTablesUsed.get(databaseName); + + if (tablesUsedToColumnsMap == null) { + if (this.connection.lowerCaseTableNames()) { + tablesUsedToColumnsMap = new TreeMap>(String.CASE_INSENSITIVE_ORDER); + } else { + tablesUsedToColumnsMap = new TreeMap>(); + } + + this.databasesUsedToTablesUsed.put(databaseName, tablesUsedToColumnsMap); + } + + nameToIndex = tablesUsedToColumnsMap.get(tableName); + + if (nameToIndex == null) { + nameToIndex = new HashMap(); + tablesUsedToColumnsMap.put(tableName, nameToIndex); + } + + return nameToIndex; + } + + private synchronized SingleByteCharsetConverter getCharConverter() throws SQLException { + if (!this.initializedCharConverter) { + this.initializedCharConverter = true; + + if (this.connection.getUseUnicode()) { + this.charEncoding = this.connection.getEncoding(); + this.charConverter = this.connection.getCharsetConverter(this.charEncoding); + } + } + + return this.charConverter; + } + + /** + * JDBC 2.0 Return the concurrency of this result set. The concurrency used + * is determined by the statement that created the result set. + * + * @return the concurrency type, CONCUR_READ_ONLY, etc. + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public int getConcurrency() throws SQLException { + return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY); + } + + private synchronized String getQuotedIdChar() throws SQLException { + if (this.quotedIdChar == null) { + boolean useQuotedIdentifiers = this.connection.supportsQuotedIdentifiers(); + + if (useQuotedIdentifiers) { + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + this.quotedIdChar = dbmd.getIdentifierQuoteString(); + } else { + this.quotedIdChar = ""; + } + } + + return this.quotedIdChar; + } + + /** + * JDBC 2.0 Insert the contents of the insert row into the result set and + * the database. Must be on the insert row when this method is called. + * + * @exception SQLException + * if a database-access error occurs, if called when not on + * the insert row, or if all non-nullable columns in the + * insert row have not been given a value + */ + @Override + public synchronized void insertRow() throws SQLException { + checkClosed(); + + if (!this.onInsertRow) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7"), getExceptionInterceptor()); + } + + this.inserter.executeUpdate(); + + long autoIncrementId = this.inserter.getLastInsertID(); + int numFields = this.fields.length; + byte[][] newRow = new byte[numFields][]; + + for (int i = 0; i < numFields; i++) { + if (this.inserter.isNull(i)) { + newRow[i] = null; + } else { + newRow[i] = this.inserter.getBytesRepresentation(i); + } + + // + // WARN: This non-variant only holds if MySQL never allows more than one auto-increment key (which is the way it is _today_) + // + if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) { + newRow[i] = StringUtils.getBytes(String.valueOf(autoIncrementId)); + this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]); + } + } + + ResultSetRow resultSetRow = new ByteArrayRow(newRow, getExceptionInterceptor()); + + refreshRow(this.inserter, resultSetRow); + + this.rowData.addRow(resultSetRow); + resetInserter(); + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is after the last row in the result set. + *

    + * + * @return true if after the last row, false otherwise. Returns false when + * the result set contains no rows. + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public synchronized boolean isAfterLast() throws SQLException { + return super.isAfterLast(); + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is before the first row in the result set. + *

    + * + * @return true if before the first row, false otherwise. Returns false when + * the result set contains no rows. + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public synchronized boolean isBeforeFirst() throws SQLException { + return super.isBeforeFirst(); + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is on the first row of the result set. + *

    + * + * @return true if on the first row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public synchronized boolean isFirst() throws SQLException { + return super.isFirst(); + } + + /** + * JDBC 2.0 + * + *

    + * Determine if the cursor is on the last row of the result set. Note: Calling isLast() may be expensive since the JDBC driver might need to fetch ahead one + * row in order to determine whether the current row is the last row in the result set. + *

    + * + * @return true if on the last row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs. + */ + @Override + public synchronized boolean isLast() throws SQLException { + return super.isLast(); + } + + boolean isUpdatable() { + return this.isUpdatable; + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the last row in the result set. + *

    + * + * @return true if on a valid row, false if no rows in the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWARD_ONLY. + */ + @Override + public synchronized boolean last() throws SQLException { + return super.last(); + } + + /** + * JDBC 2.0 Move the cursor to the remembered cursor position, usually the + * current row. Has no effect unless the cursor is on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or the result set is + * not updatable + * @throws SQLException + * if the ResultSet is not updatable or some other error occurs + */ + @Override + public synchronized void moveToCurrentRow() throws SQLException { + checkClosed(); + + if (!this.isUpdatable) { + throw new NotUpdatable(this.notUpdatableReason); + } + + if (this.onInsertRow) { + this.onInsertRow = false; + this.thisRow = this.savedCurrentRow; + } + } + + /** + * JDBC 2.0 Move to the insert row. The current cursor position is + * remembered while the cursor is positioned on the insert row. The insert + * row is a special row associated with an updatable result set. It is + * essentially a buffer where a new row may be constructed by calling the + * updateXXX() methods prior to inserting the row into the result set. Only + * the updateXXX(), getXXX(), and insertRow() methods may be called when the + * cursor is on the insert row. All of the columns in a result set must be + * given a value each time this method is called before calling insertRow(). + * UpdateXXX()must be called before getXXX() on a column. + * + * @exception SQLException + * if a database-access error occurs, or the result set is + * not updatable + * @throws NotUpdatable + */ + @Override + public synchronized void moveToInsertRow() throws SQLException { + checkClosed(); + + if (!this.isUpdatable) { + throw new NotUpdatable(this.notUpdatableReason); + } + + if (this.inserter == null) { + if (this.insertSQL == null) { + generateStatements(); + } + + this.inserter = (PreparedStatement) this.connection.clientPrepareStatement(this.insertSQL); + if (this.populateInserterWithDefaultValues) { + extractDefaultValues(); + } + + resetInserter(); + } else { + resetInserter(); + } + + int numFields = this.fields.length; + + this.onInsertRow = true; + this.doingUpdates = false; + this.savedCurrentRow = this.thisRow; + byte[][] newRowData = new byte[numFields][]; + this.thisRow = new ByteArrayRow(newRowData, getExceptionInterceptor()); + this.thisRow.setMetadata(this.fields); + + for (int i = 0; i < numFields; i++) { + if (!this.populateInserterWithDefaultValues) { + this.inserter.setBytesNoEscapeNoQuotes(i + 1, StringUtils.getBytes("DEFAULT")); + newRowData = null; + } else { + if (this.defaultColumnValue[i] != null) { + Field f = this.fields[i]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_DATE: + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_NEWDATE: + case MysqlDefs.FIELD_TYPE_TIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + + if (this.defaultColumnValue[i].length > 7 && this.defaultColumnValue[i][0] == (byte) 'C' + && this.defaultColumnValue[i][1] == (byte) 'U' && this.defaultColumnValue[i][2] == (byte) 'R' + && this.defaultColumnValue[i][3] == (byte) 'R' && this.defaultColumnValue[i][4] == (byte) 'E' + && this.defaultColumnValue[i][5] == (byte) 'N' && this.defaultColumnValue[i][6] == (byte) 'T' + && this.defaultColumnValue[i][7] == (byte) '_') { + this.inserter.setBytesNoEscapeNoQuotes(i + 1, this.defaultColumnValue[i]); + + break; + } + this.inserter.setBytes(i + 1, this.defaultColumnValue[i], false, false); + break; + + default: + this.inserter.setBytes(i + 1, this.defaultColumnValue[i], false, false); + } + + // This value _could_ be changed from a getBytes(), so we need a copy.... + byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length]; + System.arraycopy(this.defaultColumnValue[i], 0, defaultValueCopy, 0, defaultValueCopy.length); + newRowData[i] = defaultValueCopy; + } else { + this.inserter.setNull(i + 1, java.sql.Types.NULL); + newRowData[i] = null; + } + } + } + } + + // --------------------------------------------------------------------- + // Updates + // --------------------------------------------------------------------- + + /** + * A ResultSet is initially positioned before its first row, the first call + * to next makes the first row the current row; the second call makes the + * second row the current row, etc. + * + *

    + * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read + *

    + * + * @return true if the new current is valid; false if there are no more rows + * + * @exception SQLException + * if a database access error occurs + */ + @Override + public synchronized boolean next() throws SQLException { + return super.next(); + } + + /** + * The prev method is not part of JDBC, but because of the architecture of + * this driver it is possible to move both forward and backward within the + * result set. + * + *

    + * If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read + *

    + * + * @return true if the new current is valid; false if there are no more rows + * + * @exception SQLException + * if a database access error occurs + */ + @Override + public synchronized boolean prev() throws SQLException { + return super.prev(); + } + + /** + * JDBC 2.0 + * + *

    + * Moves to the previous row in the result set. + *

    + * + *

    + * Note: previous() is not the same as relative(-1) since it makes sense to call previous() when there is no current row. + *

    + * + * @return true if on a valid row, false if off the result set. + * + * @exception SQLException + * if a database-access error occurs, or result set type is + * TYPE_FORWAR_DONLY. + */ + @Override + public synchronized boolean previous() throws SQLException { + return super.previous(); + } + + /** + * Closes this ResultSet and releases resources. + * + * @param calledExplicitly + * was realClose called by the standard ResultSet.close() method, or was it closed internally by the + * driver? + * + * @throws SQLException + * if an error occurs. + */ + @Override + public synchronized void realClose(boolean calledExplicitly) throws SQLException { + if (this.isClosed) { + return; + } + + SQLException sqlEx = null; + + if (this.useUsageAdvisor) { + if ((this.deleter == null) && (this.inserter == null) && (this.refresher == null) && (this.updater == null)) { + this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); + + String message = Messages.getString("UpdatableResultSet.34"); + + this.eventSink.consumeEvent( + new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + try { + if (this.deleter != null) { + this.deleter.close(); + } + } catch (SQLException ex) { + sqlEx = ex; + } + + try { + if (this.inserter != null) { + this.inserter.close(); + } + } catch (SQLException ex) { + sqlEx = ex; + } + + try { + if (this.refresher != null) { + this.refresher.close(); + } + } catch (SQLException ex) { + sqlEx = ex; + } + + try { + if (this.updater != null) { + this.updater.close(); + } + } catch (SQLException ex) { + sqlEx = ex; + } + + super.realClose(calledExplicitly); + + if (sqlEx != null) { + throw sqlEx; + } + } + + /** + * JDBC 2.0 Refresh the value of the current row with its current value in + * the database. Cannot be called when on the insert row. The refreshRow() + * method provides a way for an application to explicitly tell the JDBC + * driver to refetch a row(s) from the database. An application may want to + * call refreshRow() when caching or prefetching is being done by the JDBC + * driver to fetch the latest value of a row from the database. The JDBC + * driver may actually refresh multiple rows at once if the fetch size is + * greater than one. All values are refetched subject to the transaction + * isolation level and cursor sensitivity. If refreshRow() is called after + * calling updateXXX(), but before calling updateRow() then the updates made + * to the row are lost. Calling refreshRow() frequently will likely slow + * performance. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws NotUpdatable + */ + @Override + public synchronized void refreshRow() throws SQLException { + checkClosed(); + + if (!this.isUpdatable) { + throw new NotUpdatable(); + } + + if (this.onInsertRow) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.8"), getExceptionInterceptor()); + } else if (this.rowData.size() == 0) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.9"), getExceptionInterceptor()); + } else if (isBeforeFirst()) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.10"), getExceptionInterceptor()); + } else if (isAfterLast()) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11"), getExceptionInterceptor()); + } + + refreshRow(this.updater, this.thisRow); + } + + private synchronized void refreshRow(PreparedStatement updateInsertStmt, ResultSetRow rowToRefresh) throws SQLException { + if (this.refresher == null) { + if (this.refreshSQL == null) { + generateStatements(); + } + + this.refresher = (PreparedStatement) this.connection.clientPrepareStatement(this.refreshSQL); + } + + this.refresher.clearParameters(); + + int numKeys = this.primaryKeyIndicies.size(); + + if (numKeys == 1) { + byte[] dataFrom = null; + int index = this.primaryKeyIndicies.get(0).intValue(); + + if (!this.doingUpdates && !this.onInsertRow) { + dataFrom = rowToRefresh.getColumnValue(index); + } else { + dataFrom = updateInsertStmt.getBytesRepresentation(index); + + // Primary keys not set? + if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) { + dataFrom = rowToRefresh.getColumnValue(index); + } else { + dataFrom = stripBinaryPrefix(dataFrom); + } + } + + if (this.fields[index].getvalueNeedsQuoting()) { + this.refresher.setBytesNoEscape(1, dataFrom); + } else { + this.refresher.setBytesNoEscapeNoQuotes(1, dataFrom); + } + + } else { + for (int i = 0; i < numKeys; i++) { + byte[] dataFrom = null; + int index = this.primaryKeyIndicies.get(i).intValue(); + + if (!this.doingUpdates && !this.onInsertRow) { + dataFrom = rowToRefresh.getColumnValue(index); + } else { + dataFrom = updateInsertStmt.getBytesRepresentation(index); + + // Primary keys not set? + if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) { + dataFrom = rowToRefresh.getColumnValue(index); + } else { + dataFrom = stripBinaryPrefix(dataFrom); + } + } + + this.refresher.setBytesNoEscape(i + 1, dataFrom); + } + } + + java.sql.ResultSet rs = null; + + try { + rs = this.refresher.executeQuery(); + + int numCols = rs.getMetaData().getColumnCount(); + + if (rs.next()) { + for (int i = 0; i < numCols; i++) { + byte[] val = rs.getBytes(i + 1); + + if ((val == null) || rs.wasNull()) { + rowToRefresh.setColumnValue(i, null); + } else { + rowToRefresh.setColumnValue(i, rs.getBytes(i + 1)); + } + } + } else { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.12"), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException ex) { + // ignore + } + } + } + } + + /** + * JDBC 2.0 + * + *

    + * Moves a relative number of rows, either positive or negative. Attempting to move beyond the first/last row in the result set positions the cursor + * before/after the the first/last row. Calling relative(0) is valid, but does not change the cursor position. + *

    + * + *

    + * Note: Calling relative(1) is different than calling next() since is makes sense to call next() when there is no current row, for example, when the cursor + * is positioned before the first row or after the last row of the result set. + *

    + * + * @param rows + * + * @return true if on a row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs, or there is no current + * row, or result set type is TYPE_FORWARD_ONLY. + */ + @Override + public synchronized boolean relative(int rows) throws SQLException { + return super.relative(rows); + } + + private void resetInserter() throws SQLException { + this.inserter.clearParameters(); + + for (int i = 0; i < this.fields.length; i++) { + this.inserter.setNull(i + 1, 0); + } + } + + /** + * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave + * a visible "hole" in a result set. This method can be used to detect holes + * in a result set. The value returned depends on whether or not the result + * set can detect deletions. + * + * @return true if deleted and deletes are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#deletesAreDetected + */ + @Override + public synchronized boolean rowDeleted() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Determine if the current row has been inserted. The value + * returned depends on whether or not the result set can detect visible + * inserts. + * + * @return true if inserted and inserts are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#insertsAreDetected + */ + @Override + public synchronized boolean rowInserted() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * JDBC 2.0 Determine if the current row has been updated. The value + * returned depends on whether or not the result set can detect updates. + * + * @return true if the row has been visibly updated by the owner or another, + * and updates are detected + * + * @exception SQLException + * if a database-access error occurs + * @throws NotImplemented + * + * @see DatabaseMetaData#updatesAreDetected + */ + @Override + public synchronized boolean rowUpdated() throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Sets the concurrency type of this result set + * + * @param concurrencyFlag + * the type of concurrency that this ResultSet should support. + */ + @Override + protected void setResultSetConcurrency(int concurrencyFlag) { + super.setResultSetConcurrency(concurrencyFlag); + + // + // FIXME: Issue warning when asked for updateable result set, but result + // set is not + // updatable + // + // if ((concurrencyFlag == CONCUR_UPDATABLE) && !isUpdatable()) { + // java.sql.SQLWarning warning = new java.sql.SQLWarning( + // NotUpdatable.NOT_UPDATEABLE_MESSAGE); + // } + } + + private byte[] stripBinaryPrefix(byte[] dataFrom) { + return StringUtils.stripEnclosure(dataFrom, "_binary'", "'"); + } + + /** + * Reset UPDATE prepared statement to value in current row. This_Row MUST + * point to current, valid row. + * + * @throws SQLException + */ + protected synchronized void syncUpdate() throws SQLException { + if (this.updater == null) { + if (this.updateSQL == null) { + generateStatements(); + } + + this.updater = (PreparedStatement) this.connection.clientPrepareStatement(this.updateSQL); + } + + int numFields = this.fields.length; + this.updater.clearParameters(); + + for (int i = 0; i < numFields; i++) { + if (this.thisRow.getColumnValue(i) != null) { + + if (this.fields[i].getvalueNeedsQuoting()) { + this.updater.setBytes(i + 1, this.thisRow.getColumnValue(i), this.fields[i].isBinary(), false); + } else { + this.updater.setBytesNoEscapeNoQuotes(i + 1, this.thisRow.getColumnValue(i)); + } + } else { + this.updater.setNull(i + 1, 0); + } + } + + int numKeys = this.primaryKeyIndicies.size(); + + if (numKeys == 1) { + int index = this.primaryKeyIndicies.get(0).intValue(); + this.setParamValue(this.updater, numFields + 1, this.thisRow, index, this.fields[index].getSQLType()); + } else { + for (int i = 0; i < numKeys; i++) { + int idx = this.primaryKeyIndicies.get(i).intValue(); + this.setParamValue(this.updater, numFields + i + 1, this.thisRow, idx, this.fields[idx].getSQLType()); + } + } + } + + /** + * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setAsciiStream(columnIndex, x, length); + } else { + this.inserter.setAsciiStream(columnIndex, x, length); + this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); + } + } + + /** + * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException { + updateAsciiStream(findColumn(columnName), x, length); + } + + /** + * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setBigDecimal(columnIndex, x); + } else { + this.inserter.setBigDecimal(columnIndex, x); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x.toString())); + } + } + } + + /** + * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { + updateBigDecimal(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a binary stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setBinaryStream(columnIndex, x, length); + } else { + this.inserter.setBinaryStream(columnIndex, x, length); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); + } + } + } + + /** + * JDBC 2.0 Update a column with a binary stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException { + updateBinaryStream(findColumn(columnName), x, length); + } + + /** + * @see ResultSetInternalMethods#updateBlob(int, Blob) + */ + @Override + public synchronized void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setBlob(columnIndex, blob); + } else { + this.inserter.setBlob(columnIndex, blob); + + if (blob == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); + } + } + } + + /** + * @see ResultSetInternalMethods#updateBlob(String, Blob) + */ + @Override + public synchronized void updateBlob(String columnName, java.sql.Blob blob) throws SQLException { + updateBlob(findColumn(columnName), blob); + } + + /** + * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setBoolean(columnIndex, x); + } else { + this.inserter.setBoolean(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBoolean(String columnName, boolean x) throws SQLException { + updateBoolean(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateByte(int columnIndex, byte x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setByte(columnIndex, x); + } else { + this.inserter.setByte(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateByte(String columnName, byte x) throws SQLException { + updateByte(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setBytes(columnIndex, x); + } else { + this.inserter.setBytes(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, x); + } + } + + /** + * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateBytes(String columnName, byte[] x) throws SQLException { + updateBytes(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param length + * the length of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setCharacterStream(columnIndex, x, length); + } else { + this.inserter.setCharacterStream(columnIndex, x, length); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); + } + } + } + + /** + * JDBC 2.0 Update a column with a character stream value. The updateXXX() + * methods are used to update column values in the current row, or the + * insert row. The updateXXX() methods do not update the underlying + * database, instead the updateRow() or insertRow() methods are called to + * update the database. + * + * @param columnName + * the name of the column + * @param reader + * the new column value + * @param length + * of the stream + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { + updateCharacterStream(findColumn(columnName), reader, length); + } + + /** + * @see ResultSetInternalMethods#updateClob(int, Clob) + */ + @Override + public void updateClob(int columnIndex, java.sql.Clob clob) throws SQLException { + if (clob == null) { + updateNull(columnIndex); + } else { + updateCharacterStream(columnIndex, clob.getCharacterStream(), (int) clob.length()); + } + } + + /** + * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setDate(columnIndex, x); + } else { + this.inserter.setDate(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateDate(String columnName, java.sql.Date x) throws SQLException { + updateDate(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Double value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateDouble(int columnIndex, double x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setDouble(columnIndex, x); + } else { + this.inserter.setDouble(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a double value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateDouble(String columnName, double x) throws SQLException { + updateDouble(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a float value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateFloat(int columnIndex, float x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setFloat(columnIndex, x); + } else { + this.inserter.setFloat(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a float value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateFloat(String columnName, float x) throws SQLException { + updateFloat(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with an integer value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateInt(int columnIndex, int x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setInt(columnIndex, x); + } else { + this.inserter.setInt(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with an integer value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateInt(String columnName, int x) throws SQLException { + updateInt(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a long value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateLong(int columnIndex, long x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setLong(columnIndex, x); + } else { + this.inserter.setLong(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a long value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateLong(String columnName, long x) throws SQLException { + updateLong(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Give a nullable column a null value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateNull(int columnIndex) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setNull(columnIndex, 0); + } else { + this.inserter.setNull(columnIndex, 0); + + this.thisRow.setColumnValue(columnIndex - 1, null); + } + } + + /** + * JDBC 2.0 Update a column with a null value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateNull(String columnName) throws SQLException { + updateNull(findColumn(columnName)); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateObject(int columnIndex, Object x) throws SQLException { + updateObjectInternal(columnIndex, x, null, 0); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * @param scale + * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException { + updateObjectInternal(columnIndex, x, null, scale); + } + + /** + * Internal setObject implementation. Although targetType is not part of default ResultSet methods signatures, it is used for type conversions from + * JDBC42UpdatableResultSet new JDBC 4.2 updateObject() methods. + * + * @param columnIndex + * @param x + * @param targetType + * @param scaleOrLength + * @throws SQLException + */ + protected synchronized void updateObjectInternal(int columnIndex, Object x, Integer targetType, int scaleOrLength) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + if (targetType == null) { + this.updater.setObject(columnIndex, x); + } else { + this.updater.setObject(columnIndex, x, targetType); + } + } else { + if (targetType == null) { + this.inserter.setObject(columnIndex, x); + } else { + this.inserter.setObject(columnIndex, x, targetType); + } + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateObject(String columnName, Object x) throws SQLException { + updateObject(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with an Object value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * @param scale + * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateObject(String columnName, Object x, int scale) throws SQLException { + updateObject(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update the underlying database with the new contents of the + * current row. Cannot be called when on the insert row. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row + * @throws NotUpdatable + */ + @Override + public synchronized void updateRow() throws SQLException { + if (!this.isUpdatable) { + throw new NotUpdatable(this.notUpdatableReason); + } + + if (this.doingUpdates) { + this.updater.executeUpdate(); + refreshRow(); + this.doingUpdates = false; + } else if (this.onInsertRow) { + throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.44"), getExceptionInterceptor()); + } + + // + // fixes calling updateRow() and then doing more + // updates on same row... + syncUpdate(); + } + + /** + * JDBC 2.0 Update a column with a short value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateShort(int columnIndex, short x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setShort(columnIndex, x); + } else { + this.inserter.setShort(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a short value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateShort(String columnName, short x) throws SQLException { + updateShort(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a String value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateString(int columnIndex, String x) throws SQLException { + checkClosed(); + + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setString(columnIndex, x); + } else { + this.inserter.setString(columnIndex, x); + + if (x == null) { + this.thisRow.setColumnValue(columnIndex - 1, null); + } else { + if (getCharConverter() != null) { + this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x, this.charConverter, this.charEncoding, + this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor())); + } else { + this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x)); + } + } + } + } + + /** + * JDBC 2.0 Update a column with a String value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateString(String columnName, String x) throws SQLException { + updateString(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateTime(int columnIndex, java.sql.Time x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setTime(columnIndex, x); + } else { + this.inserter.setTime(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are + * used to update column values in the current row, or the insert row. The + * updateXXX() methods do not update the underlying database, instead the + * updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateTime(String columnName, java.sql.Time x) throws SQLException { + updateTime(findColumn(columnName), x); + } + + /** + * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException { + if (!this.onInsertRow) { + if (!this.doingUpdates) { + this.doingUpdates = true; + syncUpdate(); + } + + this.updater.setTimestamp(columnIndex, x); + } else { + this.inserter.setTimestamp(columnIndex, x); + + this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); + } + } + + /** + * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods + * are used to update column values in the current row, or the insert row. + * The updateXXX() methods do not update the underlying database, instead + * the updateRow() or insertRow() methods are called to update the database. + * + * @param columnName + * the name of the column + * @param x + * the new column value + * + * @exception SQLException + * if a database-access error occurs + */ + @Override + public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { + updateTimestamp(findColumn(columnName), x); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Util.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Util.java new file mode 100644 index 0000000..b1e4bfe --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Util.java @@ -0,0 +1,702 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ObjectInputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Various utility methods for the driver. + */ +public class Util { + class RandStructcture { + long maxValue; + + double maxValueDbl; + + long seed1; + + long seed2; + } + + private static Util enclosingInstance = new Util(); + + private static boolean isJdbc4; + + private static boolean isJdbc42; + + private static int jvmVersion = -1; + + private static int jvmUpdateNumber = -1; + + private static boolean isColdFusion = false; + + static { + try { + Class.forName("java.sql.NClob"); + isJdbc4 = true; + } catch (ClassNotFoundException e) { + isJdbc4 = false; + } + + try { + Class.forName("java.sql.JDBCType"); + isJdbc42 = true; + } catch (Throwable t) { + isJdbc42 = false; + } + + String jvmVersionString = System.getProperty("java.version"); + int startPos = jvmVersionString.indexOf('.'); + int endPos = startPos + 1; + if (startPos != -1) { + while (Character.isDigit(jvmVersionString.charAt(endPos)) && ++endPos < jvmVersionString.length()) { + // continue + } + } + startPos++; + if (endPos > startPos) { + jvmVersion = Integer.parseInt(jvmVersionString.substring(startPos, endPos)); + } else { + // use best approximate value + jvmVersion = isJdbc42 ? 8 : isJdbc4 ? 6 : 5; + } + startPos = jvmVersionString.indexOf("_"); + endPos = startPos + 1; + if (startPos != -1) { + while (Character.isDigit(jvmVersionString.charAt(endPos)) && ++endPos < jvmVersionString.length()) { + // continue + } + } + startPos++; + if (endPos > startPos) { + jvmUpdateNumber = Integer.parseInt(jvmVersionString.substring(startPos, endPos)); + } + + // + // Detect the ColdFusion MX environment + // + // Unfortunately, no easy-to-discern classes are available to our classloader to check... + // + + String loadedFrom = stackTraceToString(new Throwable()); + + if (loadedFrom != null) { + isColdFusion = loadedFrom.indexOf("coldfusion") != -1; + } else { + isColdFusion = false; + } + } + + public static boolean isJdbc4() { + return isJdbc4; + } + + public static boolean isJdbc42() { + return isJdbc42; + } + + public static int getJVMVersion() { + return jvmVersion; + } + + public static boolean jvmMeetsMinimum(int version, int updateNumber) { + return getJVMVersion() > version || getJVMVersion() == version && getJVMUpdateNumber() >= updateNumber; + } + + public static int getJVMUpdateNumber() { + return jvmUpdateNumber; + } + + public static boolean isColdFusion() { + return isColdFusion; + } + + /** + * Checks whether the given server version string is a MySQL Community edition + */ + public static boolean isCommunityEdition(String serverVersion) { + return !isEnterpriseEdition(serverVersion); + } + + /** + * Checks whether the given server version string is a MySQL Enterprise edition + */ + public static boolean isEnterpriseEdition(String serverVersion) { + return serverVersion.contains("enterprise") || serverVersion.contains("commercial") || serverVersion.contains("advanced"); + } + + // Right from Monty's code + public static String newCrypt(String password, String seed, String encoding) { + byte b; + double d; + + if ((password == null) || (password.length() == 0)) { + return password; + } + + long[] pw = newHash(seed.getBytes()); + long[] msg = hashPre41Password(password, encoding); + long max = 0x3fffffffL; + long seed1 = (pw[0] ^ msg[0]) % max; + long seed2 = (pw[1] ^ msg[1]) % max; + char[] chars = new char[seed.length()]; + + for (int i = 0; i < seed.length(); i++) { + seed1 = ((seed1 * 3) + seed2) % max; + seed2 = (seed1 + seed2 + 33) % max; + d = (double) seed1 / (double) max; + b = (byte) java.lang.Math.floor((d * 31) + 64); + chars[i] = (char) b; + } + + seed1 = ((seed1 * 3) + seed2) % max; + seed2 = (seed1 + seed2 + 33) % max; + d = (double) seed1 / (double) max; + b = (byte) java.lang.Math.floor(d * 31); + + for (int i = 0; i < seed.length(); i++) { + chars[i] ^= (char) b; + } + + return new String(chars); + } + + public static long[] hashPre41Password(String password, String encoding) { + // remove white spaces and convert to bytes + try { + return newHash(password.replaceAll("\\s", "").getBytes(encoding)); + } catch (UnsupportedEncodingException e) { + return new long[0]; + } + } + + public static long[] hashPre41Password(String password) { + return hashPre41Password(password, Charset.defaultCharset().name()); + } + + static long[] newHash(byte[] password) { + long nr = 1345345333L; + long add = 7; + long nr2 = 0x12345671L; + long tmp; + + for (byte b : password) { + tmp = 0xff & b; + nr ^= ((((nr & 63) + add) * tmp) + (nr << 8)); + nr2 += ((nr2 << 8) ^ nr); + add += tmp; + } + + long[] result = new long[2]; + result[0] = nr & 0x7fffffffL; + result[1] = nr2 & 0x7fffffffL; + + return result; + } + + public static String oldCrypt(String password, String seed) { + long hp; + long hm; + long s1; + long s2; + long max = 0x01FFFFFF; + double d; + byte b; + + if ((password == null) || (password.length() == 0)) { + return password; + } + + hp = oldHash(seed); + hm = oldHash(password); + + long nr = hp ^ hm; + nr %= max; + s1 = nr; + s2 = nr / 2; + + char[] chars = new char[seed.length()]; + + for (int i = 0; i < seed.length(); i++) { + s1 = ((s1 * 3) + s2) % max; + s2 = (s1 + s2 + 33) % max; + d = (double) s1 / max; + b = (byte) java.lang.Math.floor((d * 31) + 64); + chars[i] = (char) b; + } + + return new String(chars); + } + + static long oldHash(String password) { + long nr = 1345345333; + long nr2 = 7; + long tmp; + + for (int i = 0; i < password.length(); i++) { + if ((password.charAt(i) == ' ') || (password.charAt(i) == '\t')) { + continue; + } + + tmp = password.charAt(i); + nr ^= ((((nr & 63) + nr2) * tmp) + (nr << 8)); + nr2 += tmp; + } + + return nr & ((1L << 31) - 1L); + } + + private static RandStructcture randomInit(long seed1, long seed2) { + RandStructcture randStruct = enclosingInstance.new RandStructcture(); + + randStruct.maxValue = 0x3FFFFFFFL; + randStruct.maxValueDbl = randStruct.maxValue; + randStruct.seed1 = seed1 % randStruct.maxValue; + randStruct.seed2 = seed2 % randStruct.maxValue; + + return randStruct; + } + + /** + * Given a ResultSet and an index into the columns of that ResultSet, read + * binary data from the column which represents a serialized object, and + * re-create the object. + * + * @param resultSet + * the ResultSet to use. + * @param index + * an index into the ResultSet. + * @return the object if it can be de-serialized + * @throws Exception + * if an error occurs + */ + public static Object readObject(java.sql.ResultSet resultSet, int index) throws Exception { + ObjectInputStream objIn = new ObjectInputStream(resultSet.getBinaryStream(index)); + Object obj = objIn.readObject(); + objIn.close(); + + return obj; + } + + private static double rnd(RandStructcture randStruct) { + randStruct.seed1 = ((randStruct.seed1 * 3) + randStruct.seed2) % randStruct.maxValue; + randStruct.seed2 = (randStruct.seed1 + randStruct.seed2 + 33) % randStruct.maxValue; + + return ((randStruct.seed1) / randStruct.maxValueDbl); + } + + /** + * @param message + * @param password + */ + public static String scramble(String message, String password) { + long[] hashPass; + long[] hashMessage; + byte[] to = new byte[8]; + String val = ""; + + message = message.substring(0, 8); + + if ((password != null) && (password.length() > 0)) { + hashPass = hashPre41Password(password); + hashMessage = newHash(message.getBytes()); + + RandStructcture randStruct = randomInit(hashPass[0] ^ hashMessage[0], hashPass[1] ^ hashMessage[1]); + + int msgPos = 0; + int msgLength = message.length(); + int toPos = 0; + + while (msgPos++ < msgLength) { + to[toPos++] = (byte) (Math.floor(rnd(randStruct) * 31) + 64); + } + + /* Make it harder to break */ + byte extra = (byte) (Math.floor(rnd(randStruct) * 31)); + + for (int i = 0; i < to.length; i++) { + to[i] ^= extra; + } + + val = StringUtils.toString(to); + } + + return val; + } + + /** + * Converts a nested exception into a nicer message + * + * @param ex + * the exception to expand into a message. + * + * @return a message containing the exception, the message (if any), and a + * stacktrace. + */ + public static String stackTraceToString(Throwable ex) { + StringBuilder traceBuf = new StringBuilder(); + traceBuf.append(Messages.getString("Util.1")); + + if (ex != null) { + traceBuf.append(ex.getClass().getName()); + + String message = ex.getMessage(); + + if (message != null) { + traceBuf.append(Messages.getString("Util.2")); + traceBuf.append(message); + } + + StringWriter out = new StringWriter(); + + PrintWriter printOut = new PrintWriter(out); + + ex.printStackTrace(printOut); + + traceBuf.append(Messages.getString("Util.3")); + traceBuf.append(out.toString()); + } + + traceBuf.append(Messages.getString("Util.4")); + + return traceBuf.toString(); + } + + public static Object getInstance(String className, Class[] argTypes, Object[] args, ExceptionInterceptor exceptionInterceptor) throws SQLException { + + try { + return handleNewInstance(Class.forName(className).getConstructor(argTypes), args, exceptionInterceptor); + } catch (SecurityException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } catch (NoSuchMethodException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } catch (ClassNotFoundException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } + } + + /** + * Handles constructing new instance with the given constructor and wrapping + * (or not, as required) the exceptions that could possibly be generated + */ + public static final Object handleNewInstance(Constructor ctor, Object[] args, ExceptionInterceptor exceptionInterceptor) throws SQLException { + try { + + return ctor.newInstance(args); + } catch (IllegalArgumentException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } catch (InstantiationException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } catch (IllegalAccessException e) { + throw SQLError.createSQLException("Can't instantiate required class", SQLError.SQL_STATE_GENERAL_ERROR, e, exceptionInterceptor); + } catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + + if (target instanceof SQLException) { + throw (SQLException) target; + } + + if (target instanceof ExceptionInInitializerError) { + target = ((ExceptionInInitializerError) target).getException(); + } + + throw SQLError.createSQLException(target.toString(), SQLError.SQL_STATE_GENERAL_ERROR, target, exceptionInterceptor); + } + } + + /** + * Does a network interface exist locally with the given hostname? + * + * @param hostname + * the hostname (or IP address in string form) to check + * @return true if it exists, false if no, or unable to determine due to VM + * version support of java.net.NetworkInterface + */ + public static boolean interfaceExists(String hostname) { + try { + Class networkInterfaceClass = Class.forName("java.net.NetworkInterface"); + return networkInterfaceClass.getMethod("getByName", (Class[]) null).invoke(networkInterfaceClass, new Object[] { hostname }) != null; + } catch (Throwable t) { + return false; + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void resultSetToMap(Map mappedValues, java.sql.ResultSet rs) throws SQLException { + while (rs.next()) { + mappedValues.put(rs.getObject(1), rs.getObject(2)); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void resultSetToMap(Map mappedValues, java.sql.ResultSet rs, int key, int value) throws SQLException { + while (rs.next()) { + mappedValues.put(rs.getObject(key), rs.getObject(value)); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void resultSetToMap(Map mappedValues, java.sql.ResultSet rs, String key, String value) throws SQLException { + while (rs.next()) { + mappedValues.put(rs.getObject(key), rs.getObject(value)); + } + } + + public static Map calculateDifferences(Map map1, Map map2) { + Map diffMap = new HashMap(); + + for (Map.Entry entry : map1.entrySet()) { + Object key = entry.getKey(); + + Number value1 = null; + Number value2 = null; + + if (entry.getValue() instanceof Number) { + + value1 = (Number) entry.getValue(); + value2 = (Number) map2.get(key); + } else { + try { + value1 = new Double(entry.getValue().toString()); + value2 = new Double(map2.get(key).toString()); + } catch (NumberFormatException nfe) { + continue; + } + } + + if (value1.equals(value2)) { + continue; + } + + if (value1 instanceof Byte) { + diffMap.put(key, Byte.valueOf((byte) (((Byte) value2).byteValue() - ((Byte) value1).byteValue()))); + } else if (value1 instanceof Short) { + diffMap.put(key, Short.valueOf((short) (((Short) value2).shortValue() - ((Short) value1).shortValue()))); + } else if (value1 instanceof Integer) { + diffMap.put(key, Integer.valueOf((((Integer) value2).intValue() - ((Integer) value1).intValue()))); + } else if (value1 instanceof Long) { + diffMap.put(key, Long.valueOf((((Long) value2).longValue() - ((Long) value1).longValue()))); + } else if (value1 instanceof Float) { + diffMap.put(key, Float.valueOf(((Float) value2).floatValue() - ((Float) value1).floatValue())); + } else if (value1 instanceof Double) { + diffMap.put(key, Double.valueOf((((Double) value2).shortValue() - ((Double) value1).shortValue()))); + } else if (value1 instanceof BigDecimal) { + diffMap.put(key, ((BigDecimal) value2).subtract((BigDecimal) value1)); + } else if (value1 instanceof BigInteger) { + diffMap.put(key, ((BigInteger) value2).subtract((BigInteger) value1)); + } + } + + return diffMap; + } + + /** + * Returns initialized instances of classes listed in extensionClassNames. + * There is no need to call Extension.init() method after that if you don't change connection or properties. + * + * @param conn + * @param props + * @param extensionClassNames + * @param errorMessageKey + * @param exceptionInterceptor + * @throws SQLException + */ + public static List loadExtensions(Connection conn, Properties props, String extensionClassNames, String errorMessageKey, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + List extensionList = new LinkedList(); + + List interceptorsToCreate = StringUtils.split(extensionClassNames, ",", true); + + String className = null; + + try { + for (int i = 0, s = interceptorsToCreate.size(); i < s; i++) { + className = interceptorsToCreate.get(i); + Extension extensionInstance = (Extension) Class.forName(className).newInstance(); + extensionInstance.init(conn, props); + + extensionList.add(extensionInstance); + } + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString(errorMessageKey, new Object[] { className }), exceptionInterceptor); + sqlEx.initCause(t); + + throw sqlEx; + } + + return extensionList; + } + + /** Cache for the JDBC interfaces already verified */ + private static final ConcurrentMap, Boolean> isJdbcInterfaceCache = new ConcurrentHashMap, Boolean>(); + + /** + * Recursively checks for interfaces on the given class to determine if it implements a java.sql, javax.sql or com.mysql.jdbc interface. + * + * @param clazz + * The class to investigate. + */ + public static boolean isJdbcInterface(Class clazz) { + if (Util.isJdbcInterfaceCache.containsKey(clazz)) { + return (Util.isJdbcInterfaceCache.get(clazz)); + } + + if (clazz.isInterface()) { + try { + if (isJdbcPackage(clazz.getPackage().getName())) { + Util.isJdbcInterfaceCache.putIfAbsent(clazz, true); + return true; + } + } catch (Exception ex) { + /* + * We may experience a NPE from getPackage() returning null, or class-loading facilities. + * This happens when this class is instrumented to implement runtime-generated interfaces. + */ + } + } + + for (Class iface : clazz.getInterfaces()) { + if (isJdbcInterface(iface)) { + Util.isJdbcInterfaceCache.putIfAbsent(clazz, true); + return true; + } + } + + if (clazz.getSuperclass() != null && isJdbcInterface(clazz.getSuperclass())) { + Util.isJdbcInterfaceCache.putIfAbsent(clazz, true); + return true; + } + + Util.isJdbcInterfaceCache.putIfAbsent(clazz, false); + return false; + } + + /** Main MySQL JDBC package name */ + private static final String MYSQL_JDBC_PACKAGE_ROOT; + + static { + String packageName = MultiHostConnectionProxy.class.getPackage().getName(); + // assume that packageName includes "jdbc" + MYSQL_JDBC_PACKAGE_ROOT = packageName.substring(0, packageName.indexOf("jdbc") + 4); + } + + /** + * Check if the package name is a known JDBC package. + * + * @param packageName + * The package name to check. + */ + public static boolean isJdbcPackage(String packageName) { + return packageName != null + && (packageName.startsWith("java.sql") || packageName.startsWith("javax.sql") || packageName.startsWith(MYSQL_JDBC_PACKAGE_ROOT)); + } + + /** Cache for the implemented interfaces searched. */ + private static final ConcurrentMap, Class[]> implementedInterfacesCache = new ConcurrentHashMap, Class[]>(); + + /** + * Retrieves a list with all interfaces implemented by the given class. If possible gets this information from a cache instead of navigating through the + * object hierarchy. Results are stored in a cache for future reference. + * + * @param clazz + * The class from which the interface list will be retrieved. + * @return + * An array with all the interfaces for the given class. + */ + public static Class[] getImplementedInterfaces(Class clazz) { + Class[] implementedInterfaces = Util.implementedInterfacesCache.get(clazz); + if (implementedInterfaces != null) { + return implementedInterfaces; + } + + Set> interfaces = new LinkedHashSet>(); + Class superClass = clazz; + do { + Collections.addAll(interfaces, (Class[]) superClass.getInterfaces()); + } while ((superClass = superClass.getSuperclass()) != null); + + implementedInterfaces = interfaces.toArray(new Class[interfaces.size()]); + Class[] oldValue = Util.implementedInterfacesCache.putIfAbsent(clazz, implementedInterfaces); + if (oldValue != null) { + implementedInterfaces = oldValue; + } + return implementedInterfaces; + } + + /** + * Computes the number of seconds elapsed since the given time in milliseconds. + * + * @param timeInMillis + * The past instant in milliseconds. + * @return + * The number of seconds, truncated, elapsed since timeInMillis. + */ + public static long secondsSinceMillis(long timeInMillis) { + return (System.currentTimeMillis() - timeInMillis) / 1000; + } + + /** + * Converts long to int, truncating to maximum/minimum value if needed. + * + * @param longValue + * @return + */ + public static int truncateAndConvertToInt(long longValue) { + return longValue > Integer.MAX_VALUE ? Integer.MAX_VALUE : longValue < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) longValue; + } + + /** + * Converts long[] to int[], truncating to maximum/minimum value if needed. + * + * @param longArray + * @return + */ + public static int[] truncateAndConvertToInt(long[] longArray) { + int[] intArray = new int[longArray.length]; + + for (int i = 0; i < longArray.length; i++) { + intArray[i] = longArray[i] > Integer.MAX_VALUE ? Integer.MAX_VALUE : longArray[i] < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int) longArray[i]; + } + return intArray; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/V1toV2StatementInterceptorAdapter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/V1toV2StatementInterceptorAdapter.java new file mode 100644 index 0000000..0da9bc6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/V1toV2StatementInterceptorAdapter.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.sql.SQLException; +import java.util.Properties; + +public class V1toV2StatementInterceptorAdapter implements StatementInterceptorV2 { + private final StatementInterceptor toProxy; + + public V1toV2StatementInterceptorAdapter(StatementInterceptor toProxy) { + this.toProxy = toProxy; + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection, + int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException { + return this.toProxy.postProcess(sql, interceptedStatement, originalResultSet, connection); + } + + public void destroy() { + this.toProxy.destroy(); + } + + public boolean executeTopLevelOnly() { + return this.toProxy.executeTopLevelOnly(); + } + + public void init(Connection conn, Properties props) throws SQLException { + this.toProxy.init(conn, props); + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + return this.toProxy.preProcess(sql, interceptedStatement, connection); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableOutputStream.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableOutputStream.java new file mode 100644 index 0000000..30242df --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableOutputStream.java @@ -0,0 +1,53 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * A java.io.OutputStream used to write ASCII data into Blobs and Clobs + */ +class WatchableOutputStream extends ByteArrayOutputStream { + private OutputStreamWatcher watcher; + + /** + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException { + super.close(); + + if (this.watcher != null) { + this.watcher.streamClosed(this); + } + } + + /** + * @param watcher + */ + public void setWatcher(OutputStreamWatcher watcher) { + this.watcher = watcher; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableWriter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableWriter.java new file mode 100644 index 0000000..c5c8115 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WatchableWriter.java @@ -0,0 +1,53 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +import java.io.CharArrayWriter; + +/** + * A java.io.Writer used to write unicode data into Blobs and Clobs + */ +class WatchableWriter extends CharArrayWriter { + private WriterWatcher watcher; + + /** + * @see java.io.Writer#close() + */ + @Override + public void close() { + super.close(); + + // Send data to watcher + if (this.watcher != null) { + this.watcher.writerClosed(this); + } + } + + /** + * @param watcher + */ + public void setWatcher(WriterWatcher watcher) { + this.watcher = watcher; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Wrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Wrapper.java new file mode 100644 index 0000000..d504919 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/Wrapper.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * This interface is a duplicate of java.sql.Wrapper, added in Java 1.6, and it's used to backport wrapping ability to older JDBC versions. + * + * @see java.sql.Wrapper + */ +public interface Wrapper { + T unwrap(java.lang.Class iface) throws java.sql.SQLException; + + boolean isWrapperFor(java.lang.Class iface) throws java.sql.SQLException; +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WriterWatcher.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WriterWatcher.java new file mode 100644 index 0000000..39b59e5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/WriterWatcher.java @@ -0,0 +1,35 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc; + +/** + * Objects that want to be notified of lifecycle events on a WatchableWriter should implement this interface, and register themselves with setWatcher() on the + * WatchableWriter instance. + */ +interface WriterWatcher { + /** + * Called when the Writer being watched has .close() called + */ + void writerClosed(WatchableWriter out); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlClearPasswordPlugin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlClearPasswordPlugin.java new file mode 100644 index 0000000..d2f9e14 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlClearPasswordPlugin.java @@ -0,0 +1,93 @@ +/* + Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.authentication; + +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import com.mysql.jdbc.AuthenticationPlugin; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StringUtils; + +/** + * MySQL Clear Password Authentication Plugin + */ +public class MysqlClearPasswordPlugin implements AuthenticationPlugin { + + private Connection connection; + private String password = null; + + public void init(Connection conn, Properties props) throws SQLException { + this.connection = conn; + } + + public void destroy() { + this.password = null; + } + + public String getProtocolPluginName() { + return "mysql_clear_password"; + } + + public boolean requiresConfidentiality() { + return true; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + + Buffer bresp; + try { + String encoding = this.connection.versionMeetsMinimum(5, 7, 6) ? this.connection.getPasswordCharacterEncoding() : "UTF-8"; + bresp = new Buffer(StringUtils.getBytes(this.password != null ? this.password : "", encoding)); + } catch (UnsupportedEncodingException e) { + throw SQLError.createSQLException(Messages.getString("MysqlClearPasswordPlugin.1", new Object[] { this.connection.getPasswordCharacterEncoding() }), + SQLError.SQL_STATE_GENERAL_ERROR, null); + } + + bresp.setPosition(bresp.getBufLength()); + int oldBufLength = bresp.getBufLength(); + + bresp.writeByte((byte) 0); + + bresp.setBufLength(oldBufLength + 1); + bresp.setPosition(0); + + toServer.add(bresp); + return true; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlNativePasswordPlugin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlNativePasswordPlugin.java new file mode 100644 index 0000000..bd86dd3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlNativePasswordPlugin.java @@ -0,0 +1,98 @@ +/* + Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.authentication; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import com.mysql.jdbc.AuthenticationPlugin; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Security; + +/** + * MySQL Native Password Authentication Plugin + */ +public class MysqlNativePasswordPlugin implements AuthenticationPlugin { + + private Connection connection; + private String password = null; + + public void init(Connection conn, Properties props) throws SQLException { + this.connection = conn; + } + + public void destroy() { + this.password = null; + } + + public String getProtocolPluginName() { + return "mysql_native_password"; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + + try { + toServer.clear(); + + Buffer bresp = null; + + String pwd = this.password; + + if (fromServer == null || pwd == null || pwd.length() == 0) { + bresp = new Buffer(new byte[0]); + } else { + bresp = new Buffer(Security.scramble411(pwd, fromServer.readString(), this.connection.getPasswordCharacterEncoding())); + } + toServer.add(bresp); + + } catch (NoSuchAlgorithmException nse) { + throw SQLError.createSQLException(Messages.getString("MysqlIO.91") + Messages.getString("MysqlIO.92"), SQLError.SQL_STATE_GENERAL_ERROR, null); + } catch (UnsupportedEncodingException e) { + throw SQLError.createSQLException( + Messages.getString("MysqlNativePasswordPlugin.1", new Object[] { this.connection.getPasswordCharacterEncoding() }), + SQLError.SQL_STATE_GENERAL_ERROR, null); + } + + return true; + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlOldPasswordPlugin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlOldPasswordPlugin.java new file mode 100644 index 0000000..df178f0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/MysqlOldPasswordPlugin.java @@ -0,0 +1,94 @@ +/* + Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.authentication; + +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import com.mysql.jdbc.AuthenticationPlugin; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; + +/** + * MySQL Native Old-Password Authentication Plugin + */ +public class MysqlOldPasswordPlugin implements AuthenticationPlugin { + + private Connection connection; + private String password = null; + + public void init(Connection conn, Properties props) throws SQLException { + this.connection = conn; + } + + public void destroy() { + this.password = null; + } + + public String getProtocolPluginName() { + return "mysql_old_password"; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + + Buffer bresp = null; + + String pwd = this.password; + + if (fromServer == null || pwd == null || pwd.length() == 0) { + bresp = new Buffer(new byte[0]); + } else { + bresp = new Buffer( + StringUtils.getBytes(Util.newCrypt(pwd, fromServer.readString().substring(0, 8), this.connection.getPasswordCharacterEncoding()))); + + bresp.setPosition(bresp.getBufLength()); + int oldBufLength = bresp.getBufLength(); + + bresp.writeByte((byte) 0); + + bresp.setBufLength(oldBufLength + 1); + bresp.setPosition(0); + } + toServer.add(bresp); + + return true; + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/Sha256PasswordPlugin.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/Sha256PasswordPlugin.java new file mode 100644 index 0000000..3f6c45b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/authentication/Sha256PasswordPlugin.java @@ -0,0 +1,204 @@ +/* + Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.authentication; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import com.mysql.jdbc.AuthenticationPlugin; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ExportControlled; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlIO; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Security; +import com.mysql.jdbc.StringUtils; + +/** + * MySQL Clear Password Authentication Plugin + */ +public class Sha256PasswordPlugin implements AuthenticationPlugin { + public static String PLUGIN_NAME = "sha256_password"; + + private Connection connection; + private String password = null; + private String seed = null; + private boolean publicKeyRequested = false; + private String publicKeyString = null; + + public void init(Connection conn, Properties props) throws SQLException { + this.connection = conn; + + String pkURL = this.connection.getServerRSAPublicKeyFile(); + if (pkURL != null) { + this.publicKeyString = readRSAKey(this.connection, pkURL); + } + } + + public void destroy() { + this.password = null; + this.seed = null; + this.publicKeyRequested = false; + } + + public String getProtocolPluginName() { + return PLUGIN_NAME; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + + if (this.password == null || this.password.length() == 0 || fromServer == null) { + // no password + Buffer bresp = new Buffer(new byte[] { 0 }); + toServer.add(bresp); + + } else if (((MySQLConnection) this.connection).getIO().isSSLEstablished()) { + // allow plain text over SSL + Buffer bresp; + try { + bresp = new Buffer(StringUtils.getBytes(this.password, this.connection.getPasswordCharacterEncoding())); + } catch (UnsupportedEncodingException e) { + throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.3", new Object[] { this.connection.getPasswordCharacterEncoding() }), + SQLError.SQL_STATE_GENERAL_ERROR, null); + } + bresp.setPosition(bresp.getBufLength()); + int oldBufLength = bresp.getBufLength(); + bresp.writeByte((byte) 0); + bresp.setBufLength(oldBufLength + 1); + bresp.setPosition(0); + toServer.add(bresp); + + } else if (this.connection.getServerRSAPublicKeyFile() != null) { + // encrypt with given key, don't use "Public Key Retrieval" + this.seed = fromServer.readString(); + Buffer bresp = new Buffer(encryptPassword(this.password, this.seed, this.connection, this.publicKeyString)); + toServer.add(bresp); + + } else { + if (!this.connection.getAllowPublicKeyRetrieval()) { + throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.2"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, + this.connection.getExceptionInterceptor()); + } + + // We must request the public key from the server to encrypt the password + if (this.publicKeyRequested && fromServer.getBufLength() > MysqlIO.SEED_LENGTH) { + // Servers affected by Bug#70865 could send Auth Switch instead of key after Public Key Retrieval, + // so we check payload length to detect that. + + // read key response + Buffer bresp = new Buffer(encryptPassword(this.password, this.seed, this.connection, fromServer.readString())); + toServer.add(bresp); + this.publicKeyRequested = false; + } else { + // build and send Public Key Retrieval packet + this.seed = fromServer.readString(); + Buffer bresp = new Buffer(new byte[] { 1 }); + toServer.add(bresp); + this.publicKeyRequested = true; + } + } + return true; + } + + private static byte[] encryptPassword(String password, String seed, Connection connection, String key) throws SQLException { + byte[] input = null; + try { + input = password != null ? StringUtils.getBytesNullTerminated(password, connection.getPasswordCharacterEncoding()) : new byte[] { 0 }; + } catch (UnsupportedEncodingException e) { + throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.3", new Object[] { connection.getPasswordCharacterEncoding() }), + SQLError.SQL_STATE_GENERAL_ERROR, null); + } + byte[] mysqlScrambleBuff = new byte[input.length]; + Security.xorString(input, mysqlScrambleBuff, seed.getBytes(), input.length); + return ExportControlled.encryptWithRSAPublicKey(mysqlScrambleBuff, + ExportControlled.decodeRSAPublicKey(key, ((MySQLConnection) connection).getExceptionInterceptor()), + ((MySQLConnection) connection).getExceptionInterceptor()); + } + + private static String readRSAKey(Connection connection, String pkPath) throws SQLException { + String res = null; + byte[] fileBuf = new byte[2048]; + + BufferedInputStream fileIn = null; + + try { + File f = new File(pkPath); + String canonicalPath = f.getCanonicalPath(); + fileIn = new BufferedInputStream(new FileInputStream(canonicalPath)); + + int bytesRead = 0; + + StringBuilder sb = new StringBuilder(); + while ((bytesRead = fileIn.read(fileBuf)) != -1) { + sb.append(StringUtils.toAsciiString(fileBuf, 0, bytesRead)); + } + res = sb.toString(); + + } catch (IOException ioEx) { + + if (connection.getParanoid()) { + throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.0", new Object[] { "" }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + connection.getExceptionInterceptor()); + } + throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.0", new Object[] { "'" + pkPath + "'" }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ioEx, connection.getExceptionInterceptor()); + + } finally { + if (fileIn != null) { + try { + fileIn.close(); + } catch (Exception ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.1"), SQLError.SQL_STATE_GENERAL_ERROR, ex, + connection.getExceptionInterceptor()); + + throw sqlEx; + } + } + } + + return res; + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/3-0-Compat.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/3-0-Compat.properties new file mode 100644 index 0000000..17141d3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/3-0-Compat.properties @@ -0,0 +1,19 @@ +# +# Settings to maintain Connector/J 3.0.x compatibility +# (as much as it can be) +# + +emptyStringsConvertToZero=true +jdbcCompliantTruncation=false +noDatetimeStringSync=true +nullCatalogMeansCurrent=true +nullNamePatternMatchesAll=true +transformedBitIsBoolean=false +dontTrackOpenResources=true +zeroDateTimeBehavior=convertToNull +useServerPrepStmts=false +autoClosePStmtStreams=true +processEscapeCodesForPrepStmts=false +useFastDateParsing=false +populateInsertRowWithDefaultValues=false +useDirectRowUnpack=false \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/5-0-Compat.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/5-0-Compat.properties new file mode 100644 index 0000000..56afaf3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/5-0-Compat.properties @@ -0,0 +1,6 @@ +# +# Settings to maintain Connector/J 5.0.x compatibility +# (as much as it can be) +# + +useDirectRowUnpack=false \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/clusterBase.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/clusterBase.properties new file mode 100644 index 0000000..e87a604 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/clusterBase.properties @@ -0,0 +1,4 @@ +# Basic properties for clusters +autoReconnect=true +failOverReadOnly=false +roundRobinLoadBalance=true \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/coldFusion.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/coldFusion.properties new file mode 100644 index 0000000..38580e2 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/coldFusion.properties @@ -0,0 +1,25 @@ +# +# Properties for optimal usage in ColdFusion +# +# Automagically pulled in if "autoConfigureForColdFusion" is "true" +# which is the default configuration of the driver +# + +# +# CF uses a _lot_ of RSMD.isCaseSensitive() - this optimizes it +# + +useDynamicCharsetInfo=false + +# +# CF's pool tends to be "chatty" like DBCP +# + +alwaysSendSetIsolation=false +useLocalSessionState=true + +# +# CF's pool seems to loose connectivity on page restart +# + +autoReconnect=true diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/fullDebug.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/fullDebug.properties new file mode 100644 index 0000000..14e47c5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/fullDebug.properties @@ -0,0 +1,6 @@ +# Settings for 'max-debug' style situations +profileSQL=true +gatherPerfMetrics=true +useUsageAdvisor=true +logSlowQueries=true +explainSlowQueries=true \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/maxPerformance.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/maxPerformance.properties new file mode 100644 index 0000000..86a800a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/maxPerformance.properties @@ -0,0 +1,34 @@ +# +# A configuration that maximizes performance, while +# still staying JDBC-compliant and not doing anything that +# would be "dangerous" to run-of-the-mill J2EE applications +# +# Note that because we're caching things like callable statements +# and the server configuration, this bundle isn't appropriate +# for use with servers that get config'd dynamically without +# restarting the application using this configuration bundle. + +cachePrepStmts=true +cacheCallableStmts=true + +cacheServerConfiguration=true + +# +# Reduces amount of calls to database to set +# session state. "Safe" as long as application uses +# Connection methods to set current database, autocommit +# and transaction isolation +# + +useLocalSessionState=true +elideSetAutoCommits=true +alwaysSendSetIsolation=false + +# Can cause high-GC pressure if timeouts are used on every +# query +enableQueryTimeouts=false + +# Bypass connection attribute handling during connection +# setup +connectionAttributes=none + diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties new file mode 100644 index 0000000..b4b31a1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties @@ -0,0 +1,13 @@ +# +# Solaris has pretty high syscall overhead, so these configs +# remove as many syscalls as possible. +# + +# Reduce recv() syscalls + +useUnbufferedInput=false +useReadAheadInput=false + +# Reduce number of calls to getTimeOfDay() + +maintainTimeStats=false \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/DeadlockTimeoutRollbackMarker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/DeadlockTimeoutRollbackMarker.java new file mode 100644 index 0000000..aca6cbc --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/DeadlockTimeoutRollbackMarker.java @@ -0,0 +1,31 @@ +/* + Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +/** + * Marker interface for exceptions that are caused by deadlock/wait timeout + */ +public interface DeadlockTimeoutRollbackMarker { + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLDataException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLDataException.java new file mode 100644 index 0000000..042d291 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLDataException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLDataException extends MySQLNonTransientException { + + static final long serialVersionUID = 4317904269797988676L; + + public MySQLDataException() { + super(); + } + + public MySQLDataException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLDataException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLDataException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java new file mode 100644 index 0000000..820839d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLIntegrityConstraintViolationException extends MySQLNonTransientException { + + static final long serialVersionUID = -5528363270635808904L; + + public MySQLIntegrityConstraintViolationException() { + super(); + } + + public MySQLIntegrityConstraintViolationException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLIntegrityConstraintViolationException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLIntegrityConstraintViolationException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java new file mode 100644 index 0000000..8240bd8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLInvalidAuthorizationSpecException extends MySQLNonTransientException { + + static final long serialVersionUID = 6878889837492500030L; + + public MySQLInvalidAuthorizationSpecException() { + super(); + } + + public MySQLInvalidAuthorizationSpecException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLInvalidAuthorizationSpecException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLInvalidAuthorizationSpecException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java new file mode 100644 index 0000000..d7bf2bf --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLNonTransientConnectionException extends MySQLNonTransientException { + + static final long serialVersionUID = -3050543822763367670L; + + public MySQLNonTransientConnectionException() { + super(); + } + + public MySQLNonTransientConnectionException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLNonTransientConnectionException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLNonTransientConnectionException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java new file mode 100644 index 0000000..45c6792 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +import java.sql.SQLException; + +public class MySQLNonTransientException extends SQLException { + + static final long serialVersionUID = -8714521137552613517L; + + public MySQLNonTransientException() { + super(); + } + + public MySQLNonTransientException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLNonTransientException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLNonTransientException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLQueryInterruptedException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLQueryInterruptedException.java new file mode 100644 index 0000000..c3ed39c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLQueryInterruptedException.java @@ -0,0 +1,46 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLQueryInterruptedException extends MySQLNonTransientException { + + private static final long serialVersionUID = -8714521137662613517L; + + public MySQLQueryInterruptedException() { + super(); + } + + public MySQLQueryInterruptedException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLQueryInterruptedException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLQueryInterruptedException(String reason) { + super(reason); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java new file mode 100644 index 0000000..d2d74f5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLStatementCancelledException extends MySQLNonTransientException { + + static final long serialVersionUID = -8762717748377197378L; + + public MySQLStatementCancelledException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLStatementCancelledException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLStatementCancelledException(String reason) { + super(reason); + } + + public MySQLStatementCancelledException() { + super("Statement cancelled due to client request"); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java new file mode 100644 index 0000000..c482f42 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLSyntaxErrorException extends MySQLNonTransientException { + + static final long serialVersionUID = 6919059513432113764L; + + public MySQLSyntaxErrorException() { + super(); + } + + public MySQLSyntaxErrorException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLSyntaxErrorException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLSyntaxErrorException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java new file mode 100644 index 0000000..1949ad5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLTimeoutException extends MySQLTransientException { + + static final long serialVersionUID = -789621240523230339L; + + public MySQLTimeoutException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTimeoutException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTimeoutException(String reason) { + super(reason); + } + + public MySQLTimeoutException() { + super("Statement cancelled due to timeout or client request"); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java new file mode 100644 index 0000000..ec920ae --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLTransactionRollbackException extends MySQLTransientException implements DeadlockTimeoutRollbackMarker { + + static final long serialVersionUID = 6034999468737801730L; + + public MySQLTransactionRollbackException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransactionRollbackException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransactionRollbackException(String reason) { + super(reason); + } + + public MySQLTransactionRollbackException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java new file mode 100644 index 0000000..430b7b3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java @@ -0,0 +1,45 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +public class MySQLTransientConnectionException extends MySQLTransientException { + + static final long serialVersionUID = 8699144578759941201L; + + public MySQLTransientConnectionException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransientConnectionException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransientConnectionException(String reason) { + super(reason); + } + + public MySQLTransientConnectionException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientException.java new file mode 100644 index 0000000..3c40b5d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/MySQLTransientException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions; + +import java.sql.SQLException; + +public class MySQLTransientException extends SQLException { + + static final long serialVersionUID = -1885878228558607563L; + + public MySQLTransientException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransientException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransientException(String reason) { + super(reason); + } + + public MySQLTransientException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java new file mode 100644 index 0000000..cfd6efb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java @@ -0,0 +1,75 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.net.BindException; +import java.sql.SQLRecoverableException; + +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StreamingNotifiable; + +/** + * An exception to represent communications errors with the database. + * + * Attempts to provide 'friendler' error messages to end-users, including last time a packet was sent to the database, what the client-timeout is set to, and + * whether the idle time has been exceeded. + */ +public class CommunicationsException extends SQLRecoverableException implements StreamingNotifiable { + + static final long serialVersionUID = 4317904269797988677L; + + private String exceptionMessage; + + public CommunicationsException(MySQLConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, Exception underlyingException) { + this.exceptionMessage = SQLError.createLinkFailureMessageBasedOnHeuristics(conn, lastPacketSentTimeMs, lastPacketReceivedTimeMs, underlyingException); + + if (underlyingException != null) { + initCause(underlyingException); + } + } + + /* + * @see java.lang.Throwable#getMessage() + */ + public String getMessage() { + return this.exceptionMessage; + } + + /* + * @see java.sql.SQLException#getSQLState() + */ + public String getSQLState() { + return SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE; + } + + /* + * @see com.mysql.jdbc.StreamingNotifiable#setWasStreamingResults() + */ + public void setWasStreamingResults() { + // replace exception message + this.exceptionMessage = Messages.getString("CommunicationsException.ClientWasStreaming"); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java new file mode 100644 index 0000000..112824b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLDataException; + +public class MySQLDataException extends SQLDataException { + + static final long serialVersionUID = 4317904269797988676L; + + public MySQLDataException() { + super(); + } + + public MySQLDataException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLDataException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLDataException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java new file mode 100644 index 0000000..c24772c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLIntegrityConstraintViolationException; + +public class MySQLIntegrityConstraintViolationException extends SQLIntegrityConstraintViolationException { + + static final long serialVersionUID = -5528363270635808904L; + + public MySQLIntegrityConstraintViolationException() { + super(); + } + + public MySQLIntegrityConstraintViolationException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLIntegrityConstraintViolationException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLIntegrityConstraintViolationException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java new file mode 100644 index 0000000..434d30c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLInvalidAuthorizationSpecException; + +public class MySQLInvalidAuthorizationSpecException extends SQLInvalidAuthorizationSpecException { + + static final long serialVersionUID = 6878889837492500030L; + + public MySQLInvalidAuthorizationSpecException() { + super(); + } + + public MySQLInvalidAuthorizationSpecException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLInvalidAuthorizationSpecException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLInvalidAuthorizationSpecException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java new file mode 100644 index 0000000..c33da04 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLNonTransientConnectionException; + +public class MySQLNonTransientConnectionException extends SQLNonTransientConnectionException { + + static final long serialVersionUID = -3050543822763367670L; + + public MySQLNonTransientConnectionException() { + super(); + } + + public MySQLNonTransientConnectionException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLNonTransientConnectionException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLNonTransientConnectionException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java new file mode 100644 index 0000000..38ae1c3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLException; +import java.sql.SQLNonTransientException; + +public class MySQLNonTransientException extends SQLNonTransientException { + + static final long serialVersionUID = -8714521137552613517L; + + public MySQLNonTransientException() { + super(); + } + + public MySQLNonTransientException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLNonTransientException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLNonTransientException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLQueryInterruptedException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLQueryInterruptedException.java new file mode 100644 index 0000000..15ebb56 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLQueryInterruptedException.java @@ -0,0 +1,46 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +public class MySQLQueryInterruptedException extends com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientException { + + private static final long serialVersionUID = -8714521137662613517L; + + public MySQLQueryInterruptedException() { + super(); + } + + public MySQLQueryInterruptedException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLQueryInterruptedException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLQueryInterruptedException(String reason) { + super(reason); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java new file mode 100644 index 0000000..ef8a94a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLSyntaxErrorException; + +public class MySQLSyntaxErrorException extends SQLSyntaxErrorException { + + static final long serialVersionUID = 6919059513432113764L; + + public MySQLSyntaxErrorException() { + super(); + } + + public MySQLSyntaxErrorException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLSyntaxErrorException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLSyntaxErrorException(String reason) { + super(reason); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java new file mode 100644 index 0000000..e6db1e0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java @@ -0,0 +1,51 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLTimeoutException; + +public class MySQLTimeoutException extends SQLTimeoutException { + + static final long serialVersionUID = -789621240523230339L; + + public MySQLTimeoutException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTimeoutException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTimeoutException(String reason) { + super(reason); + } + + public MySQLTimeoutException() { + super("Statement cancelled due to timeout or client request"); + } + + public int getErrorCode() { + return super.getErrorCode(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java new file mode 100644 index 0000000..5bdb2e2 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java @@ -0,0 +1,49 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLTransactionRollbackException; + +import com.mysql.jdbc.exceptions.DeadlockTimeoutRollbackMarker; + +public class MySQLTransactionRollbackException extends SQLTransactionRollbackException implements DeadlockTimeoutRollbackMarker { + + static final long serialVersionUID = 6034999468737801730L; + + public MySQLTransactionRollbackException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransactionRollbackException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransactionRollbackException(String reason) { + super(reason); + } + + public MySQLTransactionRollbackException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java new file mode 100644 index 0000000..01cc358 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLTransientConnectionException; + +public class MySQLTransientConnectionException extends SQLTransientConnectionException { + + static final long serialVersionUID = 8699144578759941201L; + + public MySQLTransientConnectionException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransientConnectionException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransientConnectionException(String reason) { + super(reason); + } + + public MySQLTransientConnectionException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java new file mode 100644 index 0000000..1b4f296 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.exceptions.jdbc4; + +import java.sql.SQLException; +import java.sql.SQLTransientException; + +public class MySQLTransientException extends SQLTransientException { + + static final long serialVersionUID = -1885878228558607563L; + + public MySQLTransientException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransientException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransientException(String reason) { + super(reason); + } + + public MySQLTransientException() { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java new file mode 100644 index 0000000..4f13a61 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java @@ -0,0 +1,123 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.integration.c3p0; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mchange.v2.c3p0.C3P0ProxyConnection; +import com.mchange.v2.c3p0.QueryConnectionTester; +import com.mysql.jdbc.CommunicationsException; + +/** + * ConnectionTester for C3P0 connection pool that uses the more efficient COM_PING method of testing connection 'liveness' for MySQL, and 'sorts' exceptions + * based on SQLState or class of 'CommunicationsException' for handling exceptions. + */ +public final class MysqlConnectionTester implements QueryConnectionTester { + + private static final long serialVersionUID = 3256444690067896368L; + + private static final Object[] NO_ARGS_ARRAY = new Object[0]; + + private transient Method pingMethod; + + public MysqlConnectionTester() { + try { + this.pingMethod = com.mysql.jdbc.Connection.class.getMethod("ping", (Class[]) null); + } catch (Exception ex) { + // punt, we have no way to recover, other than we now use 'SELECT 1' for handling the connection testing. + } + } + + /* + * (non-Javadoc) + * + * @see com.mchange.v2.c3p0.ConnectionTester#activeCheckConnection(java.sql.Connection) + */ + public int activeCheckConnection(Connection con) { + try { + if (this.pingMethod != null) { + if (con instanceof com.mysql.jdbc.Connection) { + // We've been passed an instance of a MySQL connection -- no need for reflection + ((com.mysql.jdbc.Connection) con).ping(); + } else { + // Assume the connection is a C3P0 proxy + C3P0ProxyConnection castCon = (C3P0ProxyConnection) con; + castCon.rawConnectionOperation(this.pingMethod, C3P0ProxyConnection.RAW_CONNECTION, NO_ARGS_ARRAY); + } + } else { + Statement pingStatement = null; + + try { + pingStatement = con.createStatement(); + pingStatement.executeQuery("SELECT 1").close(); + } finally { + if (pingStatement != null) { + pingStatement.close(); + } + } + } + + return CONNECTION_IS_OKAY; + } catch (Exception ex) { + return CONNECTION_IS_INVALID; + } + } + + /* + * (non-Javadoc) + * + * @see com.mchange.v2.c3p0.ConnectionTester#statusOnException(java.sql.Connection, java.lang.Throwable) + */ + public int statusOnException(Connection arg0, Throwable throwable) { + if (throwable instanceof CommunicationsException || "com.mysql.jdbc.exceptions.jdbc4.CommunicationsException".equals(throwable.getClass().getName())) { + return CONNECTION_IS_INVALID; + } + + if (throwable instanceof SQLException) { + String sqlState = ((SQLException) throwable).getSQLState(); + + if (sqlState != null && sqlState.startsWith("08")) { + return CONNECTION_IS_INVALID; + } + + return CONNECTION_IS_OKAY; + } + + // Runtime/Unchecked? + + return CONNECTION_IS_INVALID; + } + + /* + * (non-Javadoc) + * + * @see com.mchange.v2.c3p0.QueryConnectionTester#activeCheckConnection(java.sql.Connection, java.lang.String) + */ + public int activeCheckConnection(Connection arg0, String arg1) { + return CONNECTION_IS_OKAY; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java new file mode 100644 index 0000000..0b9095c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java @@ -0,0 +1,53 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.integration.jboss; + +import java.sql.SQLException; + +import org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter; + +/** + * Exception sorter used for JBoss to make recovery of downed/stale connections work more consistently. + */ +public final class ExtendedMysqlExceptionSorter extends MySQLExceptionSorter { + + static final long serialVersionUID = -2454582336945931069L; + + /* + * (non-Javadoc) + * + * @see org.jboss.resource.adapter.jdbc.ExceptionSorter#isExceptionFatal(java.sql.SQLException) + */ + @Override + public boolean isExceptionFatal(SQLException ex) { + String sqlState = ex.getSQLState(); + + if (sqlState != null && sqlState.startsWith("08")) { + return true; + } + + return super.isExceptionFatal(ex); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java new file mode 100644 index 0000000..af5d8c6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.integration.jboss; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import org.jboss.resource.adapter.jdbc.ValidConnectionChecker; + +/** + * A more efficient connection checker for JBoss. + */ +public final class MysqlValidConnectionChecker implements ValidConnectionChecker, Serializable { + + private static final long serialVersionUID = 8909421133577519177L; + + public MysqlValidConnectionChecker() { + } + + /* + * (non-Javadoc) + * + * @see org.jboss.resource.adapter.jdbc.ValidConnectionChecker#isValidConnection(java.sql.Connection) + */ + public SQLException isValidConnection(Connection conn) { + + // Use "/* ping */ SELECT 1" which will send pings across multi-connections too in case the connection was "wrapped" by Jboss in any way... + + Statement pingStatement = null; + + try { + pingStatement = conn.createStatement(); + + pingStatement.executeQuery("/* ping */ SELECT 1").close(); + + return null; + } catch (SQLException sqlEx) { + return sqlEx; + } finally { + if (pingStatement != null) { + try { + pingStatement.close(); + } catch (SQLException sqlEx) { + // can't do anything about it here + } + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java new file mode 100644 index 0000000..6aabb77 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java @@ -0,0 +1,105 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.interceptors; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StatementInterceptor; + +public class ResultSetScannerInterceptor implements StatementInterceptor { + + protected Pattern regexP; + + public void init(Connection conn, Properties props) throws SQLException { + String regexFromUser = props.getProperty("resultSetScannerRegex"); + + if (regexFromUser == null || regexFromUser.length() == 0) { + throw new SQLException("resultSetScannerRegex must be configured, and must be > 0 characters"); + } + + try { + this.regexP = Pattern.compile(regexFromUser); + } catch (Throwable t) { + SQLException sqlEx = new SQLException("Can't use configured regex due to underlying exception."); + sqlEx.initCause(t); + + throw sqlEx; + } + + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection) + throws SQLException { + + // requirement of anonymous class + final ResultSetInternalMethods finalResultSet = originalResultSet; + + return (ResultSetInternalMethods) Proxy.newProxyInstance(originalResultSet.getClass().getClassLoader(), new Class[] { ResultSetInternalMethods.class }, + new InvocationHandler() { + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + Object invocationResult = method.invoke(finalResultSet, args); + + String methodName = method.getName(); + + if (invocationResult != null && invocationResult instanceof String || "getString".equals(methodName) || "getObject".equals(methodName) + || "getObjectStoredProc".equals(methodName)) { + Matcher matcher = ResultSetScannerInterceptor.this.regexP.matcher(invocationResult.toString()); + + if (matcher.matches()) { + throw new SQLException("value disallowed by filter"); + } + } + + return invocationResult; + } + }); + + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + // we don't care about this event + + return null; + } + + // we don't issue queries, so it should be safe to intercept at any point + public boolean executeTopLevelOnly() { + return false; + } + + public void destroy() { + + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java new file mode 100644 index 0000000..1e6849a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java @@ -0,0 +1,97 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.interceptors; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StatementInterceptor; +import com.mysql.jdbc.Util; + +public class ServerStatusDiffInterceptor implements StatementInterceptor { + + private Map preExecuteValues = new HashMap(); + + private Map postExecuteValues = new HashMap(); + + public void init(Connection conn, Properties props) throws SQLException { + + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection) + throws SQLException { + + if (connection.versionMeetsMinimum(5, 0, 2)) { + populateMapWithSessionStatusValues(connection, this.postExecuteValues); + + connection.getLog().logInfo("Server status change for statement:\n" + Util.calculateDifferences(this.preExecuteValues, this.postExecuteValues)); + } + + return null; // we don't actually modify a result set + + } + + private void populateMapWithSessionStatusValues(Connection connection, Map toPopulate) throws SQLException { + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + toPopulate.clear(); + + stmt = connection.createStatement(); + rs = stmt.executeQuery("SHOW SESSION STATUS"); + Util.resultSetToMap(toPopulate, rs); + } finally { + if (rs != null) { + rs.close(); + } + + if (stmt != null) { + stmt.close(); + } + } + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + + if (connection.versionMeetsMinimum(5, 0, 2)) { + populateMapWithSessionStatusValues(connection, this.preExecuteValues); + } + + return null; // we don't actually modify a result set + } + + public boolean executeTopLevelOnly() { + return true; + } + + public void destroy() { + + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java new file mode 100644 index 0000000..56fb316 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java @@ -0,0 +1,87 @@ +/* + Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.interceptors; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StatementInterceptor; + +public class SessionAssociationInterceptor implements StatementInterceptor { + + protected String currentSessionKey; + protected final static ThreadLocal sessionLocal = new ThreadLocal(); + + public static final void setSessionKey(String key) { + sessionLocal.set(key); + } + + public static final void resetSessionKey() { + sessionLocal.set(null); + } + + public static final String getSessionKey() { + return sessionLocal.get(); + } + + public boolean executeTopLevelOnly() { + return true; + } + + public void init(Connection conn, Properties props) throws SQLException { + + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection) + throws SQLException { + return null; + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + String key = getSessionKey(); + + if (key != null && !key.equals(this.currentSessionKey)) { + PreparedStatement pstmt = connection.clientPrepareStatement("SET @mysql_proxy_session=?"); + + try { + pstmt.setString(1, key); + pstmt.execute(); + } finally { + pstmt.close(); + } + + this.currentSessionKey = key; + } + + return null; + } + + public void destroy() { + + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java new file mode 100644 index 0000000..ebf9e95 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java @@ -0,0 +1,2336 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Date; +import java.sql.Ref; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Map; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Util; + +/** + * Wraps callable statements created by pooled connections. + */ +public class CallableStatementWrapper extends PreparedStatementWrapper implements CallableStatement { + + private static final Constructor JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.jdbc2.optional.JDBC42CallableStatementWrapper" + : "com.mysql.jdbc.jdbc2.optional.JDBC4CallableStatementWrapper"; + JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { ConnectionWrapper.class, MysqlPooledConnection.class, CallableStatement.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR = null; + } + } + + protected static CallableStatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) throws SQLException { + if (!Util.isJdbc4()) { + return new CallableStatementWrapper(c, conn, toWrap); + } + + return (CallableStatementWrapper) Util.handleNewInstance(JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR, new Object[] { c, conn, toWrap }, + conn.getExceptionInterceptor()); + } + + /** + * @param c + * @param conn + * @param toWrap + */ + public CallableStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) { + super(c, conn, toWrap); + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(int, int) + */ + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(int, int, int) + */ + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#wasNull() + */ + public boolean wasNull() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).wasNull(); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getString(int) + */ + public String getString(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getString(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBoolean(int) + */ + public boolean getBoolean(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBoolean(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getByte(int) + */ + public byte getByte(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getByte(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getShort(int) + */ + public short getShort(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getShort(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getInt(int) + */ + public int getInt(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getInt(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getLong(int) + */ + public long getLong(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getLong(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getFloat(int) + */ + public float getFloat(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getFloat(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public double getDouble(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDouble(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + @Deprecated + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterIndex, scale); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBytes(int) + */ + public byte[] getBytes(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBytes(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Date getDate(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTime(int) + */ + public Time getTime(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTimestamp(int) + */ + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getObject(int) + */ + public Object getObject(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBigDecimal(int) + */ + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getObject(int, java.util.Map) + */ + public Object getObject(int parameterIndex, Map> typeMap) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterIndex, typeMap); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getRef(int) + */ + public Ref getRef(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRef(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBlob(int) + */ + public Blob getBlob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBlob(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getClob(int) + */ + public Clob getClob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getClob(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getArray(int) + */ + public Array getArray(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getArray(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getDate(int, java.util.Calendar) + */ + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterIndex, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTime(int, java.util.Calendar) + */ + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterIndex, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar) + */ + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterIndex, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(int, int, java.lang.String) + */ + public void registerOutParameter(int paramIndex, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(paramIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int) + */ + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, int) + */ + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, java.lang.String) + */ + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getURL(int) + */ + public URL getURL(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getURL(parameterIndex); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL) + */ + public void setURL(String parameterName, URL val) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setURL(parameterName, val); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setNull(java.lang.String, int) + */ + public void setNull(String parameterName, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNull(parameterName, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean) + */ + public void setBoolean(String parameterName, boolean x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBoolean(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setByte(java.lang.String, byte) + */ + public void setByte(String parameterName, byte x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setByte(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setShort(java.lang.String, short) + */ + public void setShort(String parameterName, short x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setShort(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setInt(java.lang.String, int) + */ + public void setInt(String parameterName, int x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setInt(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setLong(java.lang.String, long) + */ + public void setLong(String parameterName, long x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setLong(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setFloat(java.lang.String, float) + */ + public void setFloat(String parameterName, float x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setFloat(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setDouble(java.lang.String, double) + */ + public void setDouble(String parameterName, double x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDouble(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setBigDecimal(java.lang.String, java.math.BigDecimal) + */ + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBigDecimal(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setString(java.lang.String, java.lang.String) + */ + public void setString(String parameterName, String x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setString(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[]) + */ + public void setBytes(String parameterName, byte[] x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBytes(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date) + */ + public void setDate(String parameterName, Date x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDate(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time) + */ + public void setTime(String parameterName, Time x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTime(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp) + */ + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTimestamp(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setAsciiStream(java.lang.String, java.io.InputStream, int) + */ + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setBinaryStream(java.lang.String, java.io.InputStream, int) + */ + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object) + */ + public void setObject(String parameterName, Object x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setCharacterStream(java.lang.String, java.io.Reader, int) + */ + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date, java.util.Calendar) + */ + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDate(parameterName, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time, java.util.Calendar) + */ + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTime(parameterName, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp, java.util.Calendar) + */ + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTimestamp(parameterName, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#setNull(java.lang.String, int, java.lang.String) + */ + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNull(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getString(int) + */ + public String getString(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getString(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBoolean(int) + */ + public boolean getBoolean(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBoolean(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getByte(int) + */ + public byte getByte(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getByte(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getShort(int) + */ + public short getShort(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getShort(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getInt(int) + */ + public int getInt(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getInt(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getLong(int) + */ + public long getLong(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getLong(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getFloat(int) + */ + public float getFloat(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getFloat(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getDouble(int) + */ + public double getDouble(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDouble(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBytes(int) + */ + public byte[] getBytes(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBytes(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getDate(int) + */ + public Date getDate(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTime(int) + */ + public Time getTime(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTimestamp(int) + */ + public Timestamp getTimestamp(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getObject(int) + */ + public Object getObject(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBigDecimal(int) + */ + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getObject(int, java.util.Map) + */ + public Object getObject(String parameterName, Map> typeMap) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterName, typeMap); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getRef(int) + */ + public Ref getRef(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRef(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getBlob(int) + */ + public Blob getBlob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBlob(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getClob(int) + */ + public Clob getClob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getClob(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getArray(int) + */ + public Array getArray(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getArray(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getDate(int, java.util.Calendar) + */ + public Date getDate(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterName, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTime(int, java.util.Calendar) + */ + public Time getTime(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterName, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar) + */ + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterName, cal); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.sql.CallableStatement#getURL(java.lang.String) + */ + public URL getURL(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getURL(parameterName); + } + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + // + // public Reader getCharacterStream(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getCharacterStream(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public Reader getCharacterStream(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getCharacterStream(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public Reader getNCharacterStream(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getCharacterStream(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public Reader getNCharacterStream(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getNCharacterStream(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public NClob getNClob(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getNClob(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public NClob getNClob(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getNClob(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public String getNString(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getNString(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public String getNString(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getNString(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public RowId getRowId(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getRowId(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public RowId getRowId(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getRowId(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public SQLXML getSQLXML(int parameterIndex) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getSQLXML(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public SQLXML getSQLXML(String parameterName) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .getSQLXML(parameterName); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return null; + // } + // + // public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setAsciiStream(parameterName, x) ; + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setAsciiStream(parameterName, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBinaryStream(parameterName, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBinaryStream(parameterName, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(String parameterName, Blob x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBlob(parameterName, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBlob(parameterName, inputStream); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBlob(parameterName, inputStream, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setCharacterStream(parameterName, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setCharacterStream(parameterName, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(String parameterName, Clob x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setClob(parameterName, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(String parameterName, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setClob(parameterName, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(String parameterName, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setClob(parameterName, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNCharacterStream(parameterName, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNCharacterStream(parameterName, value, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(String parameterName, NClob value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterName, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(String parameterName, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterName, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterName, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNString(String parameterName, String value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNString(parameterName, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setRowId(String parameterName, RowId x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setRowId(parameterName, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setSQLXML(parameterName, xmlObject); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setAsciiStream(parameterIndex, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setAsciiStream(parameterIndex, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBinaryStream(parameterIndex, x) ; + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBinaryStream(parameterIndex, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBlob(parameterIndex, inputStream); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setBlob(parameterIndex, inputStream, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setCharacterStream(parameterIndex, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .getCharacterStream(parameterIndex); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(int parameterIndex, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setClob(parameterIndex, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setClob(parameterIndex, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNCharacterStream(parameterIndex, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // } + // + // public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNCharacterStream(parameterIndex, value, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, NClob value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterIndex, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterIndex, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNClob(parameterIndex, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNString(int parameterIndex, String value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setNString(parameterIndex, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setRowId(int parameterIndex, RowId x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setRowId(parameterIndex, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setSQLXML(parameterIndex, xmlObject); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // } + // + // public boolean isClosed() throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // .isClosed(); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return true; + // } + // + // public boolean isPoolable() throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((CallableStatement) this.wrappedStmt) + // . isPoolable(); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return false; + // } + // + // public void setPoolable(boolean poolable) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((CallableStatement) this.wrappedStmt) + // .setPoolable(poolable); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // } + // + // public boolean isWrapperFor(Class arg0) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + // + // public Object unwrap(Class arg0) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java new file mode 100644 index 0000000..e628366 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java @@ -0,0 +1,2888 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.Constructor; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.sql.Statement; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.Executor; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.Extension; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlErrorNumbers; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.log.Log; + +/** + * This class serves as a wrapper for the org.gjt.mm.mysql.jdbc2.Connection class. It is returned to the application server which may wrap it again and then + * return it to the application client in response to dataSource.getConnection(). + * + * All method invocations are forwarded to org.gjt.mm.mysql.jdbc2.Connection unless the close method was previously called, in which case a sqlException is + * thrown. The close method performs a 'logical close' on the connection. + * + * All sqlExceptions thrown by the physical connection are intercepted and sent to connectionEvent listeners before being thrown to client. + */ +public class ConnectionWrapper extends WrapperBase implements Connection { + protected Connection mc = null; + + private String invalidHandleStr = "Logical handle no longer valid"; + + private boolean closed; + + private boolean isForXa; + + private static final Constructor JDBC_4_CONNECTION_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_CONNECTION_WRAPPER_CTOR = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4ConnectionWrapper") + .getConstructor(new Class[] { MysqlPooledConnection.class, Connection.class, Boolean.TYPE }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_CONNECTION_WRAPPER_CTOR = null; + } + } + + protected static ConnectionWrapper getInstance(MysqlPooledConnection mysqlPooledConnection, Connection mysqlConnection, boolean forXa) throws SQLException { + if (!Util.isJdbc4()) { + return new ConnectionWrapper(mysqlPooledConnection, mysqlConnection, forXa); + } + + return (ConnectionWrapper) Util.handleNewInstance(JDBC_4_CONNECTION_WRAPPER_CTOR, + new Object[] { mysqlPooledConnection, mysqlConnection, Boolean.valueOf(forXa) }, mysqlPooledConnection.getExceptionInterceptor()); + } + + /** + * Construct a new LogicalHandle and set instance variables + * + * @param mysqlPooledConnection + * reference to object that instantiated this object + * @param mysqlConnection + * physical connection to db + * + * @throws SQLException + * if an error occurs. + */ + public ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection, Connection mysqlConnection, boolean forXa) throws SQLException { + super(mysqlPooledConnection); + + this.mc = mysqlConnection; + this.closed = false; + this.isForXa = forXa; + + if (this.isForXa) { + setInGlobalTx(false); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#setAutoCommit + */ + public void setAutoCommit(boolean autoCommit) throws SQLException { + checkClosed(); + + if (autoCommit && isInGlobalTx()) { + throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.setAutoCommit(autoCommit); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#getAutoCommit() + */ + public boolean getAutoCommit() throws SQLException { + checkClosed(); + + try { + return this.mc.getAutoCommit(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#setCatalog() + */ + public void setCatalog(String catalog) throws SQLException { + checkClosed(); + + try { + this.mc.setCatalog(catalog); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @return the current catalog + * + * @throws SQLException + * if an error occurs + */ + public String getCatalog() throws SQLException { + checkClosed(); + + try { + return this.mc.getCatalog(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#isClosed() + */ + public boolean isClosed() throws SQLException { + return (this.closed || this.mc.isClosed()); + } + + public boolean isMasterConnection() { + return this.mc.isMasterConnection(); + } + + /** + * @see Connection#setHoldability(int) + */ + public void setHoldability(int arg0) throws SQLException { + checkClosed(); + + try { + this.mc.setHoldability(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * @see Connection#getHoldability() + */ + public int getHoldability() throws SQLException { + checkClosed(); + + try { + return this.mc.getHoldability(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return Statement.CLOSE_CURRENT_RESULT; // we don't reach this code, + // compiler can't tell + } + + /** + * Allows clients to determine how long this connection has been idle. + * + * @return how long the connection has been idle. + */ + public long getIdleFor() { + return this.mc.getIdleFor(); + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @return a metadata instance + * + * @throws SQLException + * if an error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException { + checkClosed(); + + try { + return this.mc.getMetaData(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#setReadOnly() + */ + public void setReadOnly(boolean readOnly) throws SQLException { + checkClosed(); + + try { + this.mc.setReadOnly(readOnly); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#isReadOnly() + */ + public boolean isReadOnly() throws SQLException { + checkClosed(); + + try { + return this.mc.isReadOnly(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#setSavepoint() + */ + public java.sql.Savepoint setSavepoint() throws SQLException { + checkClosed(); + + if (isInGlobalTx()) { + throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + return this.mc.setSavepoint(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#setSavepoint(String) + */ + public java.sql.Savepoint setSavepoint(String arg0) throws SQLException { + checkClosed(); + + if (isInGlobalTx()) { + throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + return this.mc.setSavepoint(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#setTransactionIsolation() + */ + public void setTransactionIsolation(int level) throws SQLException { + checkClosed(); + + try { + this.mc.setTransactionIsolation(level); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#getTransactionIsolation() + */ + public int getTransactionIsolation() throws SQLException { + checkClosed(); + + try { + return this.mc.getTransactionIsolation(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return TRANSACTION_REPEATABLE_READ; // we don't reach this code, + // compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#getTypeMap() + */ + public java.util.Map> getTypeMap() throws SQLException { + checkClosed(); + + try { + return this.mc.getTypeMap(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#getWarnings + */ + public java.sql.SQLWarning getWarnings() throws SQLException { + checkClosed(); + + try { + return this.mc.getWarnings(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @throws SQLException + * if an error occurs + */ + public void clearWarnings() throws SQLException { + checkClosed(); + + try { + this.mc.clearWarnings(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * The physical connection is not actually closed. the physical connection + * is closed when the application server calls + * mysqlPooledConnection.close(). this object is de-referenced by the pooled + * connection each time mysqlPooledConnection.getConnection() is called by + * app server. + * + * @throws SQLException + * if an error occurs + */ + public void close() throws SQLException { + close(true); + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @throws SQLException + * if an error occurs + */ + public void commit() throws SQLException { + checkClosed(); + + if (isInGlobalTx()) { + throw SQLError.createSQLException("Can't call commit() on an XAConnection associated with a global transaction", + SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.commit(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#createStatement() + */ + public java.sql.Statement createStatement() throws SQLException { + checkClosed(); + + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement()); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#createStatement() + */ + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + checkClosed(); + + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement(resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#createStatement(int, int, int) + */ + public java.sql.Statement createStatement(int arg0, int arg1, int arg2) throws SQLException { + checkClosed(); + + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement(arg0, arg1, arg2)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#nativeSQL() + */ + public String nativeSQL(String sql) throws SQLException { + checkClosed(); + + try { + return this.mc.nativeSQL(sql); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#prepareCall() + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException { + checkClosed(); + + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#prepareCall() + */ + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + checkClosed(); + + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#prepareCall(String, int, int, int) + */ + public java.sql.CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException { + checkClosed(); + + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(arg0, arg1, arg2, arg3)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement clientPrepare(String sql) throws SQLException { + checkClosed(); + + try { + return new PreparedStatementWrapper(this, this.pooledConnection, this.mc.clientPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepare(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + checkClosed(); + + try { + return new PreparedStatementWrapper(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#prepareStatement() + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { + checkClosed(); + + java.sql.PreparedStatement res = null; + + try { + res = PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return res; + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#prepareStatement() + */ + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#prepareStatement(String, int, int, int) + */ + public java.sql.PreparedStatement prepareStatement(String arg0, int arg1, int arg2, int arg3) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1, arg2, arg3)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#prepareStatement(String, int) + */ + public java.sql.PreparedStatement prepareStatement(String arg0, int arg1) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#prepareStatement(String, int[]) + */ + public java.sql.PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#prepareStatement(String, String[]) + */ + public java.sql.PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + /** + * @see Connection#releaseSavepoint(Savepoint) + */ + public void releaseSavepoint(Savepoint arg0) throws SQLException { + checkClosed(); + + try { + this.mc.releaseSavepoint(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * Passes call to method on physical connection instance. Notifies listeners + * of any caught exceptions before re-throwing to client. + * + * @see java.sql.Connection#rollback() + */ + public void rollback() throws SQLException { + checkClosed(); + + if (isInGlobalTx()) { + throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction", + SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.rollback(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * @see Connection#rollback(Savepoint) + */ + public void rollback(Savepoint arg0) throws SQLException { + checkClosed(); + + if (isInGlobalTx()) { + throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction", + SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.rollback(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean isSameResource(com.mysql.jdbc.Connection c) { + if (c instanceof ConnectionWrapper) { + return this.mc.isSameResource(((ConnectionWrapper) c).mc); + } + return this.mc.isSameResource(c); + } + + protected void close(boolean fireClosedEvent) throws SQLException { + synchronized (this.pooledConnection) { + if (this.closed) { + return; + } + + if (!isInGlobalTx() && this.mc.getRollbackOnPooledClose() && !this.getAutoCommit()) { + rollback(); + } + + if (fireClosedEvent) { + this.pooledConnection.callConnectionEventListeners(MysqlPooledConnection.CONNECTION_CLOSED_EVENT, null); + } + + // set closed status to true so that if application client tries to make additional calls a sqlException will be thrown. The physical connection is + // re-used by the pooled connection each time getConnection is called. + this.closed = true; + } + } + + public void checkClosed() throws SQLException { + if (this.closed) { + throw SQLError.createSQLException(this.invalidHandleStr, this.exceptionInterceptor); + } + } + + public boolean isInGlobalTx() { + return this.mc.isInGlobalTx(); + } + + public void setInGlobalTx(boolean flag) { + this.mc.setInGlobalTx(flag); + } + + public void ping() throws SQLException { + if (this.mc != null) { + this.mc.ping(); + } + } + + public void changeUser(String userName, String newPassword) throws SQLException { + checkClosed(); + + try { + this.mc.changeUser(userName, newPassword); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + @Deprecated + public void clearHasTriedMaster() { + this.mc.clearHasTriedMaster(); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyIndex)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, + this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyIndexes)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyColNames)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public int getActiveStatementCount() { + return this.mc.getActiveStatementCount(); + } + + public Log getLog() throws SQLException { + return this.mc.getLog(); + } + + /** + * @deprecated replaced by getServerCharset() + */ + @Deprecated + public String getServerCharacterEncoding() { + return getServerCharset(); + } + + public String getServerCharset() { + return this.mc.getServerCharset(); + } + + public TimeZone getServerTimezoneTZ() { + return this.mc.getServerTimezoneTZ(); + } + + public String getStatementComment() { + return this.mc.getStatementComment(); + } + + @Deprecated + public boolean hasTriedMaster() { + return this.mc.hasTriedMaster(); + } + + public boolean isAbonormallyLongQuery(long millisOrNanos) { + return this.mc.isAbonormallyLongQuery(millisOrNanos); + } + + public boolean isNoBackslashEscapesSet() { + return this.mc.isNoBackslashEscapesSet(); + } + + public boolean lowerCaseTableNames() { + return this.mc.lowerCaseTableNames(); + } + + public boolean parserKnowsUnicode() { + return this.mc.parserKnowsUnicode(); + } + + public void reportQueryTime(long millisOrNanos) { + this.mc.reportQueryTime(millisOrNanos); + } + + public void resetServerState() throws SQLException { + checkClosed(); + + try { + this.mc.resetServerState(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException { + checkClosed(); + + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyIndex)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, + this.mc.serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyIndexes)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyColNames)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public void setFailedOver(boolean flag) { + this.mc.setFailedOver(flag); + + } + + @Deprecated + public void setPreferSlaveDuringFailover(boolean flag) { + this.mc.setPreferSlaveDuringFailover(flag); + } + + public void setStatementComment(String comment) { + this.mc.setStatementComment(comment); + + } + + public void shutdownServer() throws SQLException { + checkClosed(); + + try { + this.mc.shutdownServer(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + } + + public boolean supportsIsolationLevel() { + return this.mc.supportsIsolationLevel(); + } + + public boolean supportsQuotedIdentifiers() { + return this.mc.supportsQuotedIdentifiers(); + } + + public boolean supportsTransactions() { + return this.mc.supportsTransactions(); + } + + public boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException { + checkClosed(); + + try { + return this.mc.versionMeetsMinimum(major, minor, subminor); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; + } + + public String exposeAsXml() throws SQLException { + checkClosed(); + + try { + return this.mc.exposeAsXml(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public boolean getAllowLoadLocalInfile() { + return this.mc.getAllowLoadLocalInfile(); + } + + public boolean getAllowMultiQueries() { + return this.mc.getAllowMultiQueries(); + } + + public boolean getAllowNanAndInf() { + return this.mc.getAllowNanAndInf(); + } + + public boolean getAllowUrlInLocalInfile() { + return this.mc.getAllowUrlInLocalInfile(); + } + + public boolean getAlwaysSendSetIsolation() { + return this.mc.getAlwaysSendSetIsolation(); + } + + public boolean getAutoClosePStmtStreams() { + return this.mc.getAutoClosePStmtStreams(); + } + + public boolean getAutoDeserialize() { + return this.mc.getAutoDeserialize(); + } + + public boolean getAutoGenerateTestcaseScript() { + return this.mc.getAutoGenerateTestcaseScript(); + } + + public boolean getAutoReconnectForPools() { + return this.mc.getAutoReconnectForPools(); + } + + public boolean getAutoSlowLog() { + return this.mc.getAutoSlowLog(); + } + + public int getBlobSendChunkSize() { + return this.mc.getBlobSendChunkSize(); + } + + public boolean getBlobsAreStrings() { + return this.mc.getBlobsAreStrings(); + } + + public boolean getCacheCallableStatements() { + return this.mc.getCacheCallableStatements(); + } + + public boolean getCacheCallableStmts() { + return this.mc.getCacheCallableStmts(); + } + + public boolean getCachePrepStmts() { + return this.mc.getCachePrepStmts(); + } + + public boolean getCachePreparedStatements() { + return this.mc.getCachePreparedStatements(); + } + + public boolean getCacheResultSetMetadata() { + return this.mc.getCacheResultSetMetadata(); + } + + public boolean getCacheServerConfiguration() { + return this.mc.getCacheServerConfiguration(); + } + + public int getCallableStatementCacheSize() { + return this.mc.getCallableStatementCacheSize(); + } + + public int getCallableStmtCacheSize() { + return this.mc.getCallableStmtCacheSize(); + } + + public boolean getCapitalizeTypeNames() { + return this.mc.getCapitalizeTypeNames(); + } + + public String getCharacterSetResults() { + return this.mc.getCharacterSetResults(); + } + + public String getClientCertificateKeyStorePassword() { + return this.mc.getClientCertificateKeyStorePassword(); + } + + public String getClientCertificateKeyStoreType() { + return this.mc.getClientCertificateKeyStoreType(); + } + + public String getClientCertificateKeyStoreUrl() { + return this.mc.getClientCertificateKeyStoreUrl(); + } + + public String getClientInfoProvider() { + return this.mc.getClientInfoProvider(); + } + + public String getClobCharacterEncoding() { + return this.mc.getClobCharacterEncoding(); + } + + public boolean getClobberStreamingResults() { + return this.mc.getClobberStreamingResults(); + } + + public int getConnectTimeout() { + return this.mc.getConnectTimeout(); + } + + public String getConnectionCollation() { + return this.mc.getConnectionCollation(); + } + + public String getConnectionLifecycleInterceptors() { + return this.mc.getConnectionLifecycleInterceptors(); + } + + public boolean getContinueBatchOnError() { + return this.mc.getContinueBatchOnError(); + } + + public boolean getCreateDatabaseIfNotExist() { + return this.mc.getCreateDatabaseIfNotExist(); + } + + public int getDefaultFetchSize() { + return this.mc.getDefaultFetchSize(); + } + + public boolean getDontTrackOpenResources() { + return this.mc.getDontTrackOpenResources(); + } + + public boolean getDumpMetadataOnColumnNotFound() { + return this.mc.getDumpMetadataOnColumnNotFound(); + } + + public boolean getDumpQueriesOnException() { + return this.mc.getDumpQueriesOnException(); + } + + public boolean getDynamicCalendars() { + return this.mc.getDynamicCalendars(); + } + + public boolean getElideSetAutoCommits() { + return this.mc.getElideSetAutoCommits(); + } + + public boolean getEmptyStringsConvertToZero() { + return this.mc.getEmptyStringsConvertToZero(); + } + + public boolean getEmulateLocators() { + return this.mc.getEmulateLocators(); + } + + public boolean getEmulateUnsupportedPstmts() { + return this.mc.getEmulateUnsupportedPstmts(); + } + + public boolean getEnablePacketDebug() { + return this.mc.getEnablePacketDebug(); + } + + public boolean getEnableQueryTimeouts() { + return this.mc.getEnableQueryTimeouts(); + } + + public String getEncoding() { + return this.mc.getEncoding(); + } + + public boolean getExplainSlowQueries() { + return this.mc.getExplainSlowQueries(); + } + + public boolean getFailOverReadOnly() { + return this.mc.getFailOverReadOnly(); + } + + public boolean getFunctionsNeverReturnBlobs() { + return this.mc.getFunctionsNeverReturnBlobs(); + } + + public boolean getGatherPerfMetrics() { + return this.mc.getGatherPerfMetrics(); + } + + public boolean getGatherPerformanceMetrics() { + return this.mc.getGatherPerformanceMetrics(); + } + + public boolean getGenerateSimpleParameterMetadata() { + return this.mc.getGenerateSimpleParameterMetadata(); + } + + public boolean getHoldResultsOpenOverStatementClose() { + return this.mc.getHoldResultsOpenOverStatementClose(); + } + + public boolean getIgnoreNonTxTables() { + return this.mc.getIgnoreNonTxTables(); + } + + public boolean getIncludeInnodbStatusInDeadlockExceptions() { + return this.mc.getIncludeInnodbStatusInDeadlockExceptions(); + } + + public int getInitialTimeout() { + return this.mc.getInitialTimeout(); + } + + public boolean getInteractiveClient() { + return this.mc.getInteractiveClient(); + } + + public boolean getIsInteractiveClient() { + return this.mc.getIsInteractiveClient(); + } + + public boolean getJdbcCompliantTruncation() { + return this.mc.getJdbcCompliantTruncation(); + } + + public boolean getJdbcCompliantTruncationForReads() { + return this.mc.getJdbcCompliantTruncationForReads(); + } + + public String getLargeRowSizeThreshold() { + return this.mc.getLargeRowSizeThreshold(); + } + + public String getLoadBalanceStrategy() { + return this.mc.getLoadBalanceStrategy(); + } + + public String getLocalSocketAddress() { + return this.mc.getLocalSocketAddress(); + } + + public int getLocatorFetchBufferSize() { + return this.mc.getLocatorFetchBufferSize(); + } + + public boolean getLogSlowQueries() { + return this.mc.getLogSlowQueries(); + } + + public boolean getLogXaCommands() { + return this.mc.getLogXaCommands(); + } + + public String getLogger() { + return this.mc.getLogger(); + } + + public String getLoggerClassName() { + return this.mc.getLoggerClassName(); + } + + public boolean getMaintainTimeStats() { + return this.mc.getMaintainTimeStats(); + } + + public int getMaxQuerySizeToLog() { + return this.mc.getMaxQuerySizeToLog(); + } + + public int getMaxReconnects() { + return this.mc.getMaxReconnects(); + } + + public int getMaxRows() { + return this.mc.getMaxRows(); + } + + public int getMetadataCacheSize() { + return this.mc.getMetadataCacheSize(); + } + + public int getNetTimeoutForStreamingResults() { + return this.mc.getNetTimeoutForStreamingResults(); + } + + public boolean getNoAccessToProcedureBodies() { + return this.mc.getNoAccessToProcedureBodies(); + } + + public boolean getNoDatetimeStringSync() { + return this.mc.getNoDatetimeStringSync(); + } + + public boolean getNoTimezoneConversionForTimeType() { + return this.mc.getNoTimezoneConversionForTimeType(); + } + + public boolean getNoTimezoneConversionForDateType() { + return this.mc.getNoTimezoneConversionForDateType(); + } + + public boolean getCacheDefaultTimezone() { + return this.mc.getCacheDefaultTimezone(); + } + + public boolean getNullCatalogMeansCurrent() { + return this.mc.getNullCatalogMeansCurrent(); + } + + public boolean getNullNamePatternMatchesAll() { + return this.mc.getNullNamePatternMatchesAll(); + } + + public boolean getOverrideSupportsIntegrityEnhancementFacility() { + return this.mc.getOverrideSupportsIntegrityEnhancementFacility(); + } + + public int getPacketDebugBufferSize() { + return this.mc.getPacketDebugBufferSize(); + } + + public boolean getPadCharsWithSpace() { + return this.mc.getPadCharsWithSpace(); + } + + public boolean getParanoid() { + return this.mc.getParanoid(); + } + + public boolean getPedantic() { + return this.mc.getPedantic(); + } + + public boolean getPinGlobalTxToPhysicalConnection() { + return this.mc.getPinGlobalTxToPhysicalConnection(); + } + + public boolean getPopulateInsertRowWithDefaultValues() { + return this.mc.getPopulateInsertRowWithDefaultValues(); + } + + public int getPrepStmtCacheSize() { + return this.mc.getPrepStmtCacheSize(); + } + + public int getPrepStmtCacheSqlLimit() { + return this.mc.getPrepStmtCacheSqlLimit(); + } + + public int getPreparedStatementCacheSize() { + return this.mc.getPreparedStatementCacheSize(); + } + + public int getPreparedStatementCacheSqlLimit() { + return this.mc.getPreparedStatementCacheSqlLimit(); + } + + public boolean getProcessEscapeCodesForPrepStmts() { + return this.mc.getProcessEscapeCodesForPrepStmts(); + } + + public boolean getProfileSQL() { + return this.mc.getProfileSQL(); + } + + public boolean getProfileSql() { + return this.mc.getProfileSql(); + } + + public String getPropertiesTransform() { + return this.mc.getPropertiesTransform(); + } + + public int getQueriesBeforeRetryMaster() { + return this.mc.getQueriesBeforeRetryMaster(); + } + + public boolean getReconnectAtTxEnd() { + return this.mc.getReconnectAtTxEnd(); + } + + public boolean getRelaxAutoCommit() { + return this.mc.getRelaxAutoCommit(); + } + + public int getReportMetricsIntervalMillis() { + return this.mc.getReportMetricsIntervalMillis(); + } + + public boolean getRequireSSL() { + return this.mc.getRequireSSL(); + } + + public String getResourceId() { + return this.mc.getResourceId(); + } + + public int getResultSetSizeThreshold() { + return this.mc.getResultSetSizeThreshold(); + } + + public boolean getRewriteBatchedStatements() { + return this.mc.getRewriteBatchedStatements(); + } + + public boolean getRollbackOnPooledClose() { + return this.mc.getRollbackOnPooledClose(); + } + + public boolean getRoundRobinLoadBalance() { + return this.mc.getRoundRobinLoadBalance(); + } + + public boolean getRunningCTS13() { + return this.mc.getRunningCTS13(); + } + + public int getSecondsBeforeRetryMaster() { + return this.mc.getSecondsBeforeRetryMaster(); + } + + public String getServerTimezone() { + return this.mc.getServerTimezone(); + } + + public String getSessionVariables() { + return this.mc.getSessionVariables(); + } + + public int getSlowQueryThresholdMillis() { + return this.mc.getSlowQueryThresholdMillis(); + } + + public long getSlowQueryThresholdNanos() { + return this.mc.getSlowQueryThresholdNanos(); + } + + public String getSocketFactory() { + return this.mc.getSocketFactory(); + } + + public String getSocketFactoryClassName() { + return this.mc.getSocketFactoryClassName(); + } + + public int getSocketTimeout() { + return this.mc.getSocketTimeout(); + } + + public String getStatementInterceptors() { + return this.mc.getStatementInterceptors(); + } + + public boolean getStrictFloatingPoint() { + return this.mc.getStrictFloatingPoint(); + } + + public boolean getStrictUpdates() { + return this.mc.getStrictUpdates(); + } + + public boolean getTcpKeepAlive() { + return this.mc.getTcpKeepAlive(); + } + + public boolean getTcpNoDelay() { + return this.mc.getTcpNoDelay(); + } + + public int getTcpRcvBuf() { + return this.mc.getTcpRcvBuf(); + } + + public int getTcpSndBuf() { + return this.mc.getTcpSndBuf(); + } + + public int getTcpTrafficClass() { + return this.mc.getTcpTrafficClass(); + } + + public boolean getTinyInt1isBit() { + return this.mc.getTinyInt1isBit(); + } + + public boolean getTraceProtocol() { + return this.mc.getTraceProtocol(); + } + + public boolean getTransformedBitIsBoolean() { + return this.mc.getTransformedBitIsBoolean(); + } + + public boolean getTreatUtilDateAsTimestamp() { + return this.mc.getTreatUtilDateAsTimestamp(); + } + + public String getTrustCertificateKeyStorePassword() { + return this.mc.getTrustCertificateKeyStorePassword(); + } + + public String getTrustCertificateKeyStoreType() { + return this.mc.getTrustCertificateKeyStoreType(); + } + + public String getTrustCertificateKeyStoreUrl() { + return this.mc.getTrustCertificateKeyStoreUrl(); + } + + public boolean getUltraDevHack() { + return this.mc.getUltraDevHack(); + } + + public boolean getUseBlobToStoreUTF8OutsideBMP() { + return this.mc.getUseBlobToStoreUTF8OutsideBMP(); + } + + public boolean getUseCompression() { + return this.mc.getUseCompression(); + } + + public String getUseConfigs() { + return this.mc.getUseConfigs(); + } + + public boolean getUseCursorFetch() { + return this.mc.getUseCursorFetch(); + } + + public boolean getUseDirectRowUnpack() { + return this.mc.getUseDirectRowUnpack(); + } + + public boolean getUseDynamicCharsetInfo() { + return this.mc.getUseDynamicCharsetInfo(); + } + + public boolean getUseFastDateParsing() { + return this.mc.getUseFastDateParsing(); + } + + public boolean getUseFastIntParsing() { + return this.mc.getUseFastIntParsing(); + } + + public boolean getUseGmtMillisForDatetimes() { + return this.mc.getUseGmtMillisForDatetimes(); + } + + public boolean getUseHostsInPrivileges() { + return this.mc.getUseHostsInPrivileges(); + } + + public boolean getUseInformationSchema() { + return this.mc.getUseInformationSchema(); + } + + public boolean getUseJDBCCompliantTimezoneShift() { + return this.mc.getUseJDBCCompliantTimezoneShift(); + } + + public boolean getUseJvmCharsetConverters() { + return this.mc.getUseJvmCharsetConverters(); + } + + public boolean getUseLocalSessionState() { + return this.mc.getUseLocalSessionState(); + } + + public boolean getUseNanosForElapsedTime() { + return this.mc.getUseNanosForElapsedTime(); + } + + public boolean getUseOldAliasMetadataBehavior() { + return this.mc.getUseOldAliasMetadataBehavior(); + } + + public boolean getUseOldUTF8Behavior() { + return this.mc.getUseOldUTF8Behavior(); + } + + public boolean getUseOnlyServerErrorMessages() { + return this.mc.getUseOnlyServerErrorMessages(); + } + + public boolean getUseReadAheadInput() { + return this.mc.getUseReadAheadInput(); + } + + public boolean getUseSSL() { + return this.mc.getUseSSL(); + } + + public boolean getUseSSPSCompatibleTimezoneShift() { + return this.mc.getUseSSPSCompatibleTimezoneShift(); + } + + public boolean getUseServerPrepStmts() { + return this.mc.getUseServerPrepStmts(); + } + + public boolean getUseServerPreparedStmts() { + return this.mc.getUseServerPreparedStmts(); + } + + public boolean getUseSqlStateCodes() { + return this.mc.getUseSqlStateCodes(); + } + + public boolean getUseStreamLengthsInPrepStmts() { + return this.mc.getUseStreamLengthsInPrepStmts(); + } + + public boolean getUseTimezone() { + return this.mc.getUseTimezone(); + } + + public boolean getUseUltraDevWorkAround() { + return this.mc.getUseUltraDevWorkAround(); + } + + public boolean getUseUnbufferedInput() { + return this.mc.getUseUnbufferedInput(); + } + + public boolean getUseUnicode() { + return this.mc.getUseUnicode(); + } + + public boolean getUseUsageAdvisor() { + return this.mc.getUseUsageAdvisor(); + } + + public String getUtf8OutsideBmpExcludedColumnNamePattern() { + return this.mc.getUtf8OutsideBmpExcludedColumnNamePattern(); + } + + public String getUtf8OutsideBmpIncludedColumnNamePattern() { + return this.mc.getUtf8OutsideBmpIncludedColumnNamePattern(); + } + + public boolean getYearIsDateType() { + return this.mc.getYearIsDateType(); + } + + public String getZeroDateTimeBehavior() { + return this.mc.getZeroDateTimeBehavior(); + } + + public void setAllowLoadLocalInfile(boolean property) { + this.mc.setAllowLoadLocalInfile(property); + } + + public void setAllowMultiQueries(boolean property) { + this.mc.setAllowMultiQueries(property); + } + + public void setAllowNanAndInf(boolean flag) { + this.mc.setAllowNanAndInf(flag); + } + + public void setAllowUrlInLocalInfile(boolean flag) { + this.mc.setAllowUrlInLocalInfile(flag); + } + + public void setAlwaysSendSetIsolation(boolean flag) { + this.mc.setAlwaysSendSetIsolation(flag); + } + + public void setAutoClosePStmtStreams(boolean flag) { + this.mc.setAutoClosePStmtStreams(flag); + } + + public void setAutoDeserialize(boolean flag) { + this.mc.setAutoDeserialize(flag); + } + + public void setAutoGenerateTestcaseScript(boolean flag) { + this.mc.setAutoGenerateTestcaseScript(flag); + } + + public void setAutoReconnect(boolean flag) { + this.mc.setAutoReconnect(flag); + } + + public void setAutoReconnectForConnectionPools(boolean property) { + this.mc.setAutoReconnectForConnectionPools(property); + } + + public void setAutoReconnectForPools(boolean flag) { + this.mc.setAutoReconnectForPools(flag); + } + + public void setAutoSlowLog(boolean flag) { + this.mc.setAutoSlowLog(flag); + } + + public void setBlobSendChunkSize(String value) throws SQLException { + this.mc.setBlobSendChunkSize(value); + } + + public void setBlobsAreStrings(boolean flag) { + this.mc.setBlobsAreStrings(flag); + } + + public void setCacheCallableStatements(boolean flag) { + this.mc.setCacheCallableStatements(flag); + } + + public void setCacheCallableStmts(boolean flag) { + this.mc.setCacheCallableStmts(flag); + } + + public void setCachePrepStmts(boolean flag) { + this.mc.setCachePrepStmts(flag); + } + + public void setCachePreparedStatements(boolean flag) { + this.mc.setCachePreparedStatements(flag); + } + + public void setCacheResultSetMetadata(boolean property) { + this.mc.setCacheResultSetMetadata(property); + } + + public void setCacheServerConfiguration(boolean flag) { + this.mc.setCacheServerConfiguration(flag); + } + + public void setCallableStatementCacheSize(int size) throws SQLException { + this.mc.setCallableStatementCacheSize(size); + } + + public void setCallableStmtCacheSize(int cacheSize) throws SQLException { + this.mc.setCallableStmtCacheSize(cacheSize); + } + + public void setCapitalizeDBMDTypes(boolean property) { + this.mc.setCapitalizeDBMDTypes(property); + } + + public void setCapitalizeTypeNames(boolean flag) { + this.mc.setCapitalizeTypeNames(flag); + } + + public void setCharacterEncoding(String encoding) { + this.mc.setCharacterEncoding(encoding); + } + + public void setCharacterSetResults(String characterSet) { + this.mc.setCharacterSetResults(characterSet); + } + + public void setClientCertificateKeyStorePassword(String value) { + this.mc.setClientCertificateKeyStorePassword(value); + } + + public void setClientCertificateKeyStoreType(String value) { + this.mc.setClientCertificateKeyStoreType(value); + } + + public void setClientCertificateKeyStoreUrl(String value) { + this.mc.setClientCertificateKeyStoreUrl(value); + } + + public void setClientInfoProvider(String classname) { + this.mc.setClientInfoProvider(classname); + } + + public void setClobCharacterEncoding(String encoding) { + this.mc.setClobCharacterEncoding(encoding); + } + + public void setClobberStreamingResults(boolean flag) { + this.mc.setClobberStreamingResults(flag); + } + + public void setConnectTimeout(int timeoutMs) throws SQLException { + this.mc.setConnectTimeout(timeoutMs); + } + + public void setConnectionCollation(String collation) { + this.mc.setConnectionCollation(collation); + } + + public void setConnectionLifecycleInterceptors(String interceptors) { + this.mc.setConnectionLifecycleInterceptors(interceptors); + } + + public void setContinueBatchOnError(boolean property) { + this.mc.setContinueBatchOnError(property); + } + + public void setCreateDatabaseIfNotExist(boolean flag) { + this.mc.setCreateDatabaseIfNotExist(flag); + } + + public void setDefaultFetchSize(int n) throws SQLException { + this.mc.setDefaultFetchSize(n); + } + + public void setDetectServerPreparedStmts(boolean property) { + this.mc.setDetectServerPreparedStmts(property); + } + + public void setDontTrackOpenResources(boolean flag) { + this.mc.setDontTrackOpenResources(flag); + } + + public void setDumpMetadataOnColumnNotFound(boolean flag) { + this.mc.setDumpMetadataOnColumnNotFound(flag); + } + + public void setDumpQueriesOnException(boolean flag) { + this.mc.setDumpQueriesOnException(flag); + } + + public void setDynamicCalendars(boolean flag) { + this.mc.setDynamicCalendars(flag); + } + + public void setElideSetAutoCommits(boolean flag) { + this.mc.setElideSetAutoCommits(flag); + } + + public void setEmptyStringsConvertToZero(boolean flag) { + this.mc.setEmptyStringsConvertToZero(flag); + } + + public void setEmulateLocators(boolean property) { + this.mc.setEmulateLocators(property); + } + + public void setEmulateUnsupportedPstmts(boolean flag) { + this.mc.setEmulateUnsupportedPstmts(flag); + } + + public void setEnablePacketDebug(boolean flag) { + this.mc.setEnablePacketDebug(flag); + } + + public void setEnableQueryTimeouts(boolean flag) { + this.mc.setEnableQueryTimeouts(flag); + } + + public void setEncoding(String property) { + this.mc.setEncoding(property); + } + + public void setExplainSlowQueries(boolean flag) { + this.mc.setExplainSlowQueries(flag); + } + + public void setFailOverReadOnly(boolean flag) { + this.mc.setFailOverReadOnly(flag); + } + + public void setFunctionsNeverReturnBlobs(boolean flag) { + this.mc.setFunctionsNeverReturnBlobs(flag); + } + + public void setGatherPerfMetrics(boolean flag) { + this.mc.setGatherPerfMetrics(flag); + } + + public void setGatherPerformanceMetrics(boolean flag) { + this.mc.setGatherPerformanceMetrics(flag); + } + + public void setGenerateSimpleParameterMetadata(boolean flag) { + this.mc.setGenerateSimpleParameterMetadata(flag); + } + + public void setHoldResultsOpenOverStatementClose(boolean flag) { + this.mc.setHoldResultsOpenOverStatementClose(flag); + } + + public void setIgnoreNonTxTables(boolean property) { + this.mc.setIgnoreNonTxTables(property); + } + + public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) { + this.mc.setIncludeInnodbStatusInDeadlockExceptions(flag); + } + + public void setInitialTimeout(int property) throws SQLException { + this.mc.setInitialTimeout(property); + } + + public void setInteractiveClient(boolean property) { + this.mc.setInteractiveClient(property); + } + + public void setIsInteractiveClient(boolean property) { + this.mc.setIsInteractiveClient(property); + } + + public void setJdbcCompliantTruncation(boolean flag) { + this.mc.setJdbcCompliantTruncation(flag); + } + + public void setJdbcCompliantTruncationForReads(boolean jdbcCompliantTruncationForReads) { + this.mc.setJdbcCompliantTruncationForReads(jdbcCompliantTruncationForReads); + } + + public void setLargeRowSizeThreshold(String value) throws SQLException { + this.mc.setLargeRowSizeThreshold(value); + } + + public void setLoadBalanceStrategy(String strategy) { + this.mc.setLoadBalanceStrategy(strategy); + } + + public void setLocalSocketAddress(String address) { + this.mc.setLocalSocketAddress(address); + } + + public void setLocatorFetchBufferSize(String value) throws SQLException { + this.mc.setLocatorFetchBufferSize(value); + } + + public void setLogSlowQueries(boolean flag) { + this.mc.setLogSlowQueries(flag); + } + + public void setLogXaCommands(boolean flag) { + this.mc.setLogXaCommands(flag); + } + + public void setLogger(String property) { + this.mc.setLogger(property); + } + + public void setLoggerClassName(String className) { + this.mc.setLoggerClassName(className); + } + + public void setMaintainTimeStats(boolean flag) { + this.mc.setMaintainTimeStats(flag); + } + + public void setMaxQuerySizeToLog(int sizeInBytes) throws SQLException { + this.mc.setMaxQuerySizeToLog(sizeInBytes); + } + + public void setMaxReconnects(int property) throws SQLException { + this.mc.setMaxReconnects(property); + } + + public void setMaxRows(int property) throws SQLException { + this.mc.setMaxRows(property); + } + + public void setMetadataCacheSize(int value) throws SQLException { + this.mc.setMetadataCacheSize(value); + } + + public void setNetTimeoutForStreamingResults(int value) throws SQLException { + this.mc.setNetTimeoutForStreamingResults(value); + } + + public void setNoAccessToProcedureBodies(boolean flag) { + this.mc.setNoAccessToProcedureBodies(flag); + } + + public void setNoDatetimeStringSync(boolean flag) { + this.mc.setNoDatetimeStringSync(flag); + } + + public void setNoTimezoneConversionForTimeType(boolean flag) { + this.mc.setNoTimezoneConversionForTimeType(flag); + } + + public void setNoTimezoneConversionForDateType(boolean flag) { + this.mc.setNoTimezoneConversionForDateType(flag); + } + + public void setCacheDefaultTimezone(boolean flag) { + this.mc.setCacheDefaultTimezone(flag); + } + + public void setNullCatalogMeansCurrent(boolean value) { + this.mc.setNullCatalogMeansCurrent(value); + } + + public void setNullNamePatternMatchesAll(boolean value) { + this.mc.setNullNamePatternMatchesAll(value); + } + + public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) { + this.mc.setOverrideSupportsIntegrityEnhancementFacility(flag); + } + + public void setPacketDebugBufferSize(int size) throws SQLException { + this.mc.setPacketDebugBufferSize(size); + } + + public void setPadCharsWithSpace(boolean flag) { + this.mc.setPadCharsWithSpace(flag); + } + + public void setParanoid(boolean property) { + this.mc.setParanoid(property); + } + + public void setPedantic(boolean property) { + this.mc.setPedantic(property); + } + + public void setPinGlobalTxToPhysicalConnection(boolean flag) { + this.mc.setPinGlobalTxToPhysicalConnection(flag); + } + + public void setPopulateInsertRowWithDefaultValues(boolean flag) { + this.mc.setPopulateInsertRowWithDefaultValues(flag); + } + + public void setPrepStmtCacheSize(int cacheSize) throws SQLException { + this.mc.setPrepStmtCacheSize(cacheSize); + } + + public void setPrepStmtCacheSqlLimit(int sqlLimit) throws SQLException { + this.mc.setPrepStmtCacheSqlLimit(sqlLimit); + } + + public void setPreparedStatementCacheSize(int cacheSize) throws SQLException { + this.mc.setPreparedStatementCacheSize(cacheSize); + } + + public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) throws SQLException { + this.mc.setPreparedStatementCacheSqlLimit(cacheSqlLimit); + } + + public void setProcessEscapeCodesForPrepStmts(boolean flag) { + this.mc.setProcessEscapeCodesForPrepStmts(flag); + } + + public void setProfileSQL(boolean flag) { + this.mc.setProfileSQL(flag); + } + + public void setProfileSql(boolean property) { + this.mc.setProfileSql(property); + } + + public void setPropertiesTransform(String value) { + this.mc.setPropertiesTransform(value); + } + + public void setQueriesBeforeRetryMaster(int property) throws SQLException { + this.mc.setQueriesBeforeRetryMaster(property); + } + + public void setReconnectAtTxEnd(boolean property) { + this.mc.setReconnectAtTxEnd(property); + } + + public void setRelaxAutoCommit(boolean property) { + this.mc.setRelaxAutoCommit(property); + } + + public void setReportMetricsIntervalMillis(int millis) throws SQLException { + this.mc.setReportMetricsIntervalMillis(millis); + } + + public void setRequireSSL(boolean property) { + this.mc.setRequireSSL(property); + } + + public void setResourceId(String resourceId) { + this.mc.setResourceId(resourceId); + } + + public void setResultSetSizeThreshold(int threshold) throws SQLException { + this.mc.setResultSetSizeThreshold(threshold); + } + + public void setRetainStatementAfterResultSetClose(boolean flag) { + this.mc.setRetainStatementAfterResultSetClose(flag); + } + + public void setRewriteBatchedStatements(boolean flag) { + this.mc.setRewriteBatchedStatements(flag); + } + + public void setRollbackOnPooledClose(boolean flag) { + this.mc.setRollbackOnPooledClose(flag); + } + + public void setRoundRobinLoadBalance(boolean flag) { + this.mc.setRoundRobinLoadBalance(flag); + } + + public void setRunningCTS13(boolean flag) { + this.mc.setRunningCTS13(flag); + } + + public void setSecondsBeforeRetryMaster(int property) throws SQLException { + this.mc.setSecondsBeforeRetryMaster(property); + } + + public void setServerTimezone(String property) { + this.mc.setServerTimezone(property); + } + + public void setSessionVariables(String variables) { + this.mc.setSessionVariables(variables); + } + + public void setSlowQueryThresholdMillis(int millis) throws SQLException { + this.mc.setSlowQueryThresholdMillis(millis); + } + + public void setSlowQueryThresholdNanos(long nanos) throws SQLException { + this.mc.setSlowQueryThresholdNanos(nanos); + } + + public void setSocketFactory(String name) { + this.mc.setSocketFactory(name); + } + + public void setSocketFactoryClassName(String property) { + this.mc.setSocketFactoryClassName(property); + } + + public void setSocketTimeout(int property) throws SQLException { + this.mc.setSocketTimeout(property); + } + + public void setStatementInterceptors(String value) { + this.mc.setStatementInterceptors(value); + } + + public void setStrictFloatingPoint(boolean property) { + this.mc.setStrictFloatingPoint(property); + } + + public void setStrictUpdates(boolean property) { + this.mc.setStrictUpdates(property); + } + + public void setTcpKeepAlive(boolean flag) { + this.mc.setTcpKeepAlive(flag); + } + + public void setTcpNoDelay(boolean flag) { + this.mc.setTcpNoDelay(flag); + } + + public void setTcpRcvBuf(int bufSize) throws SQLException { + this.mc.setTcpRcvBuf(bufSize); + } + + public void setTcpSndBuf(int bufSize) throws SQLException { + this.mc.setTcpSndBuf(bufSize); + } + + public void setTcpTrafficClass(int classFlags) throws SQLException { + this.mc.setTcpTrafficClass(classFlags); + } + + public void setTinyInt1isBit(boolean flag) { + this.mc.setTinyInt1isBit(flag); + } + + public void setTraceProtocol(boolean flag) { + this.mc.setTraceProtocol(flag); + } + + public void setTransformedBitIsBoolean(boolean flag) { + this.mc.setTransformedBitIsBoolean(flag); + } + + public void setTreatUtilDateAsTimestamp(boolean flag) { + this.mc.setTreatUtilDateAsTimestamp(flag); + } + + public void setTrustCertificateKeyStorePassword(String value) { + this.mc.setTrustCertificateKeyStorePassword(value); + } + + public void setTrustCertificateKeyStoreType(String value) { + this.mc.setTrustCertificateKeyStoreType(value); + } + + public void setTrustCertificateKeyStoreUrl(String value) { + this.mc.setTrustCertificateKeyStoreUrl(value); + } + + public void setUltraDevHack(boolean flag) { + this.mc.setUltraDevHack(flag); + } + + public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) { + this.mc.setUseBlobToStoreUTF8OutsideBMP(flag); + } + + public void setUseCompression(boolean property) { + this.mc.setUseCompression(property); + } + + public void setUseConfigs(String configs) { + this.mc.setUseConfigs(configs); + } + + public void setUseCursorFetch(boolean flag) { + this.mc.setUseCursorFetch(flag); + } + + public void setUseDirectRowUnpack(boolean flag) { + this.mc.setUseDirectRowUnpack(flag); + } + + public void setUseDynamicCharsetInfo(boolean flag) { + this.mc.setUseDynamicCharsetInfo(flag); + } + + public void setUseFastDateParsing(boolean flag) { + this.mc.setUseFastDateParsing(flag); + } + + public void setUseFastIntParsing(boolean flag) { + this.mc.setUseFastIntParsing(flag); + } + + public void setUseGmtMillisForDatetimes(boolean flag) { + this.mc.setUseGmtMillisForDatetimes(flag); + } + + public void setUseHostsInPrivileges(boolean property) { + this.mc.setUseHostsInPrivileges(property); + } + + public void setUseInformationSchema(boolean flag) { + this.mc.setUseInformationSchema(flag); + } + + public void setUseJDBCCompliantTimezoneShift(boolean flag) { + this.mc.setUseJDBCCompliantTimezoneShift(flag); + } + + public void setUseJvmCharsetConverters(boolean flag) { + this.mc.setUseJvmCharsetConverters(flag); + } + + public void setUseLocalSessionState(boolean flag) { + this.mc.setUseLocalSessionState(flag); + } + + public void setUseNanosForElapsedTime(boolean flag) { + this.mc.setUseNanosForElapsedTime(flag); + } + + public void setUseOldAliasMetadataBehavior(boolean flag) { + this.mc.setUseOldAliasMetadataBehavior(flag); + } + + public void setUseOldUTF8Behavior(boolean flag) { + this.mc.setUseOldUTF8Behavior(flag); + } + + public void setUseOnlyServerErrorMessages(boolean flag) { + this.mc.setUseOnlyServerErrorMessages(flag); + } + + public void setUseReadAheadInput(boolean flag) { + this.mc.setUseReadAheadInput(flag); + } + + public void setUseSSL(boolean property) { + this.mc.setUseSSL(property); + } + + public void setUseSSPSCompatibleTimezoneShift(boolean flag) { + this.mc.setUseSSPSCompatibleTimezoneShift(flag); + } + + public void setUseServerPrepStmts(boolean flag) { + this.mc.setUseServerPrepStmts(flag); + } + + public void setUseServerPreparedStmts(boolean flag) { + this.mc.setUseServerPreparedStmts(flag); + } + + public void setUseSqlStateCodes(boolean flag) { + this.mc.setUseSqlStateCodes(flag); + } + + public void setUseStreamLengthsInPrepStmts(boolean property) { + this.mc.setUseStreamLengthsInPrepStmts(property); + } + + public void setUseTimezone(boolean property) { + this.mc.setUseTimezone(property); + } + + public void setUseUltraDevWorkAround(boolean property) { + this.mc.setUseUltraDevWorkAround(property); + } + + public void setUseUnbufferedInput(boolean flag) { + this.mc.setUseUnbufferedInput(flag); + } + + public void setUseUnicode(boolean flag) { + this.mc.setUseUnicode(flag); + } + + public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) { + this.mc.setUseUsageAdvisor(useUsageAdvisorFlag); + } + + public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) { + this.mc.setUtf8OutsideBmpExcludedColumnNamePattern(regexPattern); + } + + public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) { + this.mc.setUtf8OutsideBmpIncludedColumnNamePattern(regexPattern); + } + + public void setYearIsDateType(boolean flag) { + this.mc.setYearIsDateType(flag); + } + + public void setZeroDateTimeBehavior(String behavior) { + this.mc.setZeroDateTimeBehavior(behavior); + } + + public boolean useUnbufferedInput() { + return this.mc.useUnbufferedInput(); + } + + public void initializeExtension(Extension ex) throws SQLException { + this.mc.initializeExtension(ex); + } + + public String getProfilerEventHandler() { + return this.mc.getProfilerEventHandler(); + } + + public void setProfilerEventHandler(String handler) { + this.mc.setProfilerEventHandler(handler); + } + + public boolean getVerifyServerCertificate() { + return this.mc.getVerifyServerCertificate(); + } + + public void setVerifyServerCertificate(boolean flag) { + this.mc.setVerifyServerCertificate(flag); + } + + public boolean getUseLegacyDatetimeCode() { + return this.mc.getUseLegacyDatetimeCode(); + } + + public void setUseLegacyDatetimeCode(boolean flag) { + this.mc.setUseLegacyDatetimeCode(flag); + } + + public boolean getSendFractionalSeconds() { + return this.mc.getSendFractionalSeconds(); + } + + public void setSendFractionalSeconds(boolean flag) { + this.mc.setSendFractionalSeconds(flag); + } + + public int getSelfDestructOnPingMaxOperations() { + return this.mc.getSelfDestructOnPingMaxOperations(); + } + + public int getSelfDestructOnPingSecondsLifetime() { + return this.mc.getSelfDestructOnPingSecondsLifetime(); + } + + public void setSelfDestructOnPingMaxOperations(int maxOperations) throws SQLException { + this.mc.setSelfDestructOnPingMaxOperations(maxOperations); + } + + public void setSelfDestructOnPingSecondsLifetime(int seconds) throws SQLException { + this.mc.setSelfDestructOnPingSecondsLifetime(seconds); + } + + public boolean getUseColumnNamesInFindColumn() { + return this.mc.getUseColumnNamesInFindColumn(); + } + + public void setUseColumnNamesInFindColumn(boolean flag) { + this.mc.setUseColumnNamesInFindColumn(flag); + } + + public boolean getUseLocalTransactionState() { + return this.mc.getUseLocalTransactionState(); + } + + public void setUseLocalTransactionState(boolean flag) { + this.mc.setUseLocalTransactionState(flag); + } + + public boolean getCompensateOnDuplicateKeyUpdateCounts() { + return this.mc.getCompensateOnDuplicateKeyUpdateCounts(); + } + + public void setCompensateOnDuplicateKeyUpdateCounts(boolean flag) { + this.mc.setCompensateOnDuplicateKeyUpdateCounts(flag); + } + + public boolean getUseAffectedRows() { + return this.mc.getUseAffectedRows(); + } + + public void setUseAffectedRows(boolean flag) { + this.mc.setUseAffectedRows(flag); + } + + public String getPasswordCharacterEncoding() { + return this.mc.getPasswordCharacterEncoding(); + } + + public void setPasswordCharacterEncoding(String characterSet) { + this.mc.setPasswordCharacterEncoding(characterSet); + } + + public int getAutoIncrementIncrement() { + return this.mc.getAutoIncrementIncrement(); + } + + public int getLoadBalanceBlacklistTimeout() { + return this.mc.getLoadBalanceBlacklistTimeout(); + } + + public void setLoadBalanceBlacklistTimeout(int loadBalanceBlacklistTimeout) throws SQLException { + this.mc.setLoadBalanceBlacklistTimeout(loadBalanceBlacklistTimeout); + } + + public int getLoadBalancePingTimeout() { + return this.mc.getLoadBalancePingTimeout(); + } + + public void setLoadBalancePingTimeout(int loadBalancePingTimeout) throws SQLException { + this.mc.setLoadBalancePingTimeout(loadBalancePingTimeout); + } + + public boolean getLoadBalanceValidateConnectionOnSwapServer() { + return this.mc.getLoadBalanceValidateConnectionOnSwapServer(); + } + + public void setLoadBalanceValidateConnectionOnSwapServer(boolean loadBalanceValidateConnectionOnSwapServer) { + this.mc.setLoadBalanceValidateConnectionOnSwapServer(loadBalanceValidateConnectionOnSwapServer); + } + + public void setRetriesAllDown(int retriesAllDown) throws SQLException { + this.mc.setRetriesAllDown(retriesAllDown); + } + + public int getRetriesAllDown() { + return this.mc.getRetriesAllDown(); + } + + public ExceptionInterceptor getExceptionInterceptor() { + return this.pooledConnection.getExceptionInterceptor(); + } + + public String getExceptionInterceptors() { + return this.mc.getExceptionInterceptors(); + } + + public void setExceptionInterceptors(String exceptionInterceptors) { + this.mc.setExceptionInterceptors(exceptionInterceptors); + } + + public boolean getQueryTimeoutKillsConnection() { + return this.mc.getQueryTimeoutKillsConnection(); + } + + public void setQueryTimeoutKillsConnection(boolean queryTimeoutKillsConnection) { + this.mc.setQueryTimeoutKillsConnection(queryTimeoutKillsConnection); + } + + public boolean hasSameProperties(Connection c) { + return this.mc.hasSameProperties(c); + } + + public Properties getProperties() { + return this.mc.getProperties(); + } + + public String getHost() { + return this.mc.getHost(); + } + + public void setProxy(MySQLConnection conn) { + this.mc.setProxy(conn); + } + + public boolean getRetainStatementAfterResultSetClose() { + return this.mc.getRetainStatementAfterResultSetClose(); + } + + public int getMaxAllowedPacket() { + return this.mc.getMaxAllowedPacket(); + } + + public String getLoadBalanceConnectionGroup() { + return this.mc.getLoadBalanceConnectionGroup(); + } + + public boolean getLoadBalanceEnableJMX() { + return this.mc.getLoadBalanceEnableJMX(); + } + + public String getLoadBalanceExceptionChecker() { + return this.mc.getLoadBalanceExceptionChecker(); + } + + public String getLoadBalanceSQLExceptionSubclassFailover() { + return this.mc.getLoadBalanceSQLExceptionSubclassFailover(); + } + + public String getLoadBalanceSQLStateFailover() { + return this.mc.getLoadBalanceSQLStateFailover(); + } + + public void setLoadBalanceConnectionGroup(String loadBalanceConnectionGroup) { + this.mc.setLoadBalanceConnectionGroup(loadBalanceConnectionGroup); + + } + + public void setLoadBalanceEnableJMX(boolean loadBalanceEnableJMX) { + this.mc.setLoadBalanceEnableJMX(loadBalanceEnableJMX); + + } + + public void setLoadBalanceExceptionChecker(String loadBalanceExceptionChecker) { + this.mc.setLoadBalanceExceptionChecker(loadBalanceExceptionChecker); + + } + + public void setLoadBalanceSQLExceptionSubclassFailover(String loadBalanceSQLExceptionSubclassFailover) { + this.mc.setLoadBalanceSQLExceptionSubclassFailover(loadBalanceSQLExceptionSubclassFailover); + + } + + public void setLoadBalanceSQLStateFailover(String loadBalanceSQLStateFailover) { + this.mc.setLoadBalanceSQLStateFailover(loadBalanceSQLStateFailover); + + } + + public String getLoadBalanceAutoCommitStatementRegex() { + return this.mc.getLoadBalanceAutoCommitStatementRegex(); + } + + public int getLoadBalanceAutoCommitStatementThreshold() { + return this.mc.getLoadBalanceAutoCommitStatementThreshold(); + } + + public void setLoadBalanceAutoCommitStatementRegex(String loadBalanceAutoCommitStatementRegex) { + this.mc.setLoadBalanceAutoCommitStatementRegex(loadBalanceAutoCommitStatementRegex); + + } + + public void setLoadBalanceAutoCommitStatementThreshold(int loadBalanceAutoCommitStatementThreshold) throws SQLException { + this.mc.setLoadBalanceAutoCommitStatementThreshold(loadBalanceAutoCommitStatementThreshold); + + } + + public void setLoadBalanceHostRemovalGracePeriod(int loadBalanceHostRemovalGracePeriod) throws SQLException { + this.mc.setLoadBalanceHostRemovalGracePeriod(loadBalanceHostRemovalGracePeriod); + } + + public int getLoadBalanceHostRemovalGracePeriod() { + return this.mc.getLoadBalanceHostRemovalGracePeriod(); + } + + public void setTypeMap(Map> map) throws SQLException { + checkClosed(); + + try { + this.mc.setTypeMap(map); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean getIncludeThreadDumpInDeadlockExceptions() { + return this.mc.getIncludeThreadDumpInDeadlockExceptions(); + } + + public void setIncludeThreadDumpInDeadlockExceptions(boolean flag) { + this.mc.setIncludeThreadDumpInDeadlockExceptions(flag); + + } + + public boolean getIncludeThreadNamesAsStatementComment() { + return this.mc.getIncludeThreadNamesAsStatementComment(); + } + + public void setIncludeThreadNamesAsStatementComment(boolean flag) { + this.mc.setIncludeThreadNamesAsStatementComment(flag); + } + + public boolean isServerLocal() throws SQLException { + return this.mc.isServerLocal(); + } + + public void setAuthenticationPlugins(String authenticationPlugins) { + this.mc.setAuthenticationPlugins(authenticationPlugins); + } + + public String getAuthenticationPlugins() { + return this.mc.getAuthenticationPlugins(); + } + + public void setDisabledAuthenticationPlugins(String disabledAuthenticationPlugins) { + this.mc.setDisabledAuthenticationPlugins(disabledAuthenticationPlugins); + } + + public String getDisabledAuthenticationPlugins() { + return this.mc.getDisabledAuthenticationPlugins(); + } + + public void setDefaultAuthenticationPlugin(String defaultAuthenticationPlugin) { + this.mc.setDefaultAuthenticationPlugin(defaultAuthenticationPlugin); + + } + + public String getDefaultAuthenticationPlugin() { + return this.mc.getDefaultAuthenticationPlugin(); + } + + public void setParseInfoCacheFactory(String factoryClassname) { + this.mc.setParseInfoCacheFactory(factoryClassname); + } + + public String getParseInfoCacheFactory() { + return this.mc.getParseInfoCacheFactory(); + } + + public void setSchema(String schema) throws SQLException { + this.mc.setSchema(schema); + } + + public String getSchema() throws SQLException { + return this.mc.getSchema(); + } + + public void abort(Executor executor) throws SQLException { + this.mc.abort(executor); + } + + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + this.mc.setNetworkTimeout(executor, milliseconds); + } + + public int getNetworkTimeout() throws SQLException { + return this.mc.getNetworkTimeout(); + } + + public void setServerConfigCacheFactory(String factoryClassname) { + this.mc.setServerConfigCacheFactory(factoryClassname); + } + + public String getServerConfigCacheFactory() { + return this.mc.getServerConfigCacheFactory(); + } + + public void setDisconnectOnExpiredPasswords(boolean disconnectOnExpiredPasswords) { + this.mc.setDisconnectOnExpiredPasswords(disconnectOnExpiredPasswords); + } + + public boolean getDisconnectOnExpiredPasswords() { + return this.mc.getDisconnectOnExpiredPasswords(); + } + + public void setGetProceduresReturnsFunctions(boolean getProcedureReturnsFunctions) { + this.mc.setGetProceduresReturnsFunctions(getProcedureReturnsFunctions); + } + + public boolean getGetProceduresReturnsFunctions() { + return this.mc.getGetProceduresReturnsFunctions(); + } + + public void abortInternal() throws SQLException { + this.mc.abortInternal(); + } + + public Object getConnectionMutex() { + return this.mc.getConnectionMutex(); + } + + public boolean getAllowMasterDownConnections() { + return this.mc.getAllowMasterDownConnections(); + } + + public void setAllowMasterDownConnections(boolean connectIfMasterDown) { + this.mc.setAllowMasterDownConnections(connectIfMasterDown); + } + + public boolean getAllowSlaveDownConnections() { + return this.mc.getAllowSlaveDownConnections(); + } + + public void setAllowSlaveDownConnections(boolean connectIfSlaveDown) { + this.mc.setAllowSlaveDownConnections(connectIfSlaveDown); + } + + public boolean getReadFromMasterWhenNoSlaves() { + return this.mc.getReadFromMasterWhenNoSlaves(); + } + + public void setReadFromMasterWhenNoSlaves(boolean useMasterIfSlavesDown) { + this.mc.setReadFromMasterWhenNoSlaves(useMasterIfSlavesDown); + } + + public boolean getReplicationEnableJMX() { + return this.mc.getReplicationEnableJMX(); + } + + public void setReplicationEnableJMX(boolean replicationEnableJMX) { + this.mc.setReplicationEnableJMX(replicationEnableJMX); + + } + + public String getConnectionAttributes() throws SQLException { + return this.mc.getConnectionAttributes(); + } + + public void setDetectCustomCollations(boolean detectCustomCollations) { + this.mc.setDetectCustomCollations(detectCustomCollations); + } + + public boolean getDetectCustomCollations() { + return this.mc.getDetectCustomCollations(); + } + + public int getSessionMaxRows() { + return this.mc.getSessionMaxRows(); + } + + public void setSessionMaxRows(int max) throws SQLException { + this.mc.setSessionMaxRows(max); + } + + public String getServerRSAPublicKeyFile() { + return this.mc.getServerRSAPublicKeyFile(); + } + + public void setServerRSAPublicKeyFile(String serverRSAPublicKeyFile) throws SQLException { + this.mc.setServerRSAPublicKeyFile(serverRSAPublicKeyFile); + } + + public boolean getAllowPublicKeyRetrieval() { + return this.mc.getAllowPublicKeyRetrieval(); + } + + public void setAllowPublicKeyRetrieval(boolean allowPublicKeyRetrieval) throws SQLException { + this.mc.setAllowPublicKeyRetrieval(allowPublicKeyRetrieval); + } + + public void setDontCheckOnDuplicateKeyUpdateInSQL(boolean dontCheckOnDuplicateKeyUpdateInSQL) { + this.mc.setDontCheckOnDuplicateKeyUpdateInSQL(dontCheckOnDuplicateKeyUpdateInSQL); + } + + public boolean getDontCheckOnDuplicateKeyUpdateInSQL() { + return this.mc.getDontCheckOnDuplicateKeyUpdateInSQL(); + } + + public void setSocksProxyHost(String socksProxyHost) { + this.mc.setSocksProxyHost(socksProxyHost); + } + + public String getSocksProxyHost() { + return this.mc.getSocksProxyHost(); + } + + public void setSocksProxyPort(int socksProxyPort) throws SQLException { + this.mc.setSocksProxyPort(socksProxyPort); + } + + public int getSocksProxyPort() { + return this.mc.getSocksProxyPort(); + } + + public boolean getReadOnlyPropagatesToServer() { + return this.mc.getReadOnlyPropagatesToServer(); + } + + public void setReadOnlyPropagatesToServer(boolean flag) { + this.mc.setReadOnlyPropagatesToServer(flag); + } + + public String getEnabledSSLCipherSuites() { + return this.mc.getEnabledSSLCipherSuites(); + } + + public void setEnabledSSLCipherSuites(String cipherSuites) { + this.mc.setEnabledSSLCipherSuites(cipherSuites); + } + + public boolean getEnableEscapeProcessing() { + return this.mc.getEnableEscapeProcessing(); + } + + public void setEnableEscapeProcessing(boolean flag) { + this.mc.setEnableEscapeProcessing(flag); + } + + public boolean isUseSSLExplicit() { + return this.mc.isUseSSLExplicit(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42CallableStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42CallableStatementWrapper.java new file mode 100644 index 0000000..cce6c1a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42CallableStatementWrapper.java @@ -0,0 +1,238 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.SQLType; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection; + +public class JDBC42CallableStatementWrapper extends JDBC4CallableStatementWrapper { + public JDBC42CallableStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) { + super(c, conn, toWrap); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42PreparedStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42PreparedStatementWrapper.java new file mode 100644 index 0000000..3aecbd8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC42PreparedStatementWrapper.java @@ -0,0 +1,79 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLType; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection; + +public class JDBC42PreparedStatementWrapper extends JDBC4PreparedStatementWrapper { + public JDBC42PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) { + super(c, conn, toWrap); + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java new file mode 100644 index 0000000..d09c3aa --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java @@ -0,0 +1,835 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.RowId; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Statement; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.SQLError; + +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection; + +public class JDBC4CallableStatementWrapper extends CallableStatementWrapper { + + public JDBC4CallableStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) { + super(c, conn, toWrap); + } + + public void close() throws SQLException { + try { + super.close(); + } finally { + this.unwrappedInterfaces = null; + } + } + + public boolean isClosed() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isClosed(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // never get here - compiler can't tell + } + + public void setPoolable(boolean poolable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setPoolable(poolable); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean isPoolable() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isPoolable(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // never get here - compiler can't tell + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, NClob value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setSQLXML(parameterIndex, xmlObject); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNString(int parameterIndex, String value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNString(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.PreparedStatement") || interfaceClassName.equals("java.sql.Wrapper")); + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.PreparedStatement".equals(iface.getName()) + || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (unwrappedInterfaces == null) { + unwrappedInterfaces = new HashMap, Object>(); + } + + Object cachedUnwrapped = unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + public void setRowId(String parameterName, RowId x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setRowId(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setSQLXML(parameterName, xmlObject); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + + } + + public SQLXML getSQLXML(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public RowId getRowId(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRowId(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setNClob(String parameterName, NClob value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNString(String parameterName, String value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNString(parameterName, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(int) + */ + public Reader getCharacterStream(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(java.lang.String) + */ + public Reader getCharacterStream(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /** + * @see java.sql.CallableStatement#getNCharacterStream(int) + */ + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /** + * @see java.sql.CallableStatement#getNCharacterStream(java.lang.String) + */ + public Reader getNCharacterStream(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /** + * @see java.sql.CallableStatement#getNClob(java.lang.String) + */ + public NClob getNClob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNClob(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + /** + * @see java.sql.CallableStatement#getNString(java.lang.String) + */ + public String getNString(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNString(parameterName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(String parameterName, Blob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Clob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public NClob getNClob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNClob(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public String getNString(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNString(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public RowId getRowId(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRowId(parameterIndex); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java new file mode 100644 index 0000000..818813c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java @@ -0,0 +1,320 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.SQLError; + +public class JDBC4ConnectionWrapper extends ConnectionWrapper { + + /** + * Construct a new LogicalHandle and set instance variables + * + * @param mysqlPooledConnection + * reference to object that instantiated this object + * @param mysqlConnection + * physical connection to db + * + * @throws SQLException + * if an error occurs. + */ + public JDBC4ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection, com.mysql.jdbc.Connection mysqlConnection, boolean forXa) throws SQLException { + super(mysqlPooledConnection, mysqlConnection, forXa); + } + + public void close() throws SQLException { + try { + super.close(); + } finally { + this.unwrappedInterfaces = null; + } + } + + public SQLXML createSQLXML() throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createSQLXML(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createArrayOf(typeName, elements); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createStruct(typeName, attributes); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public Properties getClientInfo() throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).getClientInfo(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public String getClientInfo(String name) throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).getClientInfo(name); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + /** + * Returns true if the connection has not been closed and is still valid. + * The driver shall submit a query on the connection or use some other + * mechanism that positively verifies the connection is still valid when + * this method is called. + *

    + * The query submitted by the driver to validate the connection shall be executed in the context of the current transaction. + * + * @param timeout + * - + * The time in seconds to wait for the database operation used to + * validate the connection to complete. If the timeout period + * expires before the operation completes, this method returns + * false. A value of 0 indicates a timeout is not applied to the + * database operation. + *

    + * @return true if the connection is valid, false otherwise + * @exception SQLException + * if the value supplied for timeout is less + * then 0 + * @since 1.6 + */ + public synchronized boolean isValid(int timeout) throws SQLException { + try { + return ((java.sql.Connection) this.mc).isValid(timeout); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // never reached, but compiler can't tell + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + checkClosed(); + + ((java.sql.Connection) this.mc).setClientInfo(properties); + } catch (SQLException sqlException) { + try { + checkAndFireConnectionError(sqlException); + } catch (SQLException sqlEx2) { + SQLClientInfoException clientEx = new SQLClientInfoException(); + clientEx.initCause(sqlEx2); + + throw clientEx; + } + } + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + checkClosed(); + + ((java.sql.Connection) this.mc).setClientInfo(name, value); + } catch (SQLException sqlException) { + try { + checkAndFireConnectionError(sqlException); + } catch (SQLException sqlEx2) { + SQLClientInfoException clientEx = new SQLClientInfoException(); + clientEx.initCause(sqlEx2); + + throw clientEx; + } + } + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + return (iface.getName().equals("com.mysql.jdbc.Connection") || iface.getName().equals("com.mysql.jdbc.ConnectionProperties")); + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Connection".equals(iface.getName()) || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (unwrappedInterfaces == null) { + unwrappedInterfaces = new HashMap, Object>(); + } + + Object cachedUnwrapped = unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.mc.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.mc)); + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + /** + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createBlob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + /** + * @see java.sql.Connection#createClob() + */ + public Clob createClob() throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createClob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + /** + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() throws SQLException { + checkClosed(); + + try { + return ((java.sql.Connection) this.mc).createNClob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlPooledConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlPooledConnection.java new file mode 100644 index 0000000..23ac6b1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlPooledConnection.java @@ -0,0 +1,94 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.StatementEvent; +import javax.sql.StatementEventListener; + +/** + * This class is used to wrap and return a physical connection within a logical handle. It also registers and notifies ConnectionEventListeners of any + * ConnectionEvents + */ +public class JDBC4MysqlPooledConnection extends MysqlPooledConnection { + + private final Map statementEventListeners = new HashMap(); + + public JDBC4MysqlPooledConnection(com.mysql.jdbc.Connection connection) { + super(connection); + } + + public synchronized void close() throws SQLException { + super.close(); + + this.statementEventListeners.clear(); + } + + /** + * Registers a StatementEventListener with this PooledConnection object. Components that + * wish to be notified when PreparedStatements created by the + * connection are closed or are detected to be invalid may use this method + * to register a StatementEventListener with this PooledConnection object. + * + * @param listener + * an component which implements the StatementEventListener interface that is to be registered with this + * PooledConnection object + * + * @since 1.6 + */ + public void addStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.put(listener, listener); + } + } + + /** + * Removes the specified StatementEventListener from the list of + * components that will be notified when the driver detects that a PreparedStatement has been closed or is invalid. + * + * @param listener + * the component which implements the StatementEventListener interface that was previously + * registered with this PooledConnection object + * + * @since 1.6 + */ + public void removeStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.remove(listener); + } + } + + void fireStatementEvent(StatementEvent event) throws SQLException { + synchronized (this.statementEventListeners) { + for (StatementEventListener listener : this.statementEventListeners.keySet()) { + listener.statementClosed(event); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlXAConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlXAConnection.java new file mode 100644 index 0000000..98ea61a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4MysqlXAConnection.java @@ -0,0 +1,90 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.StatementEvent; +import javax.sql.StatementEventListener; + +import com.mysql.jdbc.Connection; + +public class JDBC4MysqlXAConnection extends MysqlXAConnection { + + private final Map statementEventListeners = new HashMap(); + + public JDBC4MysqlXAConnection(Connection connection, boolean logXaCommands) throws SQLException { + super(connection, logXaCommands); + } + + public synchronized void close() throws SQLException { + super.close(); + + this.statementEventListeners.clear(); + } + + /** + * Registers a StatementEventListener with this PooledConnection object. Components that + * wish to be notified when PreparedStatements created by the + * connection are closed or are detected to be invalid may use this method + * to register a StatementEventListener with this PooledConnection object. + * + * @param listener + * an component which implements the StatementEventListener interface that is to be registered with this + * PooledConnection object + * @since 1.6 + */ + public void addStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.put(listener, listener); + } + } + + /** + * Removes the specified StatementEventListener from the list of + * components that will be notified when the driver detects that a PreparedStatement has been closed or is invalid. + * + * @param listener + * the component which implements the StatementEventListener interface that was previously + * registered with this PooledConnection object + * @since 1.6 + */ + public void removeStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.remove(listener); + } + } + + void fireStatementEvent(StatementEvent event) throws SQLException { + synchronized (this.statementEventListeners) { + for (StatementEventListener listener : this.statementEventListeners.keySet()) { + listener.statementClosed(event); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java new file mode 100644 index 0000000..e3940e0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java @@ -0,0 +1,427 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.RowId; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Statement; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.sql.StatementEvent; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection; + +public class JDBC4PreparedStatementWrapper extends PreparedStatementWrapper { + + public JDBC4PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) { + super(c, conn, toWrap); + } + + public synchronized void close() throws SQLException { + if (this.pooledConnection == null) { + // no-op + return; + } + + MysqlPooledConnection con = this.pooledConnection; // we need this later... + + try { + super.close(); + } finally { + try { + StatementEvent e = new StatementEvent(con, this); + // todo: pull this all up into base classes when we support *only* JDK6 or newer + if (con instanceof JDBC4MysqlPooledConnection) { + ((JDBC4MysqlPooledConnection) con).fireStatementEvent(e); + } else if (con instanceof JDBC4MysqlXAConnection) { + ((JDBC4MysqlXAConnection) con).fireStatementEvent(e); + } else if (con instanceof JDBC4SuspendableXAConnection) { + ((JDBC4SuspendableXAConnection) con).fireStatementEvent(e); + } + } finally { + this.unwrappedInterfaces = null; + } + } + } + + public boolean isClosed() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isClosed(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // never get here - compiler can't tell + } + + public void setPoolable(boolean poolable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setPoolable(poolable); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean isPoolable() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isPoolable(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // never get here - compiler can't tell + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, NClob value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setSQLXML(parameterIndex, xmlObject); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNString(int parameterIndex, String value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNString(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.PreparedStatement") || interfaceClassName.equals("java.sql.Wrapper")); + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.PreparedStatement".equals(iface.getName()) + || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (unwrappedInterfaces == null) { + unwrappedInterfaces = new HashMap, Object>(); + } + + Object cachedUnwrapped = unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java new file mode 100644 index 0000000..58c757d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java @@ -0,0 +1,182 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection; + +public class JDBC4StatementWrapper extends StatementWrapper { + + public JDBC4StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, Statement toWrap) { + super(c, conn, toWrap); + } + + public void close() throws SQLException { + try { + super.close(); + } finally { + this.unwrappedInterfaces = null; + } + } + + public boolean isClosed() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isClosed(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // We never get here, compiler can't tell + } + + public void setPoolable(boolean poolable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setPoolable(poolable); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean isPoolable() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isPoolable(); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // We never get here, compiler can't tell + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.Wrapper")); + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (unwrappedInterfaces == null) { + unwrappedInterfaces = new HashMap, Object>(); + } + + Object cachedUnwrapped = unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.java new file mode 100644 index 0000000..3719f0d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.java @@ -0,0 +1,90 @@ +/* + Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.SQLException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.StatementEvent; +import javax.sql.StatementEventListener; + +import com.mysql.jdbc.Connection; + +public class JDBC4SuspendableXAConnection extends SuspendableXAConnection { + + private final Map statementEventListeners = new HashMap(); + + public JDBC4SuspendableXAConnection(Connection connection) throws SQLException { + super(connection); + } + + public synchronized void close() throws SQLException { + super.close(); + + this.statementEventListeners.clear(); + } + + /** + * Registers a StatementEventListener with this PooledConnection object. Components that + * wish to be notified when PreparedStatements created by the + * connection are closed or are detected to be invalid may use this method + * to register a StatementEventListener with this PooledConnection object. + * + * @param listener + * an component which implements the StatementEventListener interface that is to be registered with this + * PooledConnection object + * @since 1.6 + */ + public void addStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.put(listener, listener); + } + } + + /** + * Removes the specified StatementEventListener from the list of + * components that will be notified when the driver detects that a PreparedStatement has been closed or is invalid. + * + * @param listener + * the component which implements the StatementEventListener interface that was previously + * registered with this PooledConnection object + * @since 1.6 + */ + public void removeStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.remove(listener); + } + } + + void fireStatementEvent(StatementEvent event) throws SQLException { + synchronized (this.statementEventListeners) { + for (StatementEventListener listener : this.statementEventListeners.keySet()) { + listener.statementClosed(event); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java new file mode 100644 index 0000000..22e59a5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +/** + * This class is used to obtain a physical connection and instantiate and return a MysqlPooledConnection. J2EE application servers map client calls to + * dataSource.getConnection to this class based upon mapping set within deployment descriptor. This class extends MysqlDataSource. + */ +public class MysqlConnectionPoolDataSource extends MysqlDataSource implements ConnectionPoolDataSource { + + static final long serialVersionUID = -7767325445592304961L; + + /** + * Returns a pooled connection. + * + * @exception SQLException + * if an error occurs + * @return a PooledConnection + */ + public synchronized PooledConnection getPooledConnection() throws SQLException { + Connection connection = getConnection(); + MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((com.mysql.jdbc.Connection) connection); + + return mysqlPooledConnection; + } + + /** + * This method is invoked by the container. Obtains physical connection + * using mySql.Driver class and returns a mysqlPooledConnection object. + * + * @param s + * user name + * @param s1 + * password + * @exception SQLException + * if an error occurs + * @return a PooledConnection + */ + public synchronized PooledConnection getPooledConnection(String s, String s1) throws SQLException { + Connection connection = getConnection(s, s1); + MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((com.mysql.jdbc.Connection) connection); + + return mysqlPooledConnection; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java new file mode 100644 index 0000000..7496024 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java @@ -0,0 +1,438 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.Properties; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import javax.sql.DataSource; + +import com.mysql.jdbc.ConnectionPropertiesImpl; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.SQLError; + +/** + * A JNDI DataSource for a Mysql JDBC connection + */ +public class MysqlDataSource extends ConnectionPropertiesImpl implements DataSource, Referenceable, Serializable { + + static final long serialVersionUID = -5515846944416881264L; + + /** The driver to create connections with */ + protected final static NonRegisteringDriver mysqlDriver; + + static { + try { + mysqlDriver = new NonRegisteringDriver(); + } catch (Exception E) { + throw new RuntimeException("Can not load Driver class com.mysql.jdbc.Driver"); + } + } + + /** Log stream */ + protected transient PrintWriter logWriter = null; + + /** Database Name */ + protected String databaseName = null; + + /** Character Encoding */ + protected String encoding = null; + + /** Hostname */ + protected String hostName = null; + + /** Password */ + protected String password = null; + + /** The profileSql property */ + protected String profileSql = "false"; + + /** The JDBC URL */ + protected String url = null; + + /** User name */ + protected String user = null; + + /** Should we construct the URL, or has it been set explicitly */ + protected boolean explicitUrl = false; + + /** Port number */ + protected int port = 3306; + + /** + * Default no-arg constructor for Serialization + */ + public MysqlDataSource() { + } + + /** + * Creates a new connection using the already configured username and + * password. + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + return getConnection(this.user, this.password); + } + + /** + * Creates a new connection with the given username and password + * + * @param userID + * the user id to connect with + * @param password + * the password to connect with + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection(String userID, String pass) throws SQLException { + Properties props = new Properties(); + + if (userID != null) { + props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, userID); + } + + if (pass != null) { + props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, pass); + } + + exposeAsProperties(props); + + return getConnection(props); + } + + /** + * Sets the database name. + * + * @param dbName + * the name of the database + */ + public void setDatabaseName(String dbName) { + this.databaseName = dbName; + } + + /** + * Gets the name of the database + * + * @return the name of the database for this data source + */ + public String getDatabaseName() { + return (this.databaseName != null) ? this.databaseName : ""; + } + + /** + * Sets the log writer for this data source. + * + * @see javax.sql.DataSource#setLogWriter(PrintWriter) + */ + public void setLogWriter(PrintWriter output) throws SQLException { + this.logWriter = output; + } + + /** + * Returns the log writer for this data source + * + * @return the log writer for this data source + */ + public java.io.PrintWriter getLogWriter() { + return this.logWriter; + } + + /** + * @param seconds + * + * @throws SQLException + */ + public void setLoginTimeout(int seconds) throws SQLException { + } + + /** + * Returns the login timeout + * + * @return the login timeout + */ + public int getLoginTimeout() { + return 0; + } + + /** + * Sets the password + * + * @param pass + * the password + */ + public void setPassword(String pass) { + this.password = pass; + } + + /** + * Sets the database port. + * + * @param p + * the port + */ + public void setPort(int p) { + this.port = p; + } + + /** + * Returns the port number + * + * @return the port number + */ + public int getPort() { + return this.port; + } + + /** + * Sets the port number + * + * @param p + * the port + * + * @see #setPort + */ + public void setPortNumber(int p) { + setPort(p); + } + + /** + * Returns the port number + * + * @return the port number + */ + public int getPortNumber() { + return getPort(); + } + + /** + * @param ref + * + * @throws SQLException + */ + public void setPropertiesViaRef(Reference ref) throws SQLException { + super.initializeFromRef(ref); + } + + /** + * Required method to support this class as a Referenceable. + * + * @return a Reference to this data source + * + * @throws NamingException + * if a JNDI error occurs + */ + public Reference getReference() throws NamingException { + String factoryName = "com.mysql.jdbc.jdbc2.optional.MysqlDataSourceFactory"; + Reference ref = new Reference(getClass().getName(), factoryName, null); + ref.add(new StringRefAddr(NonRegisteringDriver.USER_PROPERTY_KEY, getUser())); + ref.add(new StringRefAddr(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, this.password)); + ref.add(new StringRefAddr("serverName", getServerName())); + ref.add(new StringRefAddr("port", "" + getPort())); + ref.add(new StringRefAddr("databaseName", getDatabaseName())); + ref.add(new StringRefAddr("url", getUrl())); + ref.add(new StringRefAddr("explicitUrl", String.valueOf(this.explicitUrl))); + + // + // Now store all of the 'non-standard' properties... + // + try { + storeToRef(ref); + } catch (SQLException sqlEx) { + throw new NamingException(sqlEx.getMessage()); + } + + return ref; + } + + /** + * Sets the server name. + * + * @param serverName + * the server name + */ + public void setServerName(String serverName) { + this.hostName = serverName; + } + + /** + * Returns the name of the database server + * + * @return the name of the database server + */ + public String getServerName() { + return (this.hostName != null) ? this.hostName : ""; + } + + // + // I've seen application servers use both formats, URL or url (doh) + // + + /** + * Sets the URL for this connection + * + * @param url + * the URL for this connection + */ + public void setURL(String url) { + setUrl(url); + } + + /** + * Returns the URL for this connection + * + * @return the URL for this connection + */ + public String getURL() { + return getUrl(); + } + + /** + * This method is used by the app server to set the url string specified + * within the datasource deployment descriptor. It is discovered using + * introspection and matches if property name in descriptor is "url". + * + * @param url + * url to be used within driver.connect + */ + public void setUrl(String url) { + this.url = url; + this.explicitUrl = true; + } + + /** + * Returns the JDBC URL that will be used to create the database connection. + * + * @return the URL for this connection + */ + public String getUrl() { + if (!this.explicitUrl) { + String builtUrl = "jdbc:mysql://"; + builtUrl = builtUrl + getServerName() + ":" + getPort() + "/" + getDatabaseName(); + + return builtUrl; + } + + return this.url; + } + + /** + * Sets the user ID. + * + * @param userID + * the User ID + */ + public void setUser(String userID) { + this.user = userID; + } + + /** + * Returns the configured user for this connection + * + * @return the user for this connection + */ + public String getUser() { + return this.user; + } + + /** + * Creates a connection using the specified properties. + * + * @param props + * the properties to connect with + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + protected java.sql.Connection getConnection(Properties props) throws SQLException { + String jdbcUrlToUse = null; + + if (!this.explicitUrl) { + StringBuilder jdbcUrl = new StringBuilder("jdbc:mysql://"); + + if (this.hostName != null) { + jdbcUrl.append(this.hostName); + } + + jdbcUrl.append(":"); + jdbcUrl.append(this.port); + jdbcUrl.append("/"); + + if (this.databaseName != null) { + jdbcUrl.append(this.databaseName); + } + + jdbcUrlToUse = jdbcUrl.toString(); + } else { + jdbcUrlToUse = this.url; + } + + // + // URL should take precedence over properties + // + + Properties urlProps = mysqlDriver.parseURL(jdbcUrlToUse, null); + if (urlProps == null) { + throw SQLError.createSQLException(Messages.getString("MysqlDataSource.BadUrl", new Object[] { jdbcUrlToUse }), + SQLError.SQL_STATE_CONNECTION_FAILURE, null); + } + urlProps.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + urlProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + urlProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + + Iterator keys = urlProps.keySet().iterator(); + + while (keys.hasNext()) { + String key = (String) keys.next(); + + props.setProperty(key, urlProps.getProperty(key)); + } + + return mysqlDriver.connect(jdbcUrlToUse, props); + } + // + // public boolean isWrapperFor(Class iface) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + // + // public T unwrap(Class iface) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java new file mode 100644 index 0000000..761b72f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java @@ -0,0 +1,135 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; + +import com.mysql.jdbc.NonRegisteringDriver; + +/** + * Factory class for MysqlDataSource objects + */ +public class MysqlDataSourceFactory implements ObjectFactory { + /** + * The class name for a standard MySQL DataSource. + */ + protected final static String DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource"; + + /** + * The class name for a poolable MySQL DataSource. + */ + protected final static String POOL_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"; + + /** + * The class name for a MysqlXADataSource + */ + + protected final static String XA_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"; + + /** + * @param refObj + * @param nm + * @param ctx + * @param env + * @throws Exception + */ + public Object getObjectInstance(Object refObj, Name nm, Context ctx, Hashtable env) throws Exception { + Reference ref = (Reference) refObj; + String className = ref.getClassName(); + + if ((className != null) + && (className.equals(DATA_SOURCE_CLASS_NAME) || className.equals(POOL_DATA_SOURCE_CLASS_NAME) || className.equals(XA_DATA_SOURCE_CLASS_NAME))) { + MysqlDataSource dataSource = null; + + try { + dataSource = (MysqlDataSource) Class.forName(className).newInstance(); + } catch (Exception ex) { + throw new RuntimeException("Unable to create DataSource of class '" + className + "', reason: " + ex.toString()); + } + + int portNumber = 3306; + + String portNumberAsString = nullSafeRefAddrStringGet("port", ref); + + if (portNumberAsString != null) { + portNumber = Integer.parseInt(portNumberAsString); + } + + dataSource.setPort(portNumber); + + String user = nullSafeRefAddrStringGet(NonRegisteringDriver.USER_PROPERTY_KEY, ref); + + if (user != null) { + dataSource.setUser(user); + } + + String password = nullSafeRefAddrStringGet(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, ref); + + if (password != null) { + dataSource.setPassword(password); + } + + String serverName = nullSafeRefAddrStringGet("serverName", ref); + + if (serverName != null) { + dataSource.setServerName(serverName); + } + + String databaseName = nullSafeRefAddrStringGet("databaseName", ref); + + if (databaseName != null) { + dataSource.setDatabaseName(databaseName); + } + + String explicitUrlAsString = nullSafeRefAddrStringGet("explicitUrl", ref); + + if (explicitUrlAsString != null) { + if (Boolean.valueOf(explicitUrlAsString).booleanValue()) { + dataSource.setUrl(nullSafeRefAddrStringGet("url", ref)); + } + } + + dataSource.setPropertiesViaRef(ref); + + return dataSource; + } + + // We can't create an instance of the reference + return null; + } + + private String nullSafeRefAddrStringGet(String referenceName, Reference ref) { + RefAddr refAddr = ref.get(referenceName); + + String asString = refAddr != null ? (String) refAddr.getContent() : null; + + return asString; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java new file mode 100644 index 0000000..3464699 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java @@ -0,0 +1,233 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.Constructor; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.ConnectionEvent; +import javax.sql.ConnectionEventListener; +import javax.sql.PooledConnection; + +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Util; + +/** + * This class is used to wrap and return a physical connection within a logical handle. It also registers and notifies ConnectionEventListeners of any + * ConnectionEvents + */ +public class MysqlPooledConnection implements PooledConnection { + + private static final Constructor JDBC_4_POOLED_CONNECTION_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_POOLED_CONNECTION_WRAPPER_CTOR = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4MysqlPooledConnection") + .getConstructor(new Class[] { com.mysql.jdbc.Connection.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_POOLED_CONNECTION_WRAPPER_CTOR = null; + } + } + + protected static MysqlPooledConnection getInstance(com.mysql.jdbc.Connection connection) throws SQLException { + if (!Util.isJdbc4()) { + return new MysqlPooledConnection(connection); + } + + return (MysqlPooledConnection) Util.handleNewInstance(JDBC_4_POOLED_CONNECTION_WRAPPER_CTOR, new Object[] { connection }, + connection.getExceptionInterceptor()); + } + + /** + * The flag for an exception being thrown. + */ + public static final int CONNECTION_ERROR_EVENT = 1; + + /** + * The flag for a connection being closed. + */ + public static final int CONNECTION_CLOSED_EVENT = 2; + + private Map connectionEventListeners; + + private Connection logicalHandle; + + private com.mysql.jdbc.Connection physicalConn; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Construct a new MysqlPooledConnection and set instance variables + * + * @param connection + * physical connection to db + */ + public MysqlPooledConnection(com.mysql.jdbc.Connection connection) { + this.logicalHandle = null; + this.physicalConn = connection; + this.connectionEventListeners = new HashMap(); + this.exceptionInterceptor = this.physicalConn.getExceptionInterceptor(); + } + + /** + * Adds ConnectionEventListeners to a hash table to be used for notification + * of ConnectionEvents + * + * @param connectioneventlistener + * listener to be notified with ConnectionEvents + */ + public synchronized void addConnectionEventListener(ConnectionEventListener connectioneventlistener) { + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.put(connectioneventlistener, connectioneventlistener); + } + } + + /** + * Removes ConnectionEventListeners from hash table used for notification of + * ConnectionEvents + * + * @param connectioneventlistener + * listener to be removed + */ + public synchronized void removeConnectionEventListener(ConnectionEventListener connectioneventlistener) { + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.remove(connectioneventlistener); + } + } + + /** + * Invoked by the container. Return a logicalHandle object that wraps a + * physical connection. + * + * @see java.sql.DataSource#getConnection() + */ + public synchronized Connection getConnection() throws SQLException { + return getConnection(true, false); + + } + + protected synchronized Connection getConnection(boolean resetServerState, boolean forXa) throws SQLException { + if (this.physicalConn == null) { + + SQLException sqlException = SQLError.createSQLException("Physical Connection doesn't exist", this.exceptionInterceptor); + callConnectionEventListeners(CONNECTION_ERROR_EVENT, sqlException); + + throw sqlException; + } + + try { + + if (this.logicalHandle != null) { + ((ConnectionWrapper) this.logicalHandle).close(false); + } + + if (resetServerState) { + this.physicalConn.resetServerState(); + } + + this.logicalHandle = ConnectionWrapper.getInstance(this, this.physicalConn, forXa); + } catch (SQLException sqlException) { + callConnectionEventListeners(CONNECTION_ERROR_EVENT, sqlException); + + throw sqlException; + } + + return this.logicalHandle; + } + + /** + * Invoked by the container (not the client), and should close the physical + * connection. This will be called if the pool is destroyed or the + * connectionEventListener receives a connectionErrorOccurred event. + * + * @see java.sql.DataSource#close() + */ + public synchronized void close() throws SQLException { + if (this.physicalConn != null) { + this.physicalConn.close(); + + this.physicalConn = null; + } + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.clear(); + + this.connectionEventListeners = null; + } + } + + /** + * Notifies all registered ConnectionEventListeners of ConnectionEvents. + * Instantiates a new ConnectionEvent which wraps sqlException and invokes + * either connectionClose or connectionErrorOccurred on listener as + * appropriate. + * + * @param eventType + * value indicating whether connectionClosed or + * connectionErrorOccurred called + * @param sqlException + * the exception being thrown + */ + protected synchronized void callConnectionEventListeners(int eventType, SQLException sqlException) { + + if (this.connectionEventListeners == null) { + + return; + } + + Iterator> iterator = this.connectionEventListeners.entrySet().iterator(); + + ConnectionEvent connectionevent = new ConnectionEvent(this, sqlException); + + while (iterator.hasNext()) { + + ConnectionEventListener connectioneventlistener = iterator.next().getValue(); + + if (eventType == CONNECTION_CLOSED_EVENT) { + connectioneventlistener.connectionClosed(connectionevent); + } else if (eventType == CONNECTION_ERROR_EVENT) { + connectioneventlistener.connectionErrorOccurred(connectionevent); + } + } + } + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java new file mode 100644 index 0000000..b9be8b1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java @@ -0,0 +1,617 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.Constructor; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.log.Log; + +/* + * XA BEGIN [JOIN | RESUME] XA START TRANSACTION [JOIN | RESUME] XA + * COMMIT [ONE PHASE] XA END [SUSPEND [FOR MIGRATE]] XA PREPARE + * XA RECOVER XA ROLLBACK + */ + +/** + * An object that provides support for distributed transactions. An XAConnection object may be enlisted in a distributed transaction by means of + * an XAResource object. A transaction manager, usually part of a middle tier server, manages an XAConnection object through the + * XAResource object. + * + *

    + * An application programmer does not use this interface directly; rather, it is used by a transaction manager working in the middle tier server. + */ +public class MysqlXAConnection extends MysqlPooledConnection implements XAConnection, XAResource { + + private static final int MAX_COMMAND_LENGTH = 300; + + private com.mysql.jdbc.Connection underlyingConnection; + + private final static Map MYSQL_ERROR_CODES_TO_XA_ERROR_CODES; + + private Log log; + + protected boolean logXaCommands; + + static { + HashMap temp = new HashMap(); + + temp.put(1397, XAException.XAER_NOTA); + temp.put(1398, XAException.XAER_INVAL); + temp.put(1399, XAException.XAER_RMFAIL); + temp.put(1400, XAException.XAER_OUTSIDE); + temp.put(1401, XAException.XAER_RMERR); + temp.put(1402, XAException.XA_RBROLLBACK); + temp.put(1440, XAException.XAER_DUPID); + temp.put(1613, XAException.XA_RBTIMEOUT); + temp.put(1614, XAException.XA_RBDEADLOCK); + + MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp); + } + + private static final Constructor JDBC_4_XA_CONNECTION_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_XA_CONNECTION_WRAPPER_CTOR = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4MysqlXAConnection") + .getConstructor(new Class[] { com.mysql.jdbc.Connection.class, Boolean.TYPE }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_XA_CONNECTION_WRAPPER_CTOR = null; + } + } + + protected static MysqlXAConnection getInstance(com.mysql.jdbc.Connection mysqlConnection, boolean logXaCommands) throws SQLException { + if (!Util.isJdbc4()) { + return new MysqlXAConnection(mysqlConnection, logXaCommands); + } + + return (MysqlXAConnection) Util.handleNewInstance(JDBC_4_XA_CONNECTION_WRAPPER_CTOR, new Object[] { mysqlConnection, Boolean.valueOf(logXaCommands) }, + mysqlConnection.getExceptionInterceptor()); + } + + /** + * @param connection + */ + public MysqlXAConnection(com.mysql.jdbc.Connection connection, boolean logXaCommands) throws SQLException { + super(connection); + this.underlyingConnection = connection; + this.log = connection.getLog(); + this.logXaCommands = logXaCommands; + } + + /** + * Retrieves an XAResource object that the transaction + * manager will use to manage this XAConnection object's + * participation in a distributed transaction. + * + * @return the XAResource object + * @exception SQLException + * if a database access error occurs + */ + public XAResource getXAResource() throws SQLException { + return this; + } + + /** + * Obtains the current transaction timeout value set for this XAResource + * instance. If XAResource.setTransactionTimeout was not used prior to + * invoking this method, the return value is the default timeout set for the + * resource manager; otherwise, the value used in the previous + * setTransactionTimeout call is returned. + * + * @return the transaction timeout value in seconds. + * + * @throws XAException + * An error has occurred. Possible exception values are + * XAER_RMERR and XAER_RMFAIL. + */ + public int getTransactionTimeout() throws XAException { + return 0; + } + + /** + * Sets the current transaction timeout value for this XAResource instance. + * Once set, this timeout value is effective until setTransactionTimeout is + * invoked again with a different value. + * + * To reset the timeout value to the default value used by the resource + * manager, set the value to zero. If the timeout operation is performed + * successfully, the method returns true; otherwise false. + * + * If a resource manager does not support explicitly setting the transaction + * timeout value, this method returns false. + * + * @parameter seconds The transaction timeout value in seconds. + * + * @return true if the transaction timeout value is set successfully; + * otherwise false. + * + * @throws XAException + * An error has occurred. Possible exception values are + * XAER_RMERR, XAER_RMFAIL, or XAER_INVAL. + */ + public boolean setTransactionTimeout(int arg0) throws XAException { + return false; + } + + /** + * This method is called to determine if the resource manager instance + * represented by the target object is the same as the resouce manager + * instance represented by the parameter xares. + * + * @parameter xares An XAResource object whose resource manager instance is + * to be compared with the resource manager instance of the + * target object. + * + * @return true if it's the same RM instance; otherwise false. + * + * @throws XAException + * An error has occurred. Possible exception values are + * XAER_RMERR and XAER_RMFAIL. + */ + public boolean isSameRM(XAResource xares) throws XAException { + + if (xares instanceof MysqlXAConnection) { + return this.underlyingConnection.isSameResource(((MysqlXAConnection) xares).underlyingConnection); + } + + return false; + } + + /** + * This method is called to obtain a list of prepared transaction branches + * from a resource manager. The transaction manager calls this method during + * recovery to obtain the list of transaction branches that are currently in + * prepared or heuristically completed states. + * + * The flag parameter indicates where the recover scan should start or end, + * or start and end. This method may be invoked one or more times during a + * recovery scan. The resource manager maintains a cursor which marks the + * current position of the prepared or heuristically completed transaction list. + * Each invocation of the recover method moves the cursor passed the set of Xids + * that are returned. + * + * Two consecutive invocation of this method that starts from the + * beginning of the list must return the same list of transaction branches + * unless one of the following takes place: + * + * - the transaction manager invokes the commit, forget, prepare, or rollback method for that resource + * manager, between the two consecutive invocation of the recovery scan. + * + * - the resource manager heuristically completes some transaction branches + * between the two invocation of the recovery scan. + * + * @param flag + * One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be + * used when no other flags are set in the parameter. + * + * @returns The resource manager returns zero or more XIDs of the + * transaction branches that are currently in a prepared or + * heuristically completed state. If an error occurs during the + * operation, the resource manager should throw the appropriate + * XAException. + * + * @throws XAException + * An error has occurred. Possible values are XAER_RMERR, + * XAER_RMFAIL, XAER_INVAL, and XAER_PROTO. + */ + public Xid[] recover(int flag) throws XAException { + return recover(this.underlyingConnection, flag); + } + + protected static Xid[] recover(Connection c, int flag) throws XAException { + /* + * The XA RECOVER statement returns information for those XA transactions on the MySQL server that are in the PREPARED state. (See Section 13.4.7.2, �XA + * Transaction States�.) The output includes a row for each such XA transaction on the server, regardless of which client started it. + * + * XA RECOVER output rows look like this (for an example xid value consisting of the parts 'abc', 'def', and 7): + * + * mysql> XA RECOVER; + * +----------+--------------+--------------+--------+ + * | formatID | gtrid_length | bqual_length | data | + * +----------+--------------+--------------+--------+ + * | 7 | 3 | 3 | abcdef | + * +----------+--------------+--------------+--------+ + * + * The output columns have the following meanings: + * + * formatID is the formatID part of the transaction xid + * gtrid_length is the length in bytes of the gtrid part of the xid + * bqual_length is the length in bytes of the bqual part of the xid + * data is the concatenation of the gtrid and bqual parts of the xid + */ + + boolean startRscan = ((flag & TMSTARTRSCAN) > 0); + boolean endRscan = ((flag & TMENDRSCAN) > 0); + + if (!startRscan && !endRscan && flag != TMNOFLAGS) { + throw new MysqlXAException(XAException.XAER_INVAL, Messages.getString("MysqlXAConnection.001"), null); + } + + // + // We return all recovered XIDs at once, so if not TMSTARTRSCAN, return no new XIDs + // + // We don't attempt to maintain state to check for TMNOFLAGS "outside" of a scan + // + + if (!startRscan) { + return new Xid[0]; + } + + ResultSet rs = null; + Statement stmt = null; + + List recoveredXidList = new ArrayList(); + + try { + // TODO: Cache this for lifetime of XAConnection + stmt = c.createStatement(); + + rs = stmt.executeQuery("XA RECOVER"); + + while (rs.next()) { + final int formatId = rs.getInt(1); + int gtridLength = rs.getInt(2); + int bqualLength = rs.getInt(3); + byte[] gtridAndBqual = rs.getBytes(4); + + final byte[] gtrid = new byte[gtridLength]; + final byte[] bqual = new byte[bqualLength]; + + if (gtridAndBqual.length != (gtridLength + bqualLength)) { + throw new MysqlXAException(XAException.XA_RBPROTO, Messages.getString("MysqlXAConnection.002"), null); + } + + System.arraycopy(gtridAndBqual, 0, gtrid, 0, gtridLength); + System.arraycopy(gtridAndBqual, gtridLength, bqual, 0, bqualLength); + + recoveredXidList.add(new MysqlXid(gtrid, bqual, formatId)); + } + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } + } + } + + int numXids = recoveredXidList.size(); + + Xid[] asXids = new Xid[numXids]; + Object[] asObjects = recoveredXidList.toArray(); + + for (int i = 0; i < numXids; i++) { + asXids[i] = (Xid) asObjects[i]; + } + + return asXids; + } + + /** + * Asks the resource manager to prepare for a transaction commit of the + * transaction specified in xid. + * + * @parameter xid A global transaction identifier. + * + * @returns A value indicating the resource manager's vote on the outcome of + * the transaction. + * + * The possible values are: XA_RDONLY or XA_OK. If the resource manager + * wants to roll back the transaction, it should do so by raising an + * appropriate XAException in the prepare method. + * + * @throws XAException + * An error has occurred. Possible exception values are: XA_RB*, + * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or + * XAER_PROTO. + */ + public int prepare(Xid xid) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA PREPARE "); + appendXid(commandBuf, xid); + + dispatchCommand(commandBuf.toString()); + + return XA_OK; // TODO: Check for read-only + } + + /** + * Tells the resource manager to forget about a heuristically completed + * transaction branch. + * + * @parameter xid A global transaction identifier. + * + * @throws XAException + * An error has occurred. Possible exception values are + * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or + * XAER_PROTO. + */ + public void forget(Xid xid) throws XAException { + // mysql doesn't support this + } + + /** + * Informs the resource manager to roll back work done on behalf of a + * transaction branch. + * + * @parameter xid A global transaction identifier. + * + * @throws XAException + * An error has occurred. Possible XAExceptions are XA_HEURHAZ, + * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, + * XAER_NOTA, XAER_INVAL, or XAER_PROTO. + * + * If the transaction branch is already marked rollback-only the resource + * manager may throw one of the XA_RB* exceptions. + * + * Upon return, the resource manager has rolled back the branch's work and + * has released all held resources. + */ + public void rollback(Xid xid) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA ROLLBACK "); + appendXid(commandBuf, xid); + + try { + dispatchCommand(commandBuf.toString()); + } finally { + this.underlyingConnection.setInGlobalTx(false); + } + } + + /** + * Ends the work performed on behalf of a transaction branch. + * + * The resource manager disassociates the XA resource from the transaction + * branch specified and lets the transaction complete. + * + * If TMSUSPEND is specified in the flags, the transaction branch is + * temporarily suspended in an incomplete state. The transaction context is + * in a suspended state and must be resumed via the start method with + * TMRESUME specified. + * + * If TMFAIL is specified, the portion of work has failed. The resource + * manager may mark the transaction as rollback-only + * + * If TMSUCCESS is specified, the portion of work has completed + * successfully. + * + * @parameter xid A global transaction identifier that is the same as the + * identifier used previously in the start method. + * + * @parameter flags One of TMSUCCESS, TMFAIL, or TMSUSPEND. + * + * @throws XAException + * - + * An error has occurred. Possible XAException values are + * XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, XAER_PROTO, + * or XA_RB*. + */ + public void end(Xid xid, int flags) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA END "); + appendXid(commandBuf, xid); + + switch (flags) { + case TMSUCCESS: + break; // no-op + case TMSUSPEND: + commandBuf.append(" SUSPEND"); + break; + case TMFAIL: + break; // no-op + default: + throw new XAException(XAException.XAER_INVAL); + } + + dispatchCommand(commandBuf.toString()); + } + + /** + * Starts work on behalf of a transaction branch specified in xid. + * + * If TMJOIN is specified, the start applies to joining a transaction + * previously seen by the resource manager. + * + * If TMRESUME is specified, the start applies to resuming a suspended + * transaction specified in the parameter xid. + * + * If neither TMJOIN nor TMRESUME is specified and the transaction specified + * by xid has previously been seen by the resource manager, the resource + * manager throws the XAException exception with XAER_DUPID error code. + * + * @parameter xid A global transaction identifier to be associated with the + * resource. + * + * @parameter flags One of TMNOFLAGS, TMJOIN, or TMRESUME. + * + * @throws XAException + * An error has occurred. Possible exceptions are XA_RB*, + * XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, + * XAER_INVAL, or XAER_PROTO. + */ + public void start(Xid xid, int flags) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA START "); + appendXid(commandBuf, xid); + + switch (flags) { + case TMJOIN: + commandBuf.append(" JOIN"); + break; + case TMRESUME: + commandBuf.append(" RESUME"); + break; + case TMNOFLAGS: + // no-op + break; + default: + throw new XAException(XAException.XAER_INVAL); + } + + dispatchCommand(commandBuf.toString()); + + this.underlyingConnection.setInGlobalTx(true); + } + + /** + * Commits the global transaction specified by xid. + * + * @parameter xid A global transaction identifier + * @parameter onePhase - If true, the resource manager should use a + * one-phase commit protocol to commit the work done on behalf of + * xid. + * + * @throws XAException + * An error has occurred. Possible XAExceptions are XA_HEURHAZ, + * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, + * XAER_NOTA, XAER_INVAL, or XAER_PROTO. + * + * If the resource manager did not commit the transaction and the parameter + * onePhase is set to true, the resource manager may throw one of the XA_RB* + * exceptions. + * + * Upon return, the resource manager has rolled back the branch's work and + * has released all held resources. + */ + + public void commit(Xid xid, boolean onePhase) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA COMMIT "); + appendXid(commandBuf, xid); + + if (onePhase) { + commandBuf.append(" ONE PHASE"); + } + + try { + dispatchCommand(commandBuf.toString()); + } finally { + this.underlyingConnection.setInGlobalTx(false); + } + } + + private ResultSet dispatchCommand(String command) throws XAException { + Statement stmt = null; + + try { + if (this.logXaCommands) { + this.log.logDebug("Executing XA statement: " + command); + } + + // TODO: Cache this for lifetime of XAConnection + stmt = this.underlyingConnection.createStatement(); + + stmt.execute(command); + + ResultSet rs = stmt.getResultSet(); + + return rs; + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + } + } + } + } + + protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) { + Integer xaCode = MYSQL_ERROR_CODES_TO_XA_ERROR_CODES.get(sqlEx.getErrorCode()); + + if (xaCode != null) { + return (XAException) new MysqlXAException(xaCode.intValue(), sqlEx.getMessage(), null).initCause(sqlEx); + } + + return (XAException) new MysqlXAException(XAException.XAER_RMFAIL, Messages.getString("MysqlXAConnection.003"), null).initCause(sqlEx); + } + + private static void appendXid(StringBuilder builder, Xid xid) { + byte[] gtrid = xid.getGlobalTransactionId(); + byte[] btrid = xid.getBranchQualifier(); + + if (gtrid != null) { + StringUtils.appendAsHex(builder, gtrid); + } + + builder.append(','); + if (btrid != null) { + StringUtils.appendAsHex(builder, btrid); + } + + builder.append(','); + StringUtils.appendAsHex(builder, xid.getFormatId()); + } + + /* + * (non-Javadoc) + * + * @see javax.sql.PooledConnection#getConnection() + */ + @Override + public synchronized Connection getConnection() throws SQLException { + Connection connToWrap = getConnection(false, true); + + return connToWrap; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java new file mode 100644 index 0000000..a2c6e8e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.XAConnection; + +public class MysqlXADataSource extends MysqlDataSource implements javax.sql.XADataSource { + + static final long serialVersionUID = 7911390333152247455L; + + /** + * Default no-arg constructor is required by specification. + */ + public MysqlXADataSource() { + } + + /** + * @see javax.sql.XADataSource#getXAConnection() + */ + public XAConnection getXAConnection() throws SQLException { + + Connection conn = getConnection(); + + return wrapConnection(conn); + } + + /** + * @see javax.sql.XADataSource#getXAConnection(String, String) + */ + public XAConnection getXAConnection(String u, String p) throws SQLException { + + Connection conn = getConnection(u, p); + + return wrapConnection(conn); + } + + /** + * Wraps a connection as a 'fake' XAConnection + */ + + private XAConnection wrapConnection(Connection conn) throws SQLException { + if (getPinGlobalTxToPhysicalConnection() || ((com.mysql.jdbc.Connection) conn).getPinGlobalTxToPhysicalConnection()) { + return SuspendableXAConnection.getInstance((com.mysql.jdbc.Connection) conn); + } + + return MysqlXAConnection.getInstance((com.mysql.jdbc.Connection) conn, getLogXaCommands()); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java new file mode 100644 index 0000000..c5a71af --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java @@ -0,0 +1,66 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import javax.transaction.xa.XAException; + +/** + * The stock XAException class isn't too friendly (i.e. no error messages), so we extend it a bit. + */ +class MysqlXAException extends XAException { + private static final long serialVersionUID = -9075817535836563004L; + + private String message; + protected String xidAsString; + + public MysqlXAException(int errorCode, String message, String xidAsString) { + super(errorCode); + this.message = message; + this.xidAsString = xidAsString; + } + + public MysqlXAException(String message, String xidAsString) { + super(); + + this.message = message; + this.xidAsString = xidAsString; + } + + @Override + public String getMessage() { + String superMessage = super.getMessage(); + StringBuilder returnedMessage = new StringBuilder(); + + if (superMessage != null) { + returnedMessage.append(superMessage); + returnedMessage.append(":"); + } + + if (this.message != null) { + returnedMessage.append(this.message); + } + + return returnedMessage.toString(); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java new file mode 100644 index 0000000..4486f72 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java @@ -0,0 +1,110 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import javax.transaction.xa.Xid; + +/** + * Implementation of the XID interface for MySQL XA + */ +public class MysqlXid implements Xid { + + int hash = 0; + + byte[] myBqual; + + int myFormatId; + + byte[] myGtrid; + + public MysqlXid(byte[] gtrid, byte[] bqual, int formatId) { + this.myGtrid = gtrid; + this.myBqual = bqual; + this.myFormatId = formatId; + } + + @Override + public boolean equals(Object another) { + + if (another instanceof Xid) { + Xid anotherAsXid = (Xid) another; + + if (this.myFormatId != anotherAsXid.getFormatId()) { + return false; + } + + byte[] otherBqual = anotherAsXid.getBranchQualifier(); + byte[] otherGtrid = anotherAsXid.getGlobalTransactionId(); + + if (otherGtrid != null && otherGtrid.length == this.myGtrid.length) { + int length = otherGtrid.length; + + for (int i = 0; i < length; i++) { + if (otherGtrid[i] != this.myGtrid[i]) { + return false; + } + } + + if (otherBqual != null && otherBqual.length == this.myBqual.length) { + length = otherBqual.length; + + for (int i = 0; i < length; i++) { + if (otherBqual[i] != this.myBqual[i]) { + return false; + } + } + } else { + return false; + } + + return true; + } + } + + return false; + } + + public byte[] getBranchQualifier() { + return this.myBqual; + } + + public int getFormatId() { + return this.myFormatId; + }; + + public byte[] getGlobalTransactionId() { + return this.myGtrid; + } + + @Override + public synchronized int hashCode() { + if (this.hash == 0) { + for (int i = 0; i < this.myGtrid.length; i++) { + this.hash = 33 * this.hash + this.myGtrid[i]; + } + } + + return this.hash; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java new file mode 100644 index 0000000..4d6c717 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java @@ -0,0 +1,925 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Util; + +/** + * Wraps prepared statements so that errors can be reported correctly to ConnectionEventListeners. + */ +public class PreparedStatementWrapper extends StatementWrapper implements PreparedStatement { + private static final Constructor JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + String jdbc4ClassName = Util.isJdbc42() ? "com.mysql.jdbc.jdbc2.optional.JDBC42PreparedStatementWrapper" + : "com.mysql.jdbc.jdbc2.optional.JDBC4PreparedStatementWrapper"; + JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR = Class.forName(jdbc4ClassName) + .getConstructor(new Class[] { ConnectionWrapper.class, MysqlPooledConnection.class, PreparedStatement.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR = null; + } + } + + protected static PreparedStatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) throws SQLException { + if (!Util.isJdbc4()) { + return new PreparedStatementWrapper(c, conn, toWrap); + } + + return (PreparedStatementWrapper) Util.handleNewInstance(JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR, new Object[] { c, conn, toWrap }, + conn.getExceptionInterceptor()); + } + + PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) { + super(c, conn, toWrap); + } + + public void setArray(int parameterIndex, Array x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setArray(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBigDecimal(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(int parameterIndex, Blob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBoolean(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setByte(int parameterIndex, byte x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setByte(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBytes(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(int parameterIndex, Clob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(int parameterIndex, Date x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDate(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDate(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDouble(int parameterIndex, double x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDouble(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setFloat(int parameterIndex, float x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setFloat(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setInt(int parameterIndex, int x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setInt(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setLong(int parameterIndex, long x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setLong(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public ResultSetMetaData getMetaData() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).getMetaData(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setNull(int parameterIndex, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNull(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNull(parameterIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public ParameterMetaData getParameterMetaData() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).getParameterMetaData(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setRef(int parameterIndex, Ref x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setRef(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setShort(int parameterIndex, short x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setShort(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setString(int parameterIndex, String x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setString(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(int parameterIndex, Time x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTime(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTime(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTimestamp(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTimestamp(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setURL(int parameterIndex, URL x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setURL(parameterIndex, x); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * @param parameterIndex + * @param x + * @param length + * + * @throws SQLException + * + * @see java.sql.PreparedStatement#setUnicodeStream(int, java.io.InputStream, int) + * @deprecated + */ + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setUnicodeStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void addBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).addBatch(); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearParameters() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).clearParameters(); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean execute() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).execute(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet executeQuery() throws SQLException { + + ResultSet rs = null; + try { + if (this.wrappedStmt == null) { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + rs = ((PreparedStatement) this.wrappedStmt).executeQuery(); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return rs; + } + + public int executeUpdate() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).executeUpdate(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(super.toString()); + + if (this.wrappedStmt != null) { + buf.append(": "); + try { + buf.append(((com.mysql.jdbc.PreparedStatement) this.wrappedStmt).asSql()); + } catch (SQLException sqlEx) { + buf.append("EXCEPTION: " + sqlEx.toString()); + } + } + + return buf.toString(); + } + + // + // public void setAsciiStream(int parameterIndex, InputStream x) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setAsciiStream( + // parameterIndex, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setAsciiStream(int parameterIndex, InputStream x, long length) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setAsciiStream( + // parameterIndex, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(int parameterIndex, InputStream x) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setBinaryStream( + // parameterIndex, x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBinaryStream(int parameterIndex, InputStream x, long length) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setBinaryStream( + // parameterIndex, x, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(int parameterIndex, InputStream inputStream) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, + // inputStream); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setBlob(int parameterIndex, InputStream inputStream, long length) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, + // inputStream, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(int parameterIndex, Reader reader) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setCharacterStream( + // parameterIndex, reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setCharacterStream(int parameterIndex, Reader reader, + // long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setCharacterStream( + // parameterIndex, reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(int parameterIndex, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, + // reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setClob(int parameterIndex, Reader reader, long length) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, + // reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNCharacterStream(int parameterIndex, Reader value) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNCharacterStream( + // parameterIndex, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNCharacterStream(int parameterIndex, Reader value, + // long length) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNCharacterStream( + // parameterIndex, value, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, NClob value) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, + // value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, Reader reader) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, + // reader); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNClob(int parameterIndex, Reader reader, long length) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, + // reader, length); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setNString(int parameterIndex, String value) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setNString( + // parameterIndex, value); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setRowId(int parameterIndex, RowId x) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex, + // x); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public void setSQLXML(int parameterIndex, SQLXML xmlObject) + // throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setSQLXML( + // parameterIndex, xmlObject); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public boolean isClosed() throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((PreparedStatement) this.wrappedStmt).isClosed(); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return true; + // } + // + // public boolean isPoolable() throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // return ((PreparedStatement) this.wrappedStmt).isPoolable(); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // + // return false; + // } + // + // public void setPoolable(boolean poolable) throws SQLException { + // try { + // if (this.wrappedStmt != null) { + // ((PreparedStatement) this.wrappedStmt).setPoolable(poolable); + // } else { + // throw SQLError.createSQLException( + // "No operations allowed after statement closed", + // SQLError.SQL_STATE_GENERAL_ERROR); + // } + // } catch (SQLException sqlEx) { + // checkAndFireConnectionError(sqlEx); + // } + // } + // + // public boolean isWrapperFor(Class arg0) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + // + // public Object unwrap(Class arg0) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + + /** + * Same as PreparedStatement.executeUpdate() but returns long instead of int. + */ + public long executeLargeUpdate() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((com.mysql.jdbc.PreparedStatement) this.wrappedStmt).executeLargeUpdate(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java new file mode 100644 index 0000000..b293f2f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java @@ -0,0 +1,725 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.Constructor; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.Util; + +/** + * Wraps statements so that errors can be reported correctly to ConnectionEventListeners. + */ +public class StatementWrapper extends WrapperBase implements Statement { + private static final Constructor JDBC_4_STATEMENT_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_STATEMENT_WRAPPER_CTOR = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4StatementWrapper") + .getConstructor(new Class[] { ConnectionWrapper.class, MysqlPooledConnection.class, Statement.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_STATEMENT_WRAPPER_CTOR = null; + } + } + + protected static StatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, Statement toWrap) throws SQLException { + if (!Util.isJdbc4()) { + return new StatementWrapper(c, conn, toWrap); + } + + return (StatementWrapper) Util.handleNewInstance(JDBC_4_STATEMENT_WRAPPER_CTOR, new Object[] { c, conn, toWrap }, conn.getExceptionInterceptor()); + } + + protected Statement wrappedStmt; + + protected ConnectionWrapper wrappedConn; + + public StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, Statement toWrap) { + super(conn); + this.wrappedStmt = toWrap; + this.wrappedConn = c; + } + + public Connection getConnection() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedConn; + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public void setCursorName(String name) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setCursorName(name); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setEscapeProcessing(boolean enable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setEscapeProcessing(enable); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setFetchDirection(int direction) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setFetchDirection(direction); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getFetchDirection() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getFetchDirection(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return ResultSet.FETCH_FORWARD; // we actually never get here, but the compiler can't figure that out + } + + public void setFetchSize(int rows) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setFetchSize(rows); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getFetchSize() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getFetchSize(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet getGeneratedKeys() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getGeneratedKeys(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public void setMaxFieldSize(int max) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setMaxFieldSize(max); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getMaxFieldSize() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMaxFieldSize(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public void setMaxRows(int max) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setMaxRows(max); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getMaxRows() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMaxRows(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public boolean getMoreResults() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMoreResults(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public boolean getMoreResults(int current) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMoreResults(current); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public void setQueryTimeout(int seconds) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setQueryTimeout(seconds); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getQueryTimeout() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getQueryTimeout(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public ResultSet getResultSet() throws SQLException { + try { + if (this.wrappedStmt != null) { + ResultSet rs = this.wrappedStmt.getResultSet(); + + if (rs != null) { + ((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this); + } + return rs; + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public int getResultSetConcurrency() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetConcurrency(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public int getResultSetHoldability() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetHoldability(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return Statement.CLOSE_CURRENT_RESULT; + } + + public int getResultSetType() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetType(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return ResultSet.TYPE_FORWARD_ONLY; + } + + public int getUpdateCount() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getUpdateCount(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; + } + + public SQLWarning getWarnings() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getWarnings(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void addBatch(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.addBatch(sql); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void cancel() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.cancel(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.clearBatch(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearWarnings() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.clearWarnings(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void close() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.close(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } finally { + this.wrappedStmt = null; + this.pooledConnection = null; + } + } + + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, columnIndexes); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, columnNames); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public int[] executeBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeBatch(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet executeQuery(String sql) throws SQLException { + ResultSet rs = null; + try { + if (this.wrappedStmt == null) { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + rs = this.wrappedStmt.executeQuery(sql); + ((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return rs; + } + + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, columnIndexes); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, columnNames); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public void enableStreamingResults() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((com.mysql.jdbc.Statement) this.wrappedStmt).enableStreamingResults(); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", SQLError.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * JDBC 4.2 + * Same as {@link #executeBatch()} but returns long[] instead of int[]. + */ + public long[] executeLargeBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeBatch(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, columnIndexes); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, String[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, columnNames); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #getMaxRows()} but returns long instead of int. + */ + public long getLargeMaxRows() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).getLargeMaxRows(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #getUpdateCount()} but returns long instead of int; + */ + public long getLargeUpdateCount() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).getLargeUpdateCount(); + } + + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; + } + + /** + * JDBC 4.2 + * Same as {@link #setMaxRows(int)} but accepts a long value instead of an int. + */ + public void setLargeMaxRows(long max) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((StatementImpl) this.wrappedStmt).setLargeMaxRows(max); + } else { + throw SQLError.createSQLException("Statement already closed", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java new file mode 100644 index 0000000..98e1c24 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java @@ -0,0 +1,202 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.Constructor; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.Util; + +public class SuspendableXAConnection extends MysqlPooledConnection implements XAConnection, XAResource { + + private static final Constructor JDBC_4_XA_CONNECTION_WRAPPER_CTOR; + + static { + if (Util.isJdbc4()) { + try { + JDBC_4_XA_CONNECTION_WRAPPER_CTOR = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection") + .getConstructor(new Class[] { Connection.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } else { + JDBC_4_XA_CONNECTION_WRAPPER_CTOR = null; + } + } + + protected static SuspendableXAConnection getInstance(Connection mysqlConnection) throws SQLException { + if (!Util.isJdbc4()) { + return new SuspendableXAConnection(mysqlConnection); + } + + return (SuspendableXAConnection) Util.handleNewInstance(JDBC_4_XA_CONNECTION_WRAPPER_CTOR, new Object[] { mysqlConnection }, + mysqlConnection.getExceptionInterceptor()); + } + + public SuspendableXAConnection(Connection connection) { + super(connection); + this.underlyingConnection = connection; + } + + private static final Map XIDS_TO_PHYSICAL_CONNECTIONS = new HashMap(); + + private Xid currentXid; + + private XAConnection currentXAConnection; + private XAResource currentXAResource; + + private Connection underlyingConnection; + + private static synchronized XAConnection findConnectionForXid(Connection connectionToWrap, Xid xid) throws SQLException { + // TODO: check for same GTRID, but different BQUALs...MySQL doesn't allow this yet + + // Note, we don't need to check for XIDs here, because MySQL itself will complain with a XAER_NOTA if need be. + + XAConnection conn = XIDS_TO_PHYSICAL_CONNECTIONS.get(xid); + + if (conn == null) { + conn = new MysqlXAConnection(connectionToWrap, connectionToWrap.getLogXaCommands()); + XIDS_TO_PHYSICAL_CONNECTIONS.put(xid, conn); + } + + return conn; + } + + private static synchronized void removeXAConnectionMapping(Xid xid) { + XIDS_TO_PHYSICAL_CONNECTIONS.remove(xid); + } + + private synchronized void switchToXid(Xid xid) throws XAException { + if (xid == null) { + throw new XAException(); + } + + try { + if (!xid.equals(this.currentXid)) { + XAConnection toSwitchTo = findConnectionForXid(this.underlyingConnection, xid); + this.currentXAConnection = toSwitchTo; + this.currentXid = xid; + this.currentXAResource = toSwitchTo.getXAResource(); + } + } catch (SQLException sqlEx) { + throw new XAException(); + } + } + + public XAResource getXAResource() throws SQLException { + return this; + } + + public void commit(Xid xid, boolean arg1) throws XAException { + switchToXid(xid); + this.currentXAResource.commit(xid, arg1); + removeXAConnectionMapping(xid); + } + + public void end(Xid xid, int arg1) throws XAException { + switchToXid(xid); + this.currentXAResource.end(xid, arg1); + } + + public void forget(Xid xid) throws XAException { + switchToXid(xid); + this.currentXAResource.forget(xid); + // remove? + removeXAConnectionMapping(xid); + } + + public int getTransactionTimeout() throws XAException { + return 0; + } + + public boolean isSameRM(XAResource xaRes) throws XAException { + return xaRes == this; + } + + public int prepare(Xid xid) throws XAException { + switchToXid(xid); + return this.currentXAResource.prepare(xid); + } + + public Xid[] recover(int flag) throws XAException { + return MysqlXAConnection.recover(this.underlyingConnection, flag); + } + + public void rollback(Xid xid) throws XAException { + switchToXid(xid); + this.currentXAResource.rollback(xid); + removeXAConnectionMapping(xid); + } + + public boolean setTransactionTimeout(int arg0) throws XAException { + return false; + } + + public void start(Xid xid, int arg1) throws XAException { + switchToXid(xid); + + if (arg1 != XAResource.TMJOIN) { + this.currentXAResource.start(xid, arg1); + + return; + } + + // + // Emulate join, by using resume on the same physical connection + // + + this.currentXAResource.start(xid, XAResource.TMRESUME); + } + + @Override + public synchronized java.sql.Connection getConnection() throws SQLException { + if (this.currentXAConnection == null) { + return getConnection(false, true); + } + + return this.currentXAConnection.getConnection(); + } + + @Override + public void close() throws SQLException { + if (this.currentXAConnection == null) { + super.close(); + } else { + removeXAConnectionMapping(this.currentXid); + this.currentXAConnection.close(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java new file mode 100644 index 0000000..972be67 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java @@ -0,0 +1,119 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jdbc2.optional; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.Map; + +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.SQLError; + +/** + * Base class for all wrapped instances created by LogicalHandle + */ +abstract class WrapperBase { + protected MysqlPooledConnection pooledConnection; + + /** + * Fires connection error event if required, before re-throwing exception + * + * @param sqlEx + * the SQLException that has occurred + * @throws SQLException + * (rethrown) + */ + protected void checkAndFireConnectionError(SQLException sqlEx) throws SQLException { + if (this.pooledConnection != null) { + if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + this.pooledConnection.callConnectionEventListeners(MysqlPooledConnection.CONNECTION_ERROR_EVENT, sqlEx); + } + } + + throw sqlEx; + } + + protected Map, Object> unwrappedInterfaces = null; + protected ExceptionInterceptor exceptionInterceptor; + + protected WrapperBase(MysqlPooledConnection pooledConnection) { + this.pooledConnection = pooledConnection; + this.exceptionInterceptor = this.pooledConnection.getExceptionInterceptor(); + } + + protected class ConnectionErrorFiringInvocationHandler implements InvocationHandler { + Object invokeOn = null; + + public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) { + this.invokeOn = toInvokeOn; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object result = null; + + try { + result = method.invoke(this.invokeOn, args); + + if (result != null) { + result = proxyIfInterfaceIsJdbc(result, result.getClass()); + } + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof SQLException) { + checkAndFireConnectionError((SQLException) e.getTargetException()); + } else { + throw e; + } + } + + return result; + } + + /** + * Recursively checks for interfaces on the given object to determine + * if it implements a java.sql interface, and if so, proxies the + * instance so that we can catch and fire SQL errors. + * + * @param toProxy + * @param clazz + */ + private Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) { + Class[] interfaces = clazz.getInterfaces(); + + for (Class iclass : interfaces) { + String packageName = iclass.getPackage().getName(); + + if ("java.sql".equals(packageName) || "javax.sql".equals(packageName)) { + return Proxy.newProxyInstance(toProxy.getClass().getClassLoader(), interfaces, new ConnectionErrorFiringInvocationHandler(toProxy)); + } + + return proxyIfInterfaceIsJdbc(toProxy, iclass); + } + + return toProxy; + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManager.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManager.java new file mode 100644 index 0000000..0aad8ee --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManager.java @@ -0,0 +1,116 @@ +/* + Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jmx; + +import java.lang.management.ManagementFactory; +import java.sql.SQLException; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import com.mysql.jdbc.ConnectionGroupManager; +import com.mysql.jdbc.SQLError; + +public class LoadBalanceConnectionGroupManager implements LoadBalanceConnectionGroupManagerMBean { + + private boolean isJmxRegistered = false; + + public LoadBalanceConnectionGroupManager() { + + } + + public synchronized void registerJmx() throws SQLException { + if (this.isJmxRegistered) { + return; + } + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + ObjectName name = new ObjectName("com.mysql.jdbc.jmx:type=LoadBalanceConnectionGroupManager"); + mbs.registerMBean(this, name); + this.isJmxRegistered = true; + } catch (Exception e) { + throw SQLError.createSQLException("Unable to register load-balance management bean with JMX", null, e, null); + } + + } + + public void addHost(String group, String host, boolean forExisting) { + try { + ConnectionGroupManager.addHost(group, host, forExisting); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public int getActiveHostCount(String group) { + return ConnectionGroupManager.getActiveHostCount(group); + } + + public long getActiveLogicalConnectionCount(String group) { + return ConnectionGroupManager.getActiveLogicalConnectionCount(group); + } + + public long getActivePhysicalConnectionCount(String group) { + return ConnectionGroupManager.getActivePhysicalConnectionCount(group); + } + + public int getTotalHostCount(String group) { + return ConnectionGroupManager.getTotalHostCount(group); + + } + + public long getTotalLogicalConnectionCount(String group) { + return ConnectionGroupManager.getTotalLogicalConnectionCount(group); + + } + + public long getTotalPhysicalConnectionCount(String group) { + return ConnectionGroupManager.getTotalPhysicalConnectionCount(group); + + } + + public long getTotalTransactionCount(String group) { + return ConnectionGroupManager.getTotalTransactionCount(group); + + } + + public void removeHost(String group, String host) throws SQLException { + ConnectionGroupManager.removeHost(group, host); + + } + + public String getActiveHostsList(String group) { + return ConnectionGroupManager.getActiveHostLists(group); + } + + public String getRegisteredConnectionGroups() { + return ConnectionGroupManager.getRegisteredConnectionGroups(); + } + + public void stopNewConnectionsToHost(String group, String host) throws SQLException { + ConnectionGroupManager.removeHost(group, host); + + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java new file mode 100644 index 0000000..07b67af --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java @@ -0,0 +1,54 @@ +/* + Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jmx; + +import java.sql.SQLException; + +public interface LoadBalanceConnectionGroupManagerMBean { + + public abstract int getActiveHostCount(String group); + + public abstract int getTotalHostCount(String group); + + public abstract long getTotalLogicalConnectionCount(String group); + + public abstract long getActiveLogicalConnectionCount(String group); + + public abstract long getActivePhysicalConnectionCount(String group); + + public abstract long getTotalPhysicalConnectionCount(String group); + + public abstract long getTotalTransactionCount(String group); + + public abstract void removeHost(String group, String host) throws SQLException; + + public abstract void stopNewConnectionsToHost(String group, String host) throws SQLException; + + public abstract void addHost(String group, String host, boolean forExisting); + + public abstract String getActiveHostsList(String group); + + public abstract String getRegisteredConnectionGroups(); + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManager.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManager.java new file mode 100644 index 0000000..110ac90 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManager.java @@ -0,0 +1,132 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jmx; + +import java.lang.management.ManagementFactory; +import java.sql.SQLException; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import com.mysql.jdbc.ReplicationConnectionGroup; +import com.mysql.jdbc.ReplicationConnectionGroupManager; +import com.mysql.jdbc.SQLError; + +public class ReplicationGroupManager implements ReplicationGroupManagerMBean { + private boolean isJmxRegistered = false; + + public synchronized void registerJmx() throws SQLException { + if (this.isJmxRegistered) { + return; + } + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + ObjectName name = new ObjectName("com.mysql.jdbc.jmx:type=ReplicationGroupManager"); + mbs.registerMBean(this, name); + this.isJmxRegistered = true; + } catch (Exception e) { + throw SQLError.createSQLException("Unable to register replication host management bean with JMX", null, e, null); + } + + } + + public void addSlaveHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.addSlaveHost(groupFilter, host); + } + + public void removeSlaveHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.removeSlaveHost(groupFilter, host); + } + + public void promoteSlaveToMaster(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.promoteSlaveToMaster(groupFilter, host); + + } + + public void removeMasterHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.removeMasterHost(groupFilter, host); + + } + + public String getMasterHostsList(String group) { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (String host : ReplicationConnectionGroupManager.getMasterHosts(group)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(host); + } + return sb.toString(); + } + + public String getSlaveHostsList(String group) { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (String host : ReplicationConnectionGroupManager.getSlaveHosts(group)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(host); + } + return sb.toString(); + + } + + public String getRegisteredConnectionGroups() { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (ReplicationConnectionGroup group : ReplicationConnectionGroupManager.getGroupsMatching(null)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(group.getGroupName()); + } + return sb.toString(); + } + + public int getActiveMasterHostCount(String group) { + return ReplicationConnectionGroupManager.getMasterHosts(group).size(); + } + + public int getActiveSlaveHostCount(String group) { + return ReplicationConnectionGroupManager.getSlaveHosts(group).size(); + } + + public int getSlavePromotionCount(String group) { + return ReplicationConnectionGroupManager.getNumberOfMasterPromotion(group); + } + + public long getTotalLogicalConnectionCount(String group) { + return ReplicationConnectionGroupManager.getTotalConnectionCount(group); + } + + public long getActiveLogicalConnectionCount(String group) { + return ReplicationConnectionGroupManager.getActiveConnectionCount(group); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManagerMBean.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManagerMBean.java new file mode 100644 index 0000000..6cdccb9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/jmx/ReplicationGroupManagerMBean.java @@ -0,0 +1,54 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.jmx; + +import java.sql.SQLException; + +public interface ReplicationGroupManagerMBean { + + public abstract void addSlaveHost(String groupFilter, String host) throws SQLException; + + public abstract void removeSlaveHost(String groupFilter, String host) throws SQLException; + + public abstract void promoteSlaveToMaster(String groupFilter, String host) throws SQLException; + + public abstract void removeMasterHost(String groupFilter, String host) throws SQLException; + + public abstract String getMasterHostsList(String group); + + public abstract String getSlaveHostsList(String group); + + public abstract String getRegisteredConnectionGroups(); + + public abstract int getActiveMasterHostCount(String group); + + public abstract int getActiveSlaveHostCount(String group); + + public abstract int getSlavePromotionCount(String group); + + public abstract long getTotalLogicalConnectionCount(String group); + + public abstract long getActiveLogicalConnectionCount(String group); + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Jdk14Logger.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Jdk14Logger.java new file mode 100644 index 0000000..9c961ef --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Jdk14Logger.java @@ -0,0 +1,286 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * Logging functionality for JDK1.4 + */ +public class Jdk14Logger implements Log { + private static final Level DEBUG = Level.FINE; + + private static final Level ERROR = Level.SEVERE; + + private static final Level FATAL = Level.SEVERE; + + private static final Level INFO = Level.INFO; + + private static final Level TRACE = Level.FINEST; + + private static final Level WARN = Level.WARNING; + + /** + * The underlying logger from JDK-1.4 + */ + protected Logger jdkLogger = null; + + /** + * Creates a new Jdk14Logger object. + * + * @param name + */ + public Jdk14Logger(String name) { + this.jdkLogger = Logger.getLogger(name); + } + + /** + * @see com.mysql.jdbc.log.Log#isDebugEnabled() + */ + public boolean isDebugEnabled() { + return this.jdkLogger.isLoggable(Level.FINE); + } + + /** + * @see com.mysql.jdbc.log.Log#isErrorEnabled() + */ + public boolean isErrorEnabled() { + return this.jdkLogger.isLoggable(Level.SEVERE); + } + + /** + * @see com.mysql.jdbc.log.Log#isFatalEnabled() + */ + public boolean isFatalEnabled() { + return this.jdkLogger.isLoggable(Level.SEVERE); + } + + /** + * @see com.mysql.jdbc.log.Log#isInfoEnabled() + */ + public boolean isInfoEnabled() { + return this.jdkLogger.isLoggable(Level.INFO); + } + + /** + * @see com.mysql.jdbc.log.Log#isTraceEnabled() + */ + public boolean isTraceEnabled() { + return this.jdkLogger.isLoggable(Level.FINEST); + } + + /** + * @see com.mysql.jdbc.log.Log#isWarnEnabled() + */ + public boolean isWarnEnabled() { + return this.jdkLogger.isLoggable(Level.WARNING); + } + + /** + * Logs the given message instance using the 'debug' level + * + * @param message + * the message to log + */ + public void logDebug(Object message) { + logInternal(DEBUG, message, null); + } + + /** + * Logs the given message and Throwable at the 'debug' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logDebug(Object message, Throwable exception) { + logInternal(DEBUG, message, exception); + } + + /** + * Logs the given message instance using the 'error' level + * + * @param message + * the message to log + */ + public void logError(Object message) { + logInternal(ERROR, message, null); + } + + /** + * Logs the given message and Throwable at the 'error' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logError(Object message, Throwable exception) { + logInternal(ERROR, message, exception); + } + + /** + * Logs the given message instance using the 'fatal' level + * + * @param message + * the message to log + */ + public void logFatal(Object message) { + logInternal(FATAL, message, null); + } + + /** + * Logs the given message and Throwable at the 'fatal' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logFatal(Object message, Throwable exception) { + logInternal(FATAL, message, exception); + } + + /** + * Logs the given message instance using the 'info' level + * + * @param message + * the message to log + */ + public void logInfo(Object message) { + logInternal(INFO, message, null); + } + + /** + * Logs the given message and Throwable at the 'info' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logInfo(Object message, Throwable exception) { + logInternal(INFO, message, exception); + } + + /** + * Logs the given message instance using the 'trace' level + * + * @param message + * the message to log + */ + public void logTrace(Object message) { + logInternal(TRACE, message, null); + } + + /** + * Logs the given message and Throwable at the 'trace' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logTrace(Object message, Throwable exception) { + logInternal(TRACE, message, exception); + } + + /** + * Logs the given message instance using the 'warn' level + * + * @param message + * the message to log + */ + public void logWarn(Object message) { + logInternal(WARN, message, null); + } + + /** + * Logs the given message and Throwable at the 'warn' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logWarn(Object message, Throwable exception) { + logInternal(WARN, message, exception); + } + + private static final int findCallerStackDepth(StackTraceElement[] stackTrace) { + int numFrames = stackTrace.length; + + for (int i = 0; i < numFrames; i++) { + String callerClassName = stackTrace[i].getClassName(); + + if (!callerClassName.startsWith("com.mysql.jdbc") || callerClassName.startsWith("com.mysql.jdbc.compliance")) { + return i; + } + } + + return 0; + } + + private void logInternal(Level level, Object msg, Throwable exception) { + // + // only go through this exercise if the message will actually be logged. + // + + if (this.jdkLogger.isLoggable(level)) { + String messageAsString = null; + String callerMethodName = "N/A"; + String callerClassName = "N/A"; + //int lineNumber = 0; + //String fileName = "N/A"; + + if (msg instanceof ProfilerEvent) { + messageAsString = LogUtils.expandProfilerEventIfNecessary(msg).toString(); + } else { + Throwable locationException = new Throwable(); + StackTraceElement[] locations = locationException.getStackTrace(); + + int frameIdx = findCallerStackDepth(locations); + + if (frameIdx != 0) { + callerClassName = locations[frameIdx].getClassName(); + callerMethodName = locations[frameIdx].getMethodName(); + //lineNumber = locations[frameIdx].getLineNumber(); + //fileName = locations[frameIdx].getFileName(); + } + + messageAsString = String.valueOf(msg); + } + + if (exception == null) { + this.jdkLogger.logp(level, callerClassName, callerMethodName, messageAsString); + } else { + this.jdkLogger.logp(level, callerClassName, callerMethodName, messageAsString, exception); + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Log.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Log.java new file mode 100644 index 0000000..759ec6b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Log.java @@ -0,0 +1,179 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +/** + * Unified interface to logging facilities on different platforms + */ +public interface Log { + /** + * Is the 'debug' log level enabled? + * + * @return true if so. + */ + boolean isDebugEnabled(); + + /** + * Is the 'error' log level enabled? + * + * @return true if so. + */ + boolean isErrorEnabled(); + + /** + * Is the 'fatal' log level enabled? + * + * @return true if so. + */ + boolean isFatalEnabled(); + + /** + * Is the 'info' log level enabled? + * + * @return true if so. + */ + boolean isInfoEnabled(); + + /** + * Is the 'trace' log level enabled? + * + * @return true if so. + */ + boolean isTraceEnabled(); + + /** + * Is the 'warn' log level enabled? + * + * @return true if so. + */ + boolean isWarnEnabled(); + + /** + * Logs the given message instance using the 'debug' level + * + * @param msg + * the message to log + */ + void logDebug(Object msg); + + /** + * Logs the given message and Throwable at the 'debug' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logDebug(Object msg, Throwable thrown); + + /** + * Logs the given message instance using the 'error' level + * + * @param msg + * the message to log + */ + void logError(Object msg); + + /** + * Logs the given message and Throwable at the 'error' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logError(Object msg, Throwable thrown); + + /** + * Logs the given message instance using the 'fatal' level + * + * @param msg + * the message to log + */ + void logFatal(Object msg); + + /** + * Logs the given message and Throwable at the 'fatal' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logFatal(Object msg, Throwable thrown); + + /** + * Logs the given message instance using the 'info' level + * + * @param msg + * the message to log + */ + void logInfo(Object msg); + + /** + * Logs the given message and Throwable at the 'info' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logInfo(Object msg, Throwable thrown); + + /** + * Logs the given message instance using the 'trace' level + * + * @param msg + * the message to log + */ + void logTrace(Object msg); + + /** + * Logs the given message and Throwable at the 'trace' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logTrace(Object msg, Throwable thrown); + + /** + * Logs the given message instance using the 'warn' level + * + * @param msg + * the message to log + */ + void logWarn(Object msg); + + /** + * Logs the given message and Throwable at the 'warn' level. + * + * @param msg + * the message to log + * @param thrown + * the throwable to log (may be null) + */ + void logWarn(Object msg, Throwable thrown); +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogFactory.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogFactory.java new file mode 100644 index 0000000..f89e805 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogFactory.java @@ -0,0 +1,110 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.sql.SQLException; + +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.SQLError; + +/** + * Creates instances of loggers for the driver to use. + */ +public class LogFactory { + + /** + * Returns a logger instance of the given class, with the given instance + * name. + * + * @param className + * the class to instantiate + * @param instanceName + * the instance name + * @return a logger instance + * @throws SQLException + * if unable to create a logger instance + */ + public static Log getLogger(String className, String instanceName, ExceptionInterceptor exceptionInterceptor) throws SQLException { + + if (className == null) { + throw SQLError.createSQLException("Logger class can not be NULL", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + if (instanceName == null) { + throw SQLError.createSQLException("Logger instance name can not be NULL", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + try { + Class loggerClass = null; + + try { + loggerClass = Class.forName(className); + } catch (ClassNotFoundException nfe) { + loggerClass = Class.forName(Log.class.getPackage().getName() + "." + className); + } + + Constructor constructor = loggerClass.getConstructor(new Class[] { String.class }); + + return (Log) constructor.newInstance(new Object[] { instanceName }); + } catch (ClassNotFoundException cnfe) { + SQLException sqlEx = SQLError.createSQLException("Unable to load class for logger '" + className + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, + exceptionInterceptor); + sqlEx.initCause(cnfe); + + throw sqlEx; + } catch (NoSuchMethodException nsme) { + SQLException sqlEx = SQLError.createSQLException("Logger class does not have a single-arg constructor that takes an instance name", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + sqlEx.initCause(nsme); + + throw sqlEx; + } catch (InstantiationException inse) { + SQLException sqlEx = SQLError.createSQLException("Unable to instantiate logger class '" + className + "', exception in constructor?", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + sqlEx.initCause(inse); + + throw sqlEx; + } catch (InvocationTargetException ite) { + SQLException sqlEx = SQLError.createSQLException("Unable to instantiate logger class '" + className + "', exception in constructor?", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + sqlEx.initCause(ite); + + throw sqlEx; + } catch (IllegalAccessException iae) { + SQLException sqlEx = SQLError.createSQLException("Unable to instantiate logger class '" + className + "', constructor not public", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + sqlEx.initCause(iae); + + throw sqlEx; + } catch (ClassCastException cce) { + SQLException sqlEx = SQLError.createSQLException("Logger class '" + className + "' does not implement the '" + Log.class.getName() + "' interface", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + sqlEx.initCause(cce); + + throw sqlEx; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogUtils.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogUtils.java new file mode 100644 index 0000000..77b40a7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/LogUtils.java @@ -0,0 +1,152 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +import com.mysql.jdbc.Util; +import com.mysql.jdbc.profiler.ProfilerEvent; + +public class LogUtils { + + public static final String CALLER_INFORMATION_NOT_AVAILABLE = "Caller information not available"; + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private static final int LINE_SEPARATOR_LENGTH = LINE_SEPARATOR.length(); + + public static Object expandProfilerEventIfNecessary(Object possibleProfilerEvent) { + + if (possibleProfilerEvent instanceof ProfilerEvent) { + StringBuilder msgBuf = new StringBuilder(); + + ProfilerEvent evt = (ProfilerEvent) possibleProfilerEvent; + + String locationInformation = evt.getEventCreationPointAsString(); + + if (locationInformation == null) { + locationInformation = Util.stackTraceToString(new Throwable()); + } + + msgBuf.append("Profiler Event: ["); + + switch (evt.getEventType()) { + case ProfilerEvent.TYPE_EXECUTE: + msgBuf.append("EXECUTE"); + + break; + + case ProfilerEvent.TYPE_FETCH: + msgBuf.append("FETCH"); + + break; + + case ProfilerEvent.TYPE_OBJECT_CREATION: + msgBuf.append("CONSTRUCT"); + + break; + + case ProfilerEvent.TYPE_PREPARE: + msgBuf.append("PREPARE"); + + break; + + case ProfilerEvent.TYPE_QUERY: + msgBuf.append("QUERY"); + + break; + + case ProfilerEvent.TYPE_WARN: + msgBuf.append("WARN"); + + break; + + case ProfilerEvent.TYPE_SLOW_QUERY: + msgBuf.append("SLOW QUERY"); + + break; + + default: + msgBuf.append("UNKNOWN"); + } + + msgBuf.append("] "); + msgBuf.append(locationInformation); + msgBuf.append(" duration: "); + msgBuf.append(evt.getEventDuration()); + msgBuf.append(" "); + msgBuf.append(evt.getDurationUnits()); + msgBuf.append(", connection-id: "); + msgBuf.append(evt.getConnectionId()); + msgBuf.append(", statement-id: "); + msgBuf.append(evt.getStatementId()); + msgBuf.append(", resultset-id: "); + msgBuf.append(evt.getResultSetId()); + + String evtMessage = evt.getMessage(); + + if (evtMessage != null) { + msgBuf.append(", message: "); + msgBuf.append(evtMessage); + } + + return msgBuf; + } + + return possibleProfilerEvent; + } + + public static String findCallingClassAndMethod(Throwable t) { + String stackTraceAsString = Util.stackTraceToString(t); + + String callingClassAndMethod = CALLER_INFORMATION_NOT_AVAILABLE; + + int endInternalMethods = stackTraceAsString.lastIndexOf("com.mysql.jdbc"); + + if (endInternalMethods != -1) { + int endOfLine = -1; + int compliancePackage = stackTraceAsString.indexOf("com.mysql.jdbc.compliance", endInternalMethods); + + if (compliancePackage != -1) { + endOfLine = compliancePackage - LINE_SEPARATOR_LENGTH; + } else { + endOfLine = stackTraceAsString.indexOf(LINE_SEPARATOR, endInternalMethods); + } + + if (endOfLine != -1) { + int nextEndOfLine = stackTraceAsString.indexOf(LINE_SEPARATOR, endOfLine + LINE_SEPARATOR_LENGTH); + + if (nextEndOfLine != -1) { + callingClassAndMethod = stackTraceAsString.substring(endOfLine + LINE_SEPARATOR_LENGTH, nextEndOfLine); + } else { + callingClassAndMethod = stackTraceAsString.substring(endOfLine + LINE_SEPARATOR_LENGTH); + } + } + } + + if (!callingClassAndMethod.startsWith("\tat ") && !callingClassAndMethod.startsWith("at ")) { + return "at " + callingClassAndMethod; + } + + return callingClassAndMethod; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/NullLogger.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/NullLogger.java new file mode 100644 index 0000000..e926820 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/NullLogger.java @@ -0,0 +1,154 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +/** + * A logger that does nothing. Used before the log is configured via the URL or properties. + */ +public class NullLogger implements Log { + + /** + * Creates a new NullLogger with the given name + * + * @param instanceName + * (ignored) + */ + public NullLogger(String instanceName) { + } + + /** + * @see com.mysql.jdbc.log.Log#isDebugEnabled() + */ + public boolean isDebugEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#isErrorEnabled() + */ + public boolean isErrorEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#isFatalEnabled() + */ + public boolean isFatalEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#isInfoEnabled() + */ + public boolean isInfoEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#isTraceEnabled() + */ + public boolean isTraceEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#isWarnEnabled() + */ + public boolean isWarnEnabled() { + return false; + } + + /** + * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object) + */ + public void logDebug(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object, java.lang.Throwable) + */ + public void logDebug(Object msg, Throwable thrown) { + } + + /** + * @see com.mysql.jdbc.log.Log#logError(java.lang.Object) + */ + public void logError(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logError(java.lang.Object, java.lang.Throwable) + */ + public void logError(Object msg, Throwable thrown) { + } + + /** + * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object) + */ + public void logFatal(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object, java.lang.Throwable) + */ + public void logFatal(Object msg, Throwable thrown) { + } + + /** + * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object) + */ + public void logInfo(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object, java.lang.Throwable) + */ + public void logInfo(Object msg, Throwable thrown) { + } + + /** + * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object) + */ + public void logTrace(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object, java.lang.Throwable) + */ + public void logTrace(Object msg, Throwable thrown) { + } + + /** + * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object) + */ + public void logWarn(Object msg) { + } + + /** + * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object, java.lang.Throwable) + */ + public void logWarn(Object msg, Throwable thrown) { + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Slf4JLogger.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Slf4JLogger.java new file mode 100644 index 0000000..d28288f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/Slf4JLogger.java @@ -0,0 +1,108 @@ +/* + Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Slf4JLogger implements Log { + private Logger log; + + public Slf4JLogger(String name) { + this.log = LoggerFactory.getLogger(name); + } + + public boolean isDebugEnabled() { + return this.log.isDebugEnabled(); + } + + public boolean isErrorEnabled() { + return this.log.isErrorEnabled(); + } + + public boolean isFatalEnabled() { + return this.log.isErrorEnabled(); + } + + public boolean isInfoEnabled() { + return this.log.isInfoEnabled(); + } + + public boolean isTraceEnabled() { + return this.log.isTraceEnabled(); + } + + public boolean isWarnEnabled() { + return this.log.isWarnEnabled(); + } + + public void logDebug(Object msg) { + this.log.debug(msg.toString()); + } + + public void logDebug(Object msg, Throwable thrown) { + this.log.debug(msg.toString(), thrown); + } + + public void logError(Object msg) { + this.log.error(msg.toString()); + } + + public void logError(Object msg, Throwable thrown) { + this.log.error(msg.toString(), thrown); + } + + public void logFatal(Object msg) { + this.log.error(msg.toString()); + } + + public void logFatal(Object msg, Throwable thrown) { + this.log.error(msg.toString(), thrown); + } + + public void logInfo(Object msg) { + this.log.info(msg.toString()); + } + + public void logInfo(Object msg, Throwable thrown) { + this.log.info(msg.toString(), thrown); + } + + public void logTrace(Object msg) { + this.log.trace(msg.toString()); + } + + public void logTrace(Object msg, Throwable thrown) { + this.log.trace(msg.toString(), thrown); + } + + public void logWarn(Object msg) { + this.log.warn(msg.toString()); + } + + public void logWarn(Object msg, Throwable thrown) { + this.log.warn(msg.toString(), thrown); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/StandardLogger.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/StandardLogger.java new file mode 100644 index 0000000..0514c5d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/log/StandardLogger.java @@ -0,0 +1,324 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.log; + +import java.util.Date; + +import com.mysql.jdbc.Util; +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * Provides logging facilities for those platforms that don't have built-in facilities. Simply logs messages to STDERR. + */ +public class StandardLogger implements Log { + private static final int FATAL = 0; + + private static final int ERROR = 1; + + private static final int WARN = 2; + + private static final int INFO = 3; + + private static final int DEBUG = 4; + + private static final int TRACE = 5; + + private static StringBuffer bufferedLog = null; + + private boolean logLocationInfo = true; + + /** + * Creates a new StandardLogger object. + * + * @param name + * the name of the configuration to use -- ignored + */ + public StandardLogger(String name) { + this(name, false); + } + + /** + * @param name + * @param logLocationInfo + */ + public StandardLogger(String name, boolean logLocationInfo) { + this.logLocationInfo = logLocationInfo; + } + + public static void startLoggingToBuffer() { + bufferedLog = new StringBuffer(); + } + + public static void dropBuffer() { + bufferedLog = null; + } + + public static Appendable getBuffer() { + return bufferedLog; + } + + /** + * @see com.mysql.jdbc.log.Log#isDebugEnabled() + */ + public boolean isDebugEnabled() { + return true; + } + + /** + * @see com.mysql.jdbc.log.Log#isErrorEnabled() + */ + public boolean isErrorEnabled() { + return true; + } + + /** + * @see com.mysql.jdbc.log.Log#isFatalEnabled() + */ + public boolean isFatalEnabled() { + return true; + } + + /** + * @see com.mysql.jdbc.log.Log#isInfoEnabled() + */ + public boolean isInfoEnabled() { + return true; + } + + /** + * @see com.mysql.jdbc.log.Log#isTraceEnabled() + */ + public boolean isTraceEnabled() { + return true; + } + + /** + * @see com.mysql.jdbc.log.Log#isWarnEnabled() + */ + public boolean isWarnEnabled() { + return true; + } + + /** + * Logs the given message instance using the 'debug' level + * + * @param message + * the message to log + */ + public void logDebug(Object message) { + logInternal(DEBUG, message, null); + } + + /** + * Logs the given message and Throwable at the 'debug' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logDebug(Object message, Throwable exception) { + logInternal(DEBUG, message, exception); + } + + /** + * Logs the given message instance using the 'error' level + * + * @param message + * the message to log + */ + public void logError(Object message) { + logInternal(ERROR, message, null); + } + + /** + * Logs the given message and Throwable at the 'error' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logError(Object message, Throwable exception) { + logInternal(ERROR, message, exception); + } + + /** + * Logs the given message instance using the 'fatal' level + * + * @param message + * the message to log + */ + public void logFatal(Object message) { + logInternal(FATAL, message, null); + } + + /** + * Logs the given message and Throwable at the 'fatal' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logFatal(Object message, Throwable exception) { + logInternal(FATAL, message, exception); + } + + /** + * Logs the given message instance using the 'info' level + * + * @param message + * the message to log + */ + public void logInfo(Object message) { + logInternal(INFO, message, null); + } + + /** + * Logs the given message and Throwable at the 'info' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logInfo(Object message, Throwable exception) { + logInternal(INFO, message, exception); + } + + /** + * Logs the given message instance using the 'trace' level + * + * @param message + * the message to log + */ + public void logTrace(Object message) { + logInternal(TRACE, message, null); + } + + /** + * Logs the given message and Throwable at the 'trace' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logTrace(Object message, Throwable exception) { + logInternal(TRACE, message, exception); + } + + /** + * Logs the given message instance using the 'warn' level + * + * @param message + * the message to log + */ + public void logWarn(Object message) { + logInternal(WARN, message, null); + } + + /** + * Logs the given message and Throwable at the 'warn' level. + * + * @param message + * the message to log + * @param exception + * the throwable to log (may be null) + */ + public void logWarn(Object message, Throwable exception) { + logInternal(WARN, message, exception); + } + + protected void logInternal(int level, Object msg, Throwable exception) { + StringBuilder msgBuf = new StringBuilder(); + msgBuf.append(new Date().toString()); + msgBuf.append(" "); + + switch (level) { + case FATAL: + msgBuf.append("FATAL: "); + + break; + + case ERROR: + msgBuf.append("ERROR: "); + + break; + + case WARN: + msgBuf.append("WARN: "); + + break; + + case INFO: + msgBuf.append("INFO: "); + + break; + + case DEBUG: + msgBuf.append("DEBUG: "); + + break; + + case TRACE: + msgBuf.append("TRACE: "); + + break; + } + + if (msg instanceof ProfilerEvent) { + msgBuf.append(LogUtils.expandProfilerEventIfNecessary(msg)); + + } else { + if (this.logLocationInfo && level != TRACE) { + Throwable locationException = new Throwable(); + msgBuf.append(LogUtils.findCallingClassAndMethod(locationException)); + msgBuf.append(" "); + } + + if (msg != null) { + msgBuf.append(String.valueOf(msg)); + } + } + + if (exception != null) { + msgBuf.append("\n"); + msgBuf.append("\n"); + msgBuf.append("EXCEPTION STACK TRACE:"); + msgBuf.append("\n"); + msgBuf.append("\n"); + msgBuf.append(Util.stackTraceToString(exception)); + } + + String messageAsString = msgBuf.toString(); + + System.err.println(messageAsString); + + if (bufferedLog != null) { + bufferedLog.append(messageAsString); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/LoggingProfilerEventHandler.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/LoggingProfilerEventHandler.java new file mode 100644 index 0000000..5b05817 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/LoggingProfilerEventHandler.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.profiler; + +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.log.Log; + +/** + * A profile event handler that just logs to the standard logging mechanism of the JDBC driver. + */ +public class LoggingProfilerEventHandler implements ProfilerEventHandler { + private Log log; + + public LoggingProfilerEventHandler() { + } + + public void consumeEvent(ProfilerEvent evt) { + if (evt.eventType == ProfilerEvent.TYPE_WARN) { + this.log.logWarn(evt); + } else { + this.log.logInfo(evt); + } + } + + public void destroy() { + this.log = null; + } + + public void init(Connection conn, Properties props) throws SQLException { + this.log = conn.getLog(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEvent.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEvent.java new file mode 100644 index 0000000..0dc5e1e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEvent.java @@ -0,0 +1,507 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.profiler; + +import java.util.Date; + +import com.mysql.jdbc.StringUtils; + +public class ProfilerEvent { + + /** + * A Profiler warning event + */ + public static final byte TYPE_WARN = 0; + + /** + * Profiler creating object type event + */ + public static final byte TYPE_OBJECT_CREATION = 1; + + /** + * Profiler event for prepared statements being prepared + */ + public static final byte TYPE_PREPARE = 2; + + /** + * Profiler event for a query being executed + */ + public static final byte TYPE_QUERY = 3; + + /** + * Profiler event for prepared statements being executed + */ + public static final byte TYPE_EXECUTE = 4; + + /** + * Profiler event for result sets being retrieved + */ + public static final byte TYPE_FETCH = 5; + + /** + * Profiler event for slow query + */ + public static final byte TYPE_SLOW_QUERY = 6; + + /** + * Type of event + */ + protected byte eventType; + + /** + * Associated connection (-1 for none) + */ + protected long connectionId; + + /** + * Associated statement (-1 for none) + */ + protected int statementId; + + /** + * Associated result set (-1 for none) + */ + protected int resultSetId; + + /** + * When was the event created? + */ + protected long eventCreationTime; + + /** + * How long did the event last? + */ + protected long eventDuration; + + /** + * What units was the duration measured in? + */ + protected String durationUnits; + + /** + * The hostname the event occurred on (as an index into a dictionary, used + * by 'remote' profilers for efficiency)? + */ + protected int hostNameIndex; + + /** + * The hostname the event occurred on + */ + protected String hostName; + + /** + * The catalog the event occurred on (as an index into a dictionary, used by + * 'remote' profilers for efficiency)? + */ + protected int catalogIndex; + + /** + * The catalog the event occurred on + */ + protected String catalog; + + /** + * Where was the event created (as an index into a dictionary, used by + * 'remote' profilers for efficiency)? + */ + protected int eventCreationPointIndex; + + /** + * Where was the event created (as a string description of the + * eventCreationPoint)? + */ + protected String eventCreationPointDesc; + + /** + * Optional event message + */ + protected String message; + + /** + * Creates a new profiler event + * + * @param eventType + * the event type (from the constants TYPE_????) + * @param hostName + * the hostname where the event occurs + * @param catalog + * the catalog in use + * @param connectionId + * the connection id (-1 if N/A) + * @param statementId + * the statement id (-1 if N/A) + * @param resultSetId + * the result set id (-1 if N/A) + * @param eventCreationTime + * when was the event created? + * @param eventDurationMillis + * how long did the event last? + * @param eventCreationPointDesc + * event creation point as a string + * @param eventCreationPoint + * event creation point as a Throwable + * @param message + * optional message + */ + public ProfilerEvent(byte eventType, String hostName, String catalog, long connectionId, int statementId, int resultSetId, long eventCreationTime, + long eventDuration, String durationUnits, String eventCreationPointDesc, String eventCreationPoint, String message) { + this.eventType = eventType; + this.connectionId = connectionId; + this.statementId = statementId; + this.resultSetId = resultSetId; + this.eventCreationTime = eventCreationTime; + this.eventDuration = eventDuration; + this.durationUnits = durationUnits; + this.eventCreationPointDesc = eventCreationPointDesc; + this.message = message; + } + + /** + * Returns the description of when this event was created. + * + * @return a description of when this event was created. + */ + public String getEventCreationPointAsString() { + return this.eventCreationPointDesc; + } + + /** + * Returns a representation of this event as a String. + * + * @return a String representation of this event. + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(32); + + switch (this.eventType) { + case TYPE_EXECUTE: + buf.append("EXECUTE"); + break; + + case TYPE_FETCH: + buf.append("FETCH"); + break; + + case TYPE_OBJECT_CREATION: + buf.append("CONSTRUCT"); + break; + + case TYPE_PREPARE: + buf.append("PREPARE"); + break; + + case TYPE_QUERY: + buf.append("QUERY"); + break; + + case TYPE_WARN: + buf.append("WARN"); + break; + case TYPE_SLOW_QUERY: + buf.append("SLOW QUERY"); + break; + default: + buf.append("UNKNOWN"); + } + + buf.append(" created: "); + buf.append(new Date(this.eventCreationTime)); + buf.append(" duration: "); + buf.append(this.eventDuration); + buf.append(" connection: "); + buf.append(this.connectionId); + buf.append(" statement: "); + buf.append(this.statementId); + buf.append(" resultset: "); + buf.append(this.resultSetId); + + if (this.message != null) { + buf.append(" message: "); + buf.append(this.message); + + } + + if (this.eventCreationPointDesc != null) { + buf.append("\n\nEvent Created at:\n"); + buf.append(this.eventCreationPointDesc); + } + + return buf.toString(); + } + + /** + * Unpacks a binary representation of this event. + * + * @param buf + * the binary representation of this event + * @return the unpacked Event + * @throws Exception + * if an error occurs while unpacking the event + */ + public static ProfilerEvent unpack(byte[] buf) throws Exception { + int pos = 0; + + byte eventType = buf[pos++]; + long connectionId = readInt(buf, pos); + pos += 8; + int statementId = readInt(buf, pos); + pos += 4; + int resultSetId = readInt(buf, pos); + pos += 4; + long eventCreationTime = readLong(buf, pos); + pos += 8; + long eventDuration = readLong(buf, pos); + pos += 4; + + byte[] eventDurationUnits = readBytes(buf, pos); + pos += 4; + + if (eventDurationUnits != null) { + pos += eventDurationUnits.length; + } + + readInt(buf, pos); + pos += 4; + byte[] eventCreationAsBytes = readBytes(buf, pos); + pos += 4; + + if (eventCreationAsBytes != null) { + pos += eventCreationAsBytes.length; + } + + byte[] message = readBytes(buf, pos); + pos += 4; + + if (message != null) { + pos += message.length; + } + + return new ProfilerEvent(eventType, "", "", connectionId, statementId, resultSetId, eventCreationTime, eventDuration, + StringUtils.toString(eventDurationUnits, "ISO8859_1"), StringUtils.toString(eventCreationAsBytes, "ISO8859_1"), null, + StringUtils.toString(message, "ISO8859_1")); + } + + /** + * Creates a binary representation of this event. + * + * @return a binary representation of this event + * @throws Exception + * if an error occurs while packing this event. + */ + public byte[] pack() throws Exception { + + int len = 1 + 4 + 4 + 4 + 8 + 4 + 4; + + byte[] eventCreationAsBytes = null; + + getEventCreationPointAsString(); + + if (this.eventCreationPointDesc != null) { + eventCreationAsBytes = StringUtils.getBytes(this.eventCreationPointDesc, "ISO8859_1"); + len += (4 + eventCreationAsBytes.length); + } else { + len += 4; + } + + byte[] messageAsBytes = null; + + if (this.message != null) { + messageAsBytes = StringUtils.getBytes(this.message, "ISO8859_1"); + len += (4 + messageAsBytes.length); + } else { + len += 4; + } + + byte[] durationUnitsAsBytes = null; + + if (this.durationUnits != null) { + durationUnitsAsBytes = StringUtils.getBytes(this.durationUnits, "ISO8859_1"); + len += (4 + durationUnitsAsBytes.length); + } else { + len += 4; + durationUnitsAsBytes = StringUtils.getBytes("", "ISO8859_1"); + } + + byte[] buf = new byte[len]; + + int pos = 0; + + buf[pos++] = this.eventType; + pos = writeLong(this.connectionId, buf, pos); + pos = writeInt(this.statementId, buf, pos); + pos = writeInt(this.resultSetId, buf, pos); + pos = writeLong(this.eventCreationTime, buf, pos); + pos = writeLong(this.eventDuration, buf, pos); + pos = writeBytes(durationUnitsAsBytes, buf, pos); + pos = writeInt(this.eventCreationPointIndex, buf, pos); + + if (eventCreationAsBytes != null) { + pos = writeBytes(eventCreationAsBytes, buf, pos); + } else { + pos = writeInt(0, buf, pos); + } + + if (messageAsBytes != null) { + pos = writeBytes(messageAsBytes, buf, pos); + } else { + pos = writeInt(0, buf, pos); + } + + return buf; + } + + private static int writeInt(int i, byte[] buf, int pos) { + + buf[pos++] = (byte) (i & 0xff); + buf[pos++] = (byte) (i >>> 8); + buf[pos++] = (byte) (i >>> 16); + buf[pos++] = (byte) (i >>> 24); + + return pos; + } + + private static int writeLong(long l, byte[] buf, int pos) { + buf[pos++] = (byte) (l & 0xff); + buf[pos++] = (byte) (l >>> 8); + buf[pos++] = (byte) (l >>> 16); + buf[pos++] = (byte) (l >>> 24); + buf[pos++] = (byte) (l >>> 32); + buf[pos++] = (byte) (l >>> 40); + buf[pos++] = (byte) (l >>> 48); + buf[pos++] = (byte) (l >>> 56); + + return pos; + } + + private static int writeBytes(byte[] msg, byte[] buf, int pos) { + pos = writeInt(msg.length, buf, pos); + + System.arraycopy(msg, 0, buf, pos, msg.length); + + return pos + msg.length; + } + + private static int readInt(byte[] buf, int pos) { + return (buf[pos++] & 0xff) | ((buf[pos++] & 0xff) << 8) | ((buf[pos++] & 0xff) << 16) | ((buf[pos++] & 0xff) << 24); + + } + + private static long readLong(byte[] buf, int pos) { + return (buf[pos++] & 0xff) | ((long) (buf[pos++] & 0xff) << 8) | ((long) (buf[pos++] & 0xff) << 16) | ((long) (buf[pos++] & 0xff) << 24) + | ((long) (buf[pos++] & 0xff) << 32) | ((long) (buf[pos++] & 0xff) << 40) | ((long) (buf[pos++] & 0xff) << 48) + | ((long) (buf[pos++] & 0xff) << 56); + } + + private static byte[] readBytes(byte[] buf, int pos) { + int length = readInt(buf, pos); + + pos += 4; + + byte[] msg = new byte[length]; + System.arraycopy(buf, pos, msg, 0, length); + + return msg; + } + + /** + * Returns the catalog in use + * + * @return the catalog in use + */ + public String getCatalog() { + return this.catalog; + } + + /** + * Returns the id of the connection in use when this event was created. + * + * @return the connection in use + */ + public long getConnectionId() { + return this.connectionId; + } + + /** + * Returns the time (in System.currentTimeMillis() form) when this event was + * created + * + * @return the time this event was created + */ + public long getEventCreationTime() { + return this.eventCreationTime; + } + + /** + * Returns the duration of the event in milliseconds + * + * @return the duration of the event in milliseconds + */ + public long getEventDuration() { + return this.eventDuration; + } + + /** + * Returns the units for getEventDuration() + */ + public String getDurationUnits() { + return this.durationUnits; + } + + /** + * Returns the event type flag + * + * @return the event type flag + */ + public byte getEventType() { + return this.eventType; + } + + /** + * Returns the id of the result set in use when this event was created. + * + * @return the result set in use + */ + public int getResultSetId() { + return this.resultSetId; + } + + /** + * Returns the id of the statement in use when this event was created. + * + * @return the statement in use + */ + public int getStatementId() { + return this.statementId; + } + + /** + * Returns the optional message for this event + * + * @return the message stored in this event + */ + public String getMessage() { + return this.message; + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEventHandler.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEventHandler.java new file mode 100644 index 0000000..619558e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/profiler/ProfilerEventHandler.java @@ -0,0 +1,31 @@ +/* + Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.profiler; + +import com.mysql.jdbc.Extension; + +public interface ProfilerEventHandler extends Extension { + + public void consumeEvent(ProfilerEvent evt); +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/Base64Decoder.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/Base64Decoder.java new file mode 100644 index 0000000..3e89eb9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/Base64Decoder.java @@ -0,0 +1,91 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +/** + * This decoder implements standard Base64 decoding except it allows and silently ignores non-base64 input characters (spaces, line breaks etc) + * + * Note: Java 6+ provide standard decoders + */ +public class Base64Decoder { + + /* + * -1 means non-base64 character + * -2 means padding + */ + private static byte[] decoderMap = new byte[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 }; + + public static class IntWrapper { + public int value; + + public IntWrapper(int value) { + this.value = value; + } + } + + private static byte getNextValidByte(byte[] in, IntWrapper pos, int maxPos) { + while (pos.value <= maxPos) { + if (in[pos.value] >= 0 && decoderMap[in[pos.value]] >= 0) { + return in[pos.value++]; + } + pos.value++; + } + // padding if reached max position + return '='; + } + + public static byte[] decode(byte[] in, int pos, int length) { + IntWrapper offset = new Base64Decoder.IntWrapper(pos); + byte[] sestet = new byte[4]; + + int outLen = (length * 3) / 4; // over-estimated if non-base64 characters present + byte[] octet = new byte[outLen]; + int octetId = 0; + + int maxPos = offset.value + length - 1; + while (offset.value <= maxPos) { + sestet[0] = decoderMap[getNextValidByte(in, offset, maxPos)]; + sestet[1] = decoderMap[getNextValidByte(in, offset, maxPos)]; + sestet[2] = decoderMap[getNextValidByte(in, offset, maxPos)]; + sestet[3] = decoderMap[getNextValidByte(in, offset, maxPos)]; + + if (sestet[1] != -2) { + octet[octetId++] = (byte) ((sestet[0] << 2) | (sestet[1] >>> 4)); + } + if (sestet[2] != -2) { + octet[octetId++] = (byte) (((sestet[1] & 0xf) << 4) | (sestet[2] >>> 2)); + } + if (sestet[3] != -2) { + octet[octetId++] = (byte) (((sestet[2] & 3) << 6) | sestet[3]); + } + } + // return real-length value + byte[] out = new byte[octetId]; + System.arraycopy(octet, 0, out, 0, octetId); + return out; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/BaseBugReport.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/BaseBugReport.java new file mode 100644 index 0000000..607b2ff --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/BaseBugReport.java @@ -0,0 +1,248 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.jdbc.Driver; + +/** + * Base class to help file bug reports for Connector/J. + * + *

    + * MySQL AB, 2008 Sun Microsystems, 2009 Oracle Corporation + * + *

      + * really + *
    + * appreciates repeatable testcases when reporting bugs, so we're giving you this class to make that job a bit easier (and standarized). + * + *

    + * To create a testcase, create a class that inherits from this class (com.mysql.jdbc.util.BaseBugReport), and override the methods 'setUp', 'tearDown' and + * 'runTest'. + * + *

    + * In the 'setUp' method, create code that creates your tables, and populates them with any data needed to demonstrate the bug. + * + *

    + * In the 'runTest' method, create code that demonstrates the bug using the tables and data you created in the 'setUp' method. + * + *

    + * In the 'tearDown' method, drop any tables you created in the 'setUp' method. + * + *

    + * In any of the above three methods, you should use one of the variants of the 'getConnection' method to create a JDBC connection to MySQL, which will use the + * default JDBC URL of 'jdbc:mysql:///test'. + * + *

    + * If you need to use a JDBC URL that is different than 'jdbc:mysql:///test', then override the method 'getUrl' as well. + * + *

    + * Use the 'assertTrue' methods to create conditions that must be met in your testcase demonstrating the behavior you are expecting (vs. the behavior you are + * observing, which is why you are most likely filing a bug report). + * + *

    + * Finally, create a 'main' method that creates a new instance of your testcase, and calls the 'run' method: + * + *

    + * + *

    + * public static void main(String[] args) throws Exception {
    + *     new MyBugReport().run();
    + * }
    + * 
    + * + *

    + * When filing a potential bug with MySQL Connector/J at http://bugs.mysql.com/ or on the bugs mailing list, please include the code that you have just written + * using this class. + */ +public abstract class BaseBugReport { + + private Connection conn; + + private Driver driver; + + /** + * Constructor for this BugReport, sets up JDBC driver used to create + * connections. + */ + public BaseBugReport() { + try { + this.driver = new Driver(); + } catch (SQLException ex) { + throw new RuntimeException(ex.toString()); + } + } + + /** + * Override this method with code that sets up the testcase for + * demonstrating your bug (creating tables, populating data, etc). + * + * @throws Exception + * if an error occurs during the 'setUp' phase. + */ + public abstract void setUp() throws Exception; + + /** + * Override this method with code that cleans up anything created in the + * setUp() method. + * + * @throws Exception + * if an error occurs during the 'tearDown' phase. + */ + public abstract void tearDown() throws Exception; + + /** + * Override this method with code that demonstrates the bug. This method + * will be called after setUp(), and before tearDown(). + * + * @throws Exception + * if an error occurs during your test run. + */ + public abstract void runTest() throws Exception; + + /** + * Runs the testcase by calling the setUp(), runTest() and tearDown() + * methods. The tearDown() method is run regardless of any errors occuring + * in the other methods. + * + * @throws Exception + * if an error occurs in any of the aforementioned methods. + */ + public final void run() throws Exception { + try { + setUp(); + runTest(); + + } finally { + tearDown(); + } + } + + /** + * Throws an exception with the given message if condition evalutates to + * 'false'. + * + * @param message + * the message to use in the exception + * @param condition + * the condition to test for + * @throws Exception + * if !condition + */ + protected final void assertTrue(String message, boolean condition) throws Exception { + if (!condition) { + throw new Exception("Assertion failed: " + message); + } + } + + /** + * Throws an exception if condition evalutates to 'false'. + * + * @param condition + * the condition to test for + * @throws Exception + * if !condition + */ + protected final void assertTrue(boolean condition) throws Exception { + assertTrue("(no message given)", condition); + } + + /** + * Provides the JDBC URL to use to demonstrate the bug. The + * java.sql.Connection that you use to demonstrate this bug will be provided + * by the getConnection() method using this URL. + * + * The default value is 'jdbc:mysql:///test' + */ + public String getUrl() { + return "jdbc:mysql:///test"; + } + + /** + * Provides a connection to the JDBC URL specified in getUrl(). + * + * If a connection already exists, that connection is returned. Otherwise a + * new connection is created. + * + * @return a connection to the JDBC URL specified in getUrl(). + * + * @throws SQLException + * if an error is caused while creating the connection. + */ + public final synchronized Connection getConnection() throws SQLException { + if (this.conn == null || this.conn.isClosed()) { + this.conn = getNewConnection(); + } + + return this.conn; + } + + /** + * Use this if you need to get a new connection for your bug report (i.e. + * there's more than one connection involved). + * + * @return a new connection to the JDBC URL specified in getUrl(). + * + * @throws SQLException + * if an error is caused while creating the connection. + */ + public final synchronized Connection getNewConnection() throws SQLException { + return getConnection(getUrl()); + } + + /** + * Returns a connection using the given URL. + * + * @param url + * the JDBC URL to use + * @return a new java.sql.Connection to the JDBC URL. + * @throws SQLException + * if an error occurs getting the connection. + */ + public final synchronized Connection getConnection(String url) throws SQLException { + return getConnection(url, null); + } + + /** + * Returns a connection using the given URL and properties. + * + * @param url + * the JDBC URL to use + * @param props + * the JDBC properties to use + * @return a new java.sql.Connection to the JDBC URL. + * @throws SQLException + * if an error occurs getting the connection. + */ + public final synchronized Connection getConnection(String url, Properties props) throws SQLException { + + // Don't follow this example in your own code + // This is to bypass the java.sql.DriverManager + + return this.driver.connect(url, props); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java new file mode 100644 index 0000000..734274c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java @@ -0,0 +1,36 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import com.mysql.jdbc.SQLError; + +/** + * Creates XML file describing mapping of MySQL error #'s to SQL92 and X/Open states. + */ +public class ErrorMappingsDocGenerator { + + public static void main(String[] args) throws Exception { + SQLError.dumpSqlStatesMappingsAsXml(); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/LRUCache.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/LRUCache.java new file mode 100644 index 0000000..3264992 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/LRUCache.java @@ -0,0 +1,47 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +public class LRUCache extends LinkedHashMap { + private static final long serialVersionUID = 1L; + protected int maxElements; + + public LRUCache(int maxSize) { + super(maxSize, 0.75F, true); + this.maxElements = maxSize; + } + + /* + * (non-Javadoc) + * + * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) + */ + @Override + protected boolean removeEldestEntry(Entry eldest) { + return (size() > this.maxElements); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/PropertiesDocGenerator.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/PropertiesDocGenerator.java new file mode 100644 index 0000000..632bf2e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/PropertiesDocGenerator.java @@ -0,0 +1,40 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.sql.SQLException; + +import com.mysql.jdbc.ConnectionPropertiesImpl; + +/** + * Creates docbook table of connection properties from ConnectionProperties class. + */ +public class PropertiesDocGenerator extends ConnectionPropertiesImpl { + + static final long serialVersionUID = -4869689139143855383L; + + public static void main(String[] args) throws SQLException { + System.out.println(new PropertiesDocGenerator().exposeAsXml()); + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ReadAheadInputStream.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ReadAheadInputStream.java new file mode 100644 index 0000000..e8d4f1c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ReadAheadInputStream.java @@ -0,0 +1,295 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import com.mysql.jdbc.log.Log; + +/** + * A non-blocking buffered input stream. Reads more if it can, won't block to fill the buffer, only blocks to satisfy a request of read(byte[]) + */ +public class ReadAheadInputStream extends InputStream { + + private final static int DEFAULT_BUFFER_SIZE = 4096; + + private InputStream underlyingStream; + + private byte buf[]; + + protected int endOfCurrentData; + + protected int currentPosition; + + protected boolean doDebug = false; + + protected Log log; + + private void fill(int readAtLeastTheseManyBytes) throws IOException { + checkClosed(); + + this.currentPosition = 0; /* no mark: throw away the buffer */ + + this.endOfCurrentData = this.currentPosition; + + // Read at least as many bytes as the caller wants, but don't block to fill the whole buffer (like java.io.BufferdInputStream does) + + int bytesToRead = Math.min(this.buf.length - this.currentPosition, readAtLeastTheseManyBytes); + + int bytesAvailable = this.underlyingStream.available(); + + if (bytesAvailable > bytesToRead) { + + // Great, there's more available, let's grab those bytes too! (read-ahead) + + bytesToRead = Math.min(this.buf.length - this.currentPosition, bytesAvailable); + } + + if (this.doDebug) { + StringBuilder debugBuf = new StringBuilder(); + debugBuf.append(" ReadAheadInputStream.fill("); + debugBuf.append(readAtLeastTheseManyBytes); + debugBuf.append("), buffer_size="); + debugBuf.append(this.buf.length); + debugBuf.append(", current_position="); + debugBuf.append(this.currentPosition); + debugBuf.append(", need to read "); + debugBuf.append(Math.min(this.buf.length - this.currentPosition, readAtLeastTheseManyBytes)); + debugBuf.append(" bytes to fill request,"); + + if (bytesAvailable > 0) { + debugBuf.append(" underlying InputStream reports "); + debugBuf.append(bytesAvailable); + + debugBuf.append(" total bytes available,"); + } + + debugBuf.append(" attempting to read "); + debugBuf.append(bytesToRead); + debugBuf.append(" bytes."); + + if (this.log != null) { + this.log.logTrace(debugBuf.toString()); + } else { + System.err.println(debugBuf.toString()); + } + } + + int n = this.underlyingStream.read(this.buf, this.currentPosition, bytesToRead); + + if (n > 0) { + this.endOfCurrentData = n + this.currentPosition; + } + } + + private int readFromUnderlyingStreamIfNecessary(byte[] b, int off, int len) throws IOException { + checkClosed(); + + int avail = this.endOfCurrentData - this.currentPosition; + + if (this.doDebug) { + StringBuilder debugBuf = new StringBuilder(); + debugBuf.append("ReadAheadInputStream.readIfNecessary("); + debugBuf.append(Arrays.toString(b)); + debugBuf.append(","); + debugBuf.append(off); + debugBuf.append(","); + debugBuf.append(len); + debugBuf.append(")"); + + if (avail <= 0) { + debugBuf.append(" not all data available in buffer, must read from stream"); + + if (len >= this.buf.length) { + debugBuf.append(", amount requested > buffer, returning direct read() from stream"); + } + } + + if (this.log != null) { + this.log.logTrace(debugBuf.toString()); + } else { + System.err.println(debugBuf.toString()); + } + } + + if (avail <= 0) { + + if (len >= this.buf.length) { + return this.underlyingStream.read(b, off, len); + } + + fill(len); + + avail = this.endOfCurrentData - this.currentPosition; + + if (avail <= 0) { + return -1; + } + } + + int bytesActuallyRead = (avail < len) ? avail : len; + + System.arraycopy(this.buf, this.currentPosition, b, off, bytesActuallyRead); + + this.currentPosition += bytesActuallyRead; + + return bytesActuallyRead; + } + + @Override + public synchronized int read(byte b[], int off, int len) throws IOException { + checkClosed(); // Check for closed stream + if ((off | len | (off + len) | (b.length - (off + len))) < 0) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int totalBytesRead = 0; + + while (true) { + int bytesReadThisRound = readFromUnderlyingStreamIfNecessary(b, off + totalBytesRead, len - totalBytesRead); + + // end-of-stream? + if (bytesReadThisRound <= 0) { + if (totalBytesRead == 0) { + totalBytesRead = bytesReadThisRound; + } + + break; + } + + totalBytesRead += bytesReadThisRound; + + // Read _at_least_ enough bytes + if (totalBytesRead >= len) { + break; + } + + // Nothing to read? + if (this.underlyingStream.available() <= 0) { + break; + } + } + + return totalBytesRead; + } + + @Override + public int read() throws IOException { + checkClosed(); + + if (this.currentPosition >= this.endOfCurrentData) { + fill(1); + if (this.currentPosition >= this.endOfCurrentData) { + return -1; + } + } + + return this.buf[this.currentPosition++] & 0xff; + } + + @Override + public int available() throws IOException { + checkClosed(); + + return this.underlyingStream.available() + (this.endOfCurrentData - this.currentPosition); + } + + private void checkClosed() throws IOException { + + if (this.buf == null) { + throw new IOException("Stream closed"); + } + } + + public ReadAheadInputStream(InputStream toBuffer, boolean debug, Log logTo) { + this(toBuffer, DEFAULT_BUFFER_SIZE, debug, logTo); + } + + public ReadAheadInputStream(InputStream toBuffer, int bufferSize, boolean debug, Log logTo) { + this.underlyingStream = toBuffer; + this.buf = new byte[bufferSize]; + this.doDebug = debug; + this.log = logTo; + } + + /* + * (non-Javadoc) + * + * @see java.io.Closeable#close() + */ + @Override + public void close() throws IOException { + if (this.underlyingStream != null) { + try { + this.underlyingStream.close(); + } finally { + this.underlyingStream = null; + this.buf = null; + this.log = null; + } + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#markSupported() + */ + @Override + public boolean markSupported() { + return false; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#skip(long) + */ + @Override + public long skip(long n) throws IOException { + checkClosed(); + if (n <= 0) { + return 0; + } + + long bytesAvailInBuffer = this.endOfCurrentData - this.currentPosition; + + if (bytesAvailInBuffer <= 0) { + + fill((int) n); + bytesAvailInBuffer = this.endOfCurrentData - this.currentPosition; + if (bytesAvailInBuffer <= 0) { + return 0; + } + } + + long bytesSkipped = (bytesAvailInBuffer < n) ? bytesAvailInBuffer : n; + this.currentPosition += bytesSkipped; + return bytesSkipped; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ResultSetUtil.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ResultSetUtil.java new file mode 100644 index 0000000..42cf13b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ResultSetUtil.java @@ -0,0 +1,84 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +/** + * Utilities for dealing with result sets (used in testcases and profiler). + */ +public class ResultSetUtil { + + public static StringBuilder appendResultSetSlashGStyle(StringBuilder appendTo, ResultSet rs) throws SQLException { + ResultSetMetaData rsmd = rs.getMetaData(); + + int numFields = rsmd.getColumnCount(); + int maxWidth = 0; + + String[] fieldNames = new String[numFields]; + + for (int i = 0; i < numFields; i++) { + fieldNames[i] = rsmd.getColumnLabel(i + 1); + + if (fieldNames[i].length() > maxWidth) { + maxWidth = fieldNames[i].length(); + } + } + + int rowCount = 1; + + while (rs.next()) { + appendTo.append("*************************** "); + appendTo.append(rowCount++); + appendTo.append(". row ***************************\n"); + + for (int i = 0; i < numFields; i++) { + int leftPad = maxWidth - fieldNames[i].length(); + + for (int j = 0; j < leftPad; j++) { + appendTo.append(" "); + } + + appendTo.append(fieldNames[i]); + appendTo.append(": "); + + String stringVal = rs.getString(i + 1); + + if (stringVal != null) { + appendTo.append(stringVal); + } else { + appendTo.append("NULL"); + } + + appendTo.append("\n"); + } + + appendTo.append("\n"); + } + + return appendTo; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ServerController.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ServerController.java new file mode 100644 index 0000000..fcf5b2f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/ServerController.java @@ -0,0 +1,336 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.Properties; + +import com.mysql.jdbc.StringUtils; + +/** + * Controls a MySQL server using Java RunTime methods + */ +public class ServerController { + + /** + * Where is the server installed? + */ + public static final String BASEDIR_KEY = "basedir"; + + /** + * Where are the databases installed? + */ + public static final String DATADIR_KEY = "datadir"; + + /** + * Where is the config file located? + */ + + public static final String DEFAULTS_FILE_KEY = "defaults-file"; + + /** + * What is the name of the executable to run? + */ + + public static final String EXECUTABLE_NAME_KEY = "executable"; + + /** + * What is the path to the mysql server executable (if not standard?) + */ + + public static final String EXECUTABLE_PATH_KEY = "executablePath"; + + /** + * The default executable to run + */ + + /** + * The process representing the MySQL server + */ + private Process serverProcess = null; + + /** + * The list of properties for this server + */ + private Properties serverProps = null; + + /** + * The system properties + */ + private Properties systemProps = null; + + /** + * Creates a ServerController with the directory for the MySQL server. + * + * The 'datadir' is set to the same directory. + * + * @param baseDir + * the base directory for the MySQL server. + */ + public ServerController(String baseDir) { + setBaseDir(baseDir); + } + + /** + * Creates a server controller for the MySQL server with the given basedir + * and datadir. + * + * @param basedir + * the basedir to use when starting MySQL. + * @param datadir + * the datadir to use when starting MySQL. + */ + public ServerController(String basedir, String datadir) { + } + + /** + * Sets the basedir to use when starting MySQL. + * + * @param baseDir + * the basedir to use when starting MySQL. + */ + public void setBaseDir(String baseDir) { + getServerProps().setProperty(BASEDIR_KEY, baseDir); + } + + /** + * Sets the data to use when starting MySQL. + * + * @param dataDir + * the basedir to use when starting MySQL. + */ + public void setDataDir(String dataDir) { + getServerProps().setProperty(DATADIR_KEY, dataDir); + } + + /** + * Starts the server, returning a java.lang.Process instance that represents + * the mysql server. + * + * @return Process a java.lang.Process instance representing the mysql + * server process. + * @throws IOException + * if an error occurs while starting the mysql server. + */ + public Process start() throws IOException { + if (this.serverProcess != null) { + throw new IllegalArgumentException("Server already started"); + } + this.serverProcess = Runtime.getRuntime().exec(getCommandLine()); + + return this.serverProcess; + } + + /** + * Stops the server (if started) + * + * @param forceIfNecessary + * use forceStop if mysqladmin doesn't shut the server down + * + * @throws IOException + * if an error occurs while stopping the server + */ + public void stop(boolean forceIfNecessary) throws IOException { + if (this.serverProcess != null) { + + String basedir = getServerProps().getProperty(BASEDIR_KEY); + + StringBuilder pathBuf = new StringBuilder(basedir); + + if (!basedir.endsWith(File.separator)) { + pathBuf.append(File.separator); + } + + //String defaultsFilePath = getServerProps().getProperty(DEFAULTS_FILE_KEY); + + pathBuf.append("bin"); + pathBuf.append(File.separator); + pathBuf.append("mysqladmin shutdown"); + + System.out.println(pathBuf.toString()); + + Process mysqladmin = Runtime.getRuntime().exec(pathBuf.toString()); + + int exitStatus = -1; + + try { + exitStatus = mysqladmin.waitFor(); + } catch (InterruptedException ie) { + // ignore + } + + // + // Terminate the process if mysqladmin couldn't do it, and the user requested a force stop. + // + if (exitStatus != 0 && forceIfNecessary) { + forceStop(); + } + } + } + + /** + * Forcefully terminates the server process (if started). + */ + public void forceStop() { + if (this.serverProcess != null) { + this.serverProcess.destroy(); + this.serverProcess = null; + } + } + + /** + * Returns the list of properties that will be used to start/control the + * server. + * + * @return Properties the list of properties. + */ + public synchronized Properties getServerProps() { + if (this.serverProps == null) { + this.serverProps = new Properties(); + } + + return this.serverProps; + } + + /** + * Returns the full commandline used to start the mysql server, including + * and arguments to be passed to the server process. + * + * @return String the commandline used to start the mysql server. + */ + private String getCommandLine() { + StringBuilder commandLine = new StringBuilder(getFullExecutablePath()); + commandLine.append(buildOptionalCommandLine()); + + return commandLine.toString(); + } + + /** + * Returns the fully-qualifed path to the 'mysqld' executable + * + * @return String the path to the server executable. + */ + private String getFullExecutablePath() { + StringBuilder pathBuf = new StringBuilder(); + + String optionalExecutablePath = getServerProps().getProperty(EXECUTABLE_PATH_KEY); + + if (optionalExecutablePath == null) { + // build the path using the defaults + String basedir = getServerProps().getProperty(BASEDIR_KEY); + pathBuf.append(basedir); + + if (!basedir.endsWith(File.separator)) { + pathBuf.append(File.separatorChar); + } + + if (runningOnWindows()) { + pathBuf.append("bin"); + } else { + pathBuf.append("libexec"); + } + + pathBuf.append(File.separatorChar); + } else { + pathBuf.append(optionalExecutablePath); + + if (!optionalExecutablePath.endsWith(File.separator)) { + pathBuf.append(File.separatorChar); + } + } + + String executableName = getServerProps().getProperty(EXECUTABLE_NAME_KEY, "mysqld"); + + pathBuf.append(executableName); + + return pathBuf.toString(); + } + + /** + * Builds the list of command-line arguments that will be passed to the + * mysql server to be started. + * + * @return String the list of command-line arguments. + */ + private String buildOptionalCommandLine() { + StringBuilder commandLineBuf = new StringBuilder(); + + if (this.serverProps != null) { + + for (Iterator iter = this.serverProps.keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + String value = this.serverProps.getProperty(key); + + if (!isNonCommandLineArgument(key)) { + if (value != null && value.length() > 0) { + commandLineBuf.append(" \""); + commandLineBuf.append("--"); + commandLineBuf.append(key); + commandLineBuf.append("="); + commandLineBuf.append(value); + commandLineBuf.append("\""); + } else { + commandLineBuf.append(" --"); + commandLineBuf.append(key); + } + } + } + } + + return commandLineBuf.toString(); + } + + /** + * Returns true if the property does not belong as a command-line argument + * + * @return boolean if the property should not be a command-line argument. + */ + private boolean isNonCommandLineArgument(String propName) { + return propName.equals(EXECUTABLE_NAME_KEY) || propName.equals(EXECUTABLE_PATH_KEY); + } + + /** + * Lazily creates a list of system properties. + * + * @return Properties the properties from System.getProperties() + */ + private synchronized Properties getSystemProperties() { + if (this.systemProps == null) { + this.systemProps = System.getProperties(); + } + + return this.systemProps; + } + + /** + * Is this ServerController running on a Windows operating system? + * + * @return boolean if this ServerController is running on Windows + */ + private boolean runningOnWindows() { + return StringUtils.indexOfIgnoreCase(getSystemProperties().getProperty("os.name"), "WINDOWS") != -1; + } +} diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/TimezoneDump.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/TimezoneDump.java new file mode 100644 index 0000000..8d55092 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/TimezoneDump.java @@ -0,0 +1,79 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.sql.DriverManager; +import java.sql.ResultSet; + +import com.mysql.jdbc.TimeUtil; + +/** + * Dumps the timezone of the MySQL server represented by the JDBC url given on the commandline (or localhost/test if none provided). + */ +public class TimezoneDump { + private static final String DEFAULT_URL = "jdbc:mysql:///test"; + + /** + * Constructor for TimezoneDump. + */ + public TimezoneDump() { + super(); + } + + /** + * Entry point for program when called from the command line. + * + * @param args + * command-line args. Arg 1 is JDBC URL. + * @throws Exception + * if any errors occur + */ + public static void main(String[] args) throws Exception { + String jdbcUrl = DEFAULT_URL; + + if ((args.length == 1) && (args[0] != null)) { + jdbcUrl = args[0]; + } + + Class.forName("com.mysql.jdbc.Driver").newInstance(); + + ResultSet rs = null; + + try { + rs = DriverManager.getConnection(jdbcUrl).createStatement().executeQuery("SHOW VARIABLES LIKE 'timezone'"); + + while (rs.next()) { + String timezoneFromServer = rs.getString(2); + System.out.println("MySQL timezone name: " + timezoneFromServer); + + String canonicalTimezone = TimeUtil.getCanonicalTimezone(timezoneFromServer, null); + System.out.println("Java timezone name: " + canonicalTimezone); + } + } finally { + if (rs != null) { + rs.close(); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java new file mode 100644 index 0000000..d59bfba --- /dev/null +++ b/mysql-connector-java-5.1.40/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java @@ -0,0 +1,130 @@ +/* + Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package com.mysql.jdbc.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.util.Properties; + +import com.mysql.jdbc.NonRegisteringDriver; + +/** + * Creates output directory structure for multi-jvm, multi-url unit, regression and compliance tests. + */ +public class VersionFSHierarchyMaker { + public static void main(String[] args) throws Exception { + if (args.length < 3) { + usage(); + System.exit(1); + } + + String jdbcUrl = null; + + String jvmVersion = removeWhitespaceChars(System.getProperty("java.version")); + String jvmVendor = removeWhitespaceChars(System.getProperty("java.vendor")); + String osName = removeWhitespaceChars(System.getProperty("os.name")); + String osArch = removeWhitespaceChars(System.getProperty("os.arch")); + String osVersion = removeWhitespaceChars(System.getProperty("os.version")); + + jdbcUrl = System.getProperty("com.mysql.jdbc.testsuite.url"); + + String mysqlVersion = "MySQL" + args[2] + "_"; + + try { + final Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + Connection conn = new NonRegisteringDriver().connect(jdbcUrl, props); + + ResultSet rs = conn.createStatement().executeQuery("SELECT VERSION()"); + rs.next(); + mysqlVersion += removeWhitespaceChars(rs.getString(1)); + } catch (Throwable t) { + mysqlVersion += "no-server-running-on-" + removeWhitespaceChars(jdbcUrl); + } + + String jvmSubdirName = jvmVendor + "-" + jvmVersion; + String osSubdirName = osName + "-" + osArch + "-" + osVersion; + + File baseDir = new File(args[0]); + File mysqlVersionDir = new File(baseDir, mysqlVersion); + File osVersionDir = new File(mysqlVersionDir, osSubdirName); + File jvmVersionDir = new File(osVersionDir, jvmSubdirName); + + jvmVersionDir.mkdirs(); + + FileOutputStream pathOut = null; + + try { + String propsOutputPath = args[1]; + pathOut = new FileOutputStream(propsOutputPath); + String baseDirStr = baseDir.getAbsolutePath(); + String jvmVersionDirStr = jvmVersionDir.getAbsolutePath(); + + if (jvmVersionDirStr.startsWith(baseDirStr)) { + jvmVersionDirStr = jvmVersionDirStr.substring(baseDirStr.length() + 1); + } + + pathOut.write(jvmVersionDirStr.getBytes()); + } finally { + if (pathOut != null) { + pathOut.flush(); + pathOut.close(); + } + } + } + + public static String removeWhitespaceChars(String input) { + if (input == null) { + return input; + } + + int strLen = input.length(); + + StringBuilder output = new StringBuilder(strLen); + + for (int i = 0; i < strLen; i++) { + char c = input.charAt(i); + if (!Character.isDigit(c) && !Character.isLetter(c)) { + if (Character.isWhitespace(c)) { + output.append("_"); + } else { + output.append("."); + } + } else { + output.append(c); + } + } + + return output.toString(); + } + + private static void usage() { + System.err.println("Creates a fs hierarchy representing MySQL version, OS version and JVM version."); + System.err.println("Stores the full path as 'outputDirectory' property in file 'directoryPropPath'"); + System.err.println(); + System.err.println("Usage: java VersionFSHierarchyMaker baseDirectory directoryPropPath jdbcUrlIter"); + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/Client1_Fabric.java b/mysql-connector-java-5.1.40/src/demo/fabric/Client1_Fabric.java new file mode 100644 index 0000000..fd32bb5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/Client1_Fabric.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package demo.fabric; + +import com.mysql.fabric.proto.xmlrpc.XmlRpcClient; + +/** + * Basic usage client. print out a bunch of information we can ask for from Fabric + */ +public class Client1_Fabric { + public static void main(String args[]) throws Exception { + String hostname = System.getProperty("com.mysql.fabric.testsuite.hostname"); + String port = System.getProperty("com.mysql.fabric.testsuite.port"); + + XmlRpcClient fabricClient = new XmlRpcClient("http://" + hostname + ":" + port, null, null); + System.out.println("Fabrics: " + fabricClient.getFabricNames()); + System.out.println("Groups: " + fabricClient.getGroupNames()); + for (String groupName : fabricClient.getGroupNames()) { + System.out.println("Group def for '" + groupName + "': " + fabricClient.getServerGroup(groupName).toString().replaceAll("Serv", "\n\tServ")); + } + System.out.println("Servers for employees.employees.50: " + fabricClient.getServersForKey("employees.employees", 50)); + System.out.println("Servers for employees.employees.10050: " + fabricClient.getServersForKey("employees.employees", 10050)); + System.out.flush(); + System.out.println("All servers: " + fabricClient.getServerGroups()); + //fabricClient.getGroup("NON_EXISTANT_GROUP"); + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/Employee.java b/mysql-connector-java-5.1.40/src/demo/fabric/Employee.java new file mode 100644 index 0000000..e67657f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/Employee.java @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package demo.fabric; + +/** + * Employee as mapped to 'employees' table. + */ +public class Employee { + private Integer id; + private String firstName; + private String lastName; + + public Integer getId() { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return this.lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesDataSource.java b/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesDataSource.java new file mode 100644 index 0000000..8aaac20 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesDataSource.java @@ -0,0 +1,138 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package demo.fabric; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + +import com.mysql.fabric.jdbc.FabricMySQLConnection; +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +/** + * Demonstrate working with employee data in MySQL Fabric with Connector/J and the JDBC APIs via a DataSource-created connection. + */ +public class EmployeesDataSource { + public static void main(String args[]) throws Exception { + String hostname = System.getProperty("com.mysql.fabric.testsuite.hostname"); + String port = System.getProperty("com.mysql.fabric.testsuite.port"); + String database = System.getProperty("com.mysql.fabric.testsuite.database"); + // credentials to authenticate with the SQL nodes + String user = System.getProperty("com.mysql.fabric.testsuite.username"); + String password = System.getProperty("com.mysql.fabric.testsuite.password"); + // credentials to authenticate to the Fabric node + String fabricUsername = System.getProperty("com.mysql.fabric.testsuite.fabricUsername"); + String fabricPassword = System.getProperty("com.mysql.fabric.testsuite.fabricPassword"); + + // setup the Fabric datasource to create connections + FabricMySQLDataSource ds = new FabricMySQLDataSource(); + ds.setServerName(hostname); + ds.setPort(Integer.valueOf(port)); + ds.setDatabaseName(database); + ds.setFabricUsername(fabricUsername); + ds.setFabricPassword(fabricPassword); + + // Load the driver if running under Java 5 + if (!com.mysql.jdbc.Util.isJdbc4()) { + Class.forName("com.mysql.fabric.jdbc.FabricMySQLDriver"); + } + + // 1. Create database and table for our demo + ds.setDatabaseName("mysql"); // connect to the `mysql` database before creating our `employees` database + ds.setFabricServerGroup("fabric_test1_global"); // connect to the global group + Connection rawConnection = ds.getConnection(user, password); + Statement statement = rawConnection.createStatement(); + statement.executeUpdate("create database if not exists employees"); + statement.close(); + rawConnection.close(); + + // We should connect to the global group to run DDL statements, they will be replicated to the server groups for all shards. + + // The 1-st way is to set it's name explicitly via the "fabricServerGroup" datasource property + ds.setFabricServerGroup("fabric_test1_global"); + rawConnection = ds.getConnection(user, password); + statement = rawConnection.createStatement(); + statement.executeUpdate("create database if not exists employees"); + statement.close(); + rawConnection.close(); + + // The 2-nd way is to get implicitly connected to global group when the shard key isn't provided, ie. set "fabricShardTable" connection property but + // don't set "fabricShardKey" + ds.setFabricServerGroup(null); // clear the setting in the datasource for previous connections + ds.setFabricShardTable("employees.employees"); + rawConnection = ds.getConnection(user, password); + // At this point, we have a connection to the global group for the `employees.employees' shard mapping. + statement = rawConnection.createStatement(); + statement.executeUpdate("drop table if exists employees.employees"); + statement.executeUpdate("create table employees.employees (emp_no int not null, first_name varchar(50), last_name varchar(50), primary key (emp_no))"); + + // 2. Insert data + + // Cast to a Fabric connection to have access to Fabric-specific methods + FabricMySQLConnection connection = (FabricMySQLConnection) rawConnection; + + // example data used to create employee records + Integer ids[] = new Integer[] { 1, 2, 10001, 10002 }; + String firstNames[] = new String[] { "John", "Jane", "Andy", "Alice" }; + String lastNames[] = new String[] { "Doe", "Doe", "Wiley", "Wein" }; + + // insert employee data + PreparedStatement ps = connection.prepareStatement("INSERT INTO employees.employees VALUES (?,?,?)"); + for (int i = 0; i < 4; ++i) { + // choose the shard that handles the data we interested in + connection.setShardKey(ids[i].toString()); + + // perform insert in standard fashion + ps.setInt(1, ids[i]); + ps.setString(2, firstNames[i]); + ps.setString(3, lastNames[i]); + ps.executeUpdate(); + } + + // 3. Query the data from employees + System.out.println("Querying employees"); + System.out.format("%7s | %-30s | %-30s%n", "emp_no", "first_name", "last_name"); + System.out.println("--------+--------------------------------+-------------------------------"); + ps = connection.prepareStatement("select emp_no, first_name, last_name from employees.employees where emp_no = ?"); + for (int i = 0; i < 4; ++i) { + + // we need to specify the shard key before accessing the data + connection.setShardKey(ids[i].toString()); + + ps.setInt(1, ids[i]); + ResultSet rs = ps.executeQuery(); + rs.next(); + System.out.format("%7d | %-30s | %-30s%n", rs.getInt(1), rs.getString(2), rs.getString(3)); + rs.close(); + } + ps.close(); + + // 4. Connect to the global group and clean up + connection.setServerGroupName("fabric_test1_global"); + statement.executeUpdate("drop table if exists employees.employees"); + statement.close(); + connection.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesJdbc.java b/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesJdbc.java new file mode 100644 index 0000000..8e8c890 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/EmployeesJdbc.java @@ -0,0 +1,124 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package demo.fabric; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + +import com.mysql.fabric.jdbc.FabricMySQLConnection; + +/** + * Demonstrate working with employee data in MySQL Fabric with Connector/J and the JDBC APIs. + */ +public class EmployeesJdbc { + public static void main(String args[]) throws Exception { + + String hostname = System.getProperty("com.mysql.fabric.testsuite.hostname"); + String port = System.getProperty("com.mysql.fabric.testsuite.port"); + String database = System.getProperty("com.mysql.fabric.testsuite.database"); + String user = System.getProperty("com.mysql.fabric.testsuite.username"); + String password = System.getProperty("com.mysql.fabric.testsuite.password"); + + String baseUrl = "jdbc:mysql:fabric://" + hostname + ":" + Integer.valueOf(port) + "/"; + + // Load the driver if running under Java 5 + if (!com.mysql.jdbc.Util.isJdbc4()) { + Class.forName("com.mysql.fabric.jdbc.FabricMySQLDriver"); + } + + // 1. Create database and table for our demo + Connection rawConnection = DriverManager.getConnection(baseUrl + "mysql?fabricServerGroup=fabric_test1_global", user, password); + Statement statement = rawConnection.createStatement(); + statement.executeUpdate("create database if not exists employees"); + statement.close(); + rawConnection.close(); + + // We should connect to the global group to run DDL statements, they will be replicated to the server groups for all shards. + + // The 1-st way is to set it's name explicitly via the "fabricServerGroup" connection property + rawConnection = DriverManager.getConnection(baseUrl + database + "?fabricServerGroup=fabric_test1_global", user, password); + statement = rawConnection.createStatement(); + statement.executeUpdate("create database if not exists employees"); + statement.close(); + rawConnection.close(); + + // The 2-nd way is to get implicitly connected to global group when the shard key isn't provided, ie. set "fabricShardTable" connection property but + // don't set "fabricShardKey" + rawConnection = DriverManager.getConnection(baseUrl + "employees?fabricShardTable=employees.employees", user, password); + // At this point, we have a connection to the global group for the `employees.employees' shard mapping. + statement = rawConnection.createStatement(); + statement.executeUpdate("drop table if exists employees"); + statement.executeUpdate("create table employees (emp_no int not null, first_name varchar(50), last_name varchar(50), primary key (emp_no))"); + + // 2. Insert data + + // Cast to a Fabric connection to have access to specific methods + FabricMySQLConnection connection = (FabricMySQLConnection) rawConnection; + + // example data used to create employee records + Integer ids[] = new Integer[] { 1, 2, 10001, 10002 }; + String firstNames[] = new String[] { "John", "Jane", "Andy", "Alice" }; + String lastNames[] = new String[] { "Doe", "Doe", "Wiley", "Wein" }; + + // insert employee data + PreparedStatement ps = connection.prepareStatement("INSERT INTO employees.employees VALUES (?,?,?)"); + for (int i = 0; i < 4; ++i) { + // choose the shard that handles the data we interested in + connection.setShardKey(ids[i].toString()); + + // perform insert in standard fashion + ps.setInt(1, ids[i]); + ps.setString(2, firstNames[i]); + ps.setString(3, lastNames[i]); + ps.executeUpdate(); + } + + // 3. Query the data from employees + System.out.println("Querying employees"); + System.out.format("%7s | %-30s | %-30s%n", "emp_no", "first_name", "last_name"); + System.out.println("--------+--------------------------------+-------------------------------"); + ps = connection.prepareStatement("select emp_no, first_name, last_name from employees where emp_no = ?"); + for (int i = 0; i < 4; ++i) { + + // we need to specify the shard key before accessing the data + connection.setShardKey(ids[i].toString()); + + ps.setInt(1, ids[i]); + ResultSet rs = ps.executeQuery(); + rs.next(); + System.out.format("%7d | %-30s | %-30s%n", rs.getInt(1), rs.getString(2), rs.getString(3)); + rs.close(); + } + ps.close(); + + // 4. Connect to the global group and clean up + connection.setServerGroupName("fabric_test1_global"); + statement.executeUpdate("drop table if exists employees"); + statement.close(); + connection.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/HibernateFabric.java b/mysql-connector-java-5.1.40/src/demo/fabric/HibernateFabric.java new file mode 100644 index 0000000..690c279 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/HibernateFabric.java @@ -0,0 +1,119 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package demo.fabric; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.service.ServiceRegistryBuilder; + +import com.mysql.fabric.hibernate.FabricMultiTenantConnectionProvider; + +/** + * Example using Hibernate 4 Multi-tenancy in DATABASE mode with Fabric. + */ +public class HibernateFabric { + public static void main(String args[]) throws Exception { + + String hostname = System.getProperty("com.mysql.fabric.testsuite.hostname"); + String port = System.getProperty("com.mysql.fabric.testsuite.port"); + String user = System.getProperty("com.mysql.fabric.testsuite.username"); + String password = System.getProperty("com.mysql.fabric.testsuite.password"); + String database = System.getProperty("com.mysql.fabric.testsuite.database"); + String fabricUsername = System.getProperty("com.mysql.fabric.testsuite.fabricUsername"); + String fabricPassword = System.getProperty("com.mysql.fabric.testsuite.fabricPassword"); + + // Using JDBC Fabric connection to create database and table + Class.forName("com.mysql.fabric.jdbc.FabricMySQLDriver"); + Connection con = DriverManager.getConnection("jdbc:mysql:fabric://" + hostname + ":" + Integer.valueOf(port) + + "/mysql?fabricServerGroup=fabric_test1_global&fabricUsername=" + fabricUsername + "&fabricPassword=" + fabricPassword, user, password); + Statement stmt = con.createStatement(); + stmt.executeUpdate("create database if not exists employees"); + con.close(); + + con = DriverManager.getConnection("jdbc:mysql:fabric://" + hostname + ":" + Integer.valueOf(port) + "/" + database + + "?fabricServerGroup=fabric_test1_global&fabricUsername=" + fabricUsername + "&fabricPassword=" + fabricPassword, user, password); + stmt = con.createStatement(); + stmt.executeUpdate("create database if not exists employees"); + stmt.executeUpdate("drop table if exists employees.employees"); + stmt.executeUpdate("create table employees.employees (emp_no INT PRIMARY KEY, first_name CHAR(40), last_name CHAR(40))"); + stmt.close(); + + // we have to wait for replication .... + Thread.sleep(2000); + + // Using Hibernate + SessionFactory sf = createSessionFactory("http://" + hostname + ":" + port, user, password, fabricUsername, fabricPassword); + + // add some employees + for (int i = 1; i < 11; ++i) { + int j = i; + // put a few in the other shard + if ((j % 2) == 0) { + j += 10000; + } + + Session session = sf.withOptions().tenantIdentifier("" + j) // choose a db server + .openSession(); + + // vanilla hibernate code + session.beginTransaction(); + Employee e = new Employee(); + e.setId(j); + e.setFirstName("First name of employee " + j); + e.setLastName("Smith" + j); + session.save(e); + + session.getTransaction().commit(); + session.close(); + } + + // clean up + con.createStatement().executeUpdate("drop table employees.employees"); + con.close(); + + } + + /** + * Configuration of session factory with Fabric integration. + */ + public static SessionFactory createSessionFactory(String fabricUrl, String username, String password, String fabricUser, String fabricPassword) + throws Exception { + // creating this here allows passing needed params to the constructor + FabricMultiTenantConnectionProvider connProvider = new FabricMultiTenantConnectionProvider(fabricUrl, "employees", "employees", username, password, + fabricUser, fabricPassword); + ServiceRegistryBuilder srb = new ServiceRegistryBuilder(); + srb.addService(org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider.class, connProvider); + srb.applySetting("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect"); + + Configuration config = new Configuration(); + config.setProperty("hibernate.multiTenancy", "DATABASE"); + config.addResource("com/mysql/fabric/demo/employee.hbm.xml"); + return config.buildSessionFactory(srb.buildServiceRegistry()); + } +} diff --git a/mysql-connector-java-5.1.40/src/demo/fabric/resources/com/mysql/fabric/demo/employee.hbm.xml b/mysql-connector-java-5.1.40/src/demo/fabric/resources/com/mysql/fabric/demo/employee.hbm.xml new file mode 100644 index 0000000..5bd8b12 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/demo/fabric/resources/com/mysql/fabric/demo/employee.hbm.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + diff --git a/mysql-connector-java-5.1.40/src/doc/sources/pom.xml b/mysql-connector-java-5.1.40/src/doc/sources/pom.xml new file mode 100644 index 0000000..eee17fa --- /dev/null +++ b/mysql-connector-java-5.1.40/src/doc/sources/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + mysql + mysql-connector-java + @MYSQL_CJ_VERSION@ + jar + + MySQL Connector/J + MySQL JDBC Type 4 driver + + + + The GNU General Public License, Version 2 + http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + repo + MySQL Connector/J contains exceptions to GPL requirements when linking with other components +that are licensed under OSI-approved open source licenses, see EXCEPTIONS-CONNECTOR-J +in this distribution for more details. + + + + http://dev.mysql.com/doc/connector-j/en/ + + + scm:git:git@github.com:mysql/mysql-connector-j.git + https://github.com/mysql/mysql-connector-j + + + + Oracle Corporation + http://www.oracle.com + + diff --git a/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.jar b/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.jar new file mode 100644 index 0000000..3caa24b Binary files /dev/null and b/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.jar differ diff --git a/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.src.zip b/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.src.zip new file mode 100644 index 0000000..a4faedf Binary files /dev/null and b/mysql-connector-java-5.1.40/src/lib/c3p0-0.9.1-pre6.src.zip differ diff --git a/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper-src.jar b/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper-src.jar new file mode 100644 index 0000000..192cb6a Binary files /dev/null and b/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper-src.jar differ diff --git a/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper.jar b/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper.jar new file mode 100644 index 0000000..db01063 Binary files /dev/null and b/mysql-connector-java-5.1.40/src/lib/jboss-common-jdbc-wrapper.jar differ diff --git a/mysql-connector-java-5.1.40/src/lib/slf4j-api-1.6.1.jar b/mysql-connector-java-5.1.40/src/lib/slf4j-api-1.6.1.jar new file mode 100644 index 0000000..42e0ad0 Binary files /dev/null and b/mysql-connector-java-5.1.40/src/lib/slf4j-api-1.6.1.jar differ diff --git a/mysql-connector-java-5.1.40/src/org/gjt/mm/mysql/Driver.java b/mysql-connector-java-5.1.40/src/org/gjt/mm/mysql/Driver.java new file mode 100644 index 0000000..c34de0a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/org/gjt/mm/mysql/Driver.java @@ -0,0 +1,41 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package org.gjt.mm.mysql; + +import java.sql.SQLException; + +/** + * Here for backwards compatibility with MM.MySQL + */ +public class Driver extends com.mysql.jdbc.Driver { + /** + * Creates a new instance of Driver + * + * @throws SQLException + * if a database error occurs. + */ + public Driver() throws SQLException { + super(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/BaseStatementInterceptor.java b/mysql-connector-java-5.1.40/src/testsuite/BaseStatementInterceptor.java new file mode 100644 index 0000000..fbd46d6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/BaseStatementInterceptor.java @@ -0,0 +1,55 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite; + +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StatementInterceptorV2; + +public class BaseStatementInterceptor implements StatementInterceptorV2 { + + public void init(Connection conn, Properties props) throws SQLException { + } + + public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement, Connection connection) throws SQLException { + return null; + } + + public boolean executeTopLevelOnly() { + return false; + } + + public void destroy() { + } + + public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement, ResultSetInternalMethods originalResultSet, Connection connection, + int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) throws SQLException { + return originalResultSet; + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/BaseTestCase.java b/mysql-connector-java-5.1.40/src/testsuite/BaseTestCase.java new file mode 100644 index 0000000..2f50007 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/BaseTestCase.java @@ -0,0 +1,1283 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ReplicationConnection; +import com.mysql.jdbc.ReplicationDriver; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; + +import junit.framework.TestCase; + +/** + * Base class for all test cases. Creates connections, statements, etc. and closes them. + */ +public abstract class BaseTestCase extends TestCase { + + private final static String ADMIN_CONNECTION_PROPERTY_NAME = "com.mysql.jdbc.testsuite.admin-url"; + + private final static String NO_MULTI_HOST_PROPERTY_NAME = "com.mysql.jdbc.testsuite.no-multi-hosts-tests"; + + // next variables disable some tests + protected boolean DISABLED_testBug15121 = true; // TODO needs to be fixed on server + protected boolean DISABLED_testBug7033 = true; // TODO disabled for unknown reason + protected boolean DISABLED_testBug2654 = true; // TODO check if it's still a server-level bug + protected boolean DISABLED_testBug5136 = true; // TODO disabled for unknown reason + protected boolean DISABLED_testBug65503 = true; // TODO disabled for unknown reason + protected boolean DISABLED_testContention = true; // TODO disabled for unknown reason + + /** + * JDBC URL, initialized from com.mysql.jdbc.testsuite.url system property, + * or defaults to jdbc:mysql:///test + */ + protected static String dbUrl = "jdbc:mysql:///test"; + + /** + * JDBC URL, initialized from com.mysql.jdbc.testsuite.url.sha256default system property + */ + protected static String sha256Url = null; + + /** Instance counter */ + private static int instanceCount = 1; + + /** Connection to server, initialized in setUp() Cleaned up in tearDown(). */ + protected Connection conn = null; + + protected Connection sha256Conn = null; + + /** list of schema objects to be dropped in tearDown */ + private List createdObjects; + + /** The driver to use */ + protected String dbClass = "com.mysql.jdbc.Driver"; + + /** My instance number */ + private int myInstanceNumber = 0; + + /** + * PreparedStatement to be used in tests, not initialized. Cleaned up in + * tearDown(). + */ + protected PreparedStatement pstmt = null; + + /** + * ResultSet to be used in tests, not initialized. Cleaned up in tearDown(). + */ + protected ResultSet rs = null; + + protected ResultSet sha256Rs = null; + + /** + * Statement to be used in tests, initialized in setUp(). Cleaned up in + * tearDown(). + */ + protected Statement stmt = null; + + protected Statement sha256Stmt = null; + + private boolean isOnCSFS = true; + + /** + * Creates a new BaseTestCase object. + * + * @param name + * The name of the JUnit test case + */ + public BaseTestCase(String name) { + super(name); + this.myInstanceNumber = instanceCount++; + + String newDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url"); + + if ((newDbUrl != null) && (newDbUrl.trim().length() != 0)) { + dbUrl = newDbUrl; + } else { + String defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.default"); + + if ((defaultDbUrl != null) && (defaultDbUrl.trim().length() != 0)) { + dbUrl = defaultDbUrl; + } + } + + String defaultSha256Url = System.getProperty("com.mysql.jdbc.testsuite.url.sha256default"); + + if ((defaultSha256Url != null) && (defaultSha256Url.trim().length() != 0)) { + sha256Url = defaultSha256Url; + } + + String newDriver = System.getProperty("com.mysql.jdbc.testsuite.driver"); + + if ((newDriver != null) && (newDriver.trim().length() != 0)) { + this.dbClass = newDriver; + } + } + + protected void createSchemaObject(String objectType, String objectName, String columnsAndOtherStuff) throws SQLException { + createSchemaObject(this.stmt, objectType, objectName, columnsAndOtherStuff); + } + + protected void createSchemaObject(Statement st, String objectType, String objectName, String columnsAndOtherStuff) throws SQLException { + if (st != null) { + this.createdObjects.add(new String[] { objectType, objectName }); + try { + dropSchemaObject(st, objectType, objectName); + } catch (SQLException ex) { + // ignore DROP USER failures + if (!ex.getMessage().startsWith("Operation DROP USER failed")) { + throw ex; + } + } + + StringBuilder createSql = new StringBuilder(objectName.length() + objectType.length() + columnsAndOtherStuff.length() + 10); + createSql.append("CREATE "); + createSql.append(objectType); + createSql.append(" "); + createSql.append(objectName); + createSql.append(" "); + createSql.append(columnsAndOtherStuff); + + try { + st.executeUpdate(createSql.toString()); + } catch (SQLException sqlEx) { + if ("42S01".equals(sqlEx.getSQLState())) { + System.err.println("WARN: Stale mysqld table cache preventing table creation - flushing tables and trying again"); + st.executeUpdate("FLUSH TABLES"); // some bug in 5.1 on the mac causes tables to not disappear from the cache + st.executeUpdate(createSql.toString()); + } else { + throw sqlEx; + } + } + } + } + + protected void createFunction(Statement st, String functionName, String functionDefn) throws SQLException { + createSchemaObject(st, "FUNCTION", functionName, functionDefn); + } + + protected void createFunction(String functionName, String functionDefn) throws SQLException { + createFunction(this.stmt, functionName, functionDefn); + } + + protected void dropFunction(Statement st, String functionName) throws SQLException { + dropSchemaObject(st, "FUNCTION", functionName); + } + + protected void dropFunction(String functionName) throws SQLException { + dropFunction(this.stmt, functionName); + } + + protected void createProcedure(Statement st, String procedureName, String procedureDefn) throws SQLException { + createSchemaObject(st, "PROCEDURE", procedureName, procedureDefn); + } + + protected void createProcedure(String procedureName, String procedureDefn) throws SQLException { + createProcedure(this.stmt, procedureName, procedureDefn); + } + + protected void dropProcedure(Statement st, String procedureName) throws SQLException { + dropSchemaObject(st, "PROCEDURE", procedureName); + } + + protected void dropProcedure(String procedureName) throws SQLException { + dropProcedure(this.stmt, procedureName); + } + + protected void createTable(Statement st, String tableName, String columnsAndOtherStuff) throws SQLException { + createSchemaObject(st, "TABLE", tableName, columnsAndOtherStuff); + } + + protected void createTable(String tableName, String columnsAndOtherStuff) throws SQLException { + createTable(this.stmt, tableName, columnsAndOtherStuff); + } + + protected void createTable(Statement st, String tableName, String columnsAndOtherStuff, String engine) throws SQLException { + createSchemaObject(st, "TABLE", tableName, columnsAndOtherStuff + " " + getTableTypeDecl() + " = " + engine); + } + + protected void createTable(String tableName, String columnsAndOtherStuff, String engine) throws SQLException { + createTable(this.stmt, tableName, columnsAndOtherStuff, engine); + } + + protected void dropTable(Statement st, String tableName) throws SQLException { + dropSchemaObject(st, "TABLE", tableName); + } + + protected void dropTable(String tableName) throws SQLException { + dropTable(this.stmt, tableName); + } + + protected void createView(Statement st, String viewName, String columnsAndOtherStuff) throws SQLException { + createSchemaObject(st, "VIEW", viewName, columnsAndOtherStuff); + } + + protected void createView(String viewName, String columnsAndOtherStuff) throws SQLException { + createView(this.stmt, viewName, columnsAndOtherStuff); + } + + protected void dropView(Statement st, String viewName) throws SQLException { + dropSchemaObject(st, "VIEW", viewName); + } + + protected void dropView(String viewName) throws SQLException { + dropView(this.stmt, viewName); + } + + protected void createDatabase(Statement st, String databaseName) throws SQLException { + createSchemaObject(st, "DATABASE", databaseName, ""); + } + + protected void createDatabase(String databaseName) throws SQLException { + createDatabase(this.stmt, databaseName); + } + + protected void dropDatabase(Statement st, String databaseName) throws SQLException { + dropSchemaObject(st, "DATABASE", databaseName); + } + + protected void dropDatabase(String databaseName) throws SQLException { + dropDatabase(this.stmt, databaseName); + } + + protected void createUser(Statement st, String userName, String otherStuff) throws SQLException { + createSchemaObject(st, "USER", userName, otherStuff); + } + + protected void createUser(String userName, String otherStuff) throws SQLException { + createUser(this.stmt, userName, otherStuff); + } + + protected void dropUser(Statement st, String user) throws SQLException { + dropSchemaObject(st, "USER", user); + } + + protected void dropUser(String user) throws SQLException { + dropUser(this.stmt, user); + } + + protected void dropSchemaObject(String objectType, String objectName) throws SQLException { + dropSchemaObject(this.stmt, objectType, objectName); + } + + protected void dropSchemaObject(Statement st, String objectType, String objectName) throws SQLException { + if (st != null) { + if (!objectType.equalsIgnoreCase("USER") || ((ConnectionImpl) st.getConnection()).versionMeetsMinimum(5, 7, 8)) { + st.executeUpdate("DROP " + objectType + " IF EXISTS " + objectName); + } else { + st.executeUpdate("DROP " + objectType + " " + objectName); + } + st.executeUpdate("flush privileges"); + } + } + + protected Connection getAdminConnection() throws SQLException { + return getAdminConnectionWithProps(new Properties()); + } + + protected Connection getAdminConnectionWithProps(Properties props) throws SQLException { + String adminUrl = System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME); + + if (adminUrl != null) { + return DriverManager.getConnection(adminUrl, props); + } + return null; + } + + protected Connection getConnectionWithProps(String propsList) throws SQLException { + return getConnectionWithProps(dbUrl, propsList); + } + + protected Connection getConnectionWithProps(String url, String propsList) throws SQLException { + Properties props = new Properties(); + + if (propsList != null) { + List keyValuePairs = StringUtils.split(propsList, ",", false); + + for (String kvp : keyValuePairs) { + List splitUp = StringUtils.split(kvp, "=", false); + StringBuilder value = new StringBuilder(); + + for (int i = 1; i < splitUp.size(); i++) { + if (i != 1) { + value.append("="); + } + + value.append(splitUp.get(i)); + + } + + props.setProperty(splitUp.get(0).toString().trim(), value.toString()); + } + } + + return getConnectionWithProps(url, props); + } + + /** + * Returns a new connection with the given properties + * + * @param props + * the properties to use (the URL will come from the standard for + * this testcase). + * + * @return a new connection using the given properties. + * + * @throws SQLException + */ + public Connection getConnectionWithProps(Properties props) throws SQLException { + return DriverManager.getConnection(dbUrl, props); + } + + protected Connection getConnectionWithProps(String url, Properties props) throws SQLException { + return DriverManager.getConnection(url, props); + } + + protected Connection getNewConnection() throws SQLException { + return DriverManager.getConnection(dbUrl); + } + + protected Connection getNewSha256Connection() throws SQLException { + if (sha256Url != null) { + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + return DriverManager.getConnection(sha256Url, props); + } + return null; + } + + /** + * Returns the per-instance counter (for messages when multi-threading + * stress tests) + * + * @return int the instance number + */ + protected int getInstanceNumber() { + return this.myInstanceNumber; + } + + protected String getMysqlVariable(Connection c, String variableName) throws SQLException { + Object value = getSingleIndexedValueWithQuery(c, 2, "SHOW VARIABLES LIKE '" + variableName + "'"); + + if (value != null) { + if (value instanceof byte[]) { + // workaround for bad 4.1.x bugfix + return new String((byte[]) value); + } + + return value.toString(); + } + + return null; + + } + + /** + * Returns the named MySQL variable from the currently connected server. + * + * @param variableName + * the name of the variable to return + * + * @return the value of the given variable, or NULL if it doesn't exist + * + * @throws SQLException + * if an error occurs + */ + protected String getMysqlVariable(String variableName) throws SQLException { + return getMysqlVariable(this.conn, variableName); + } + + /** + * Returns the properties that represent the default URL used for + * connections for all testcases. + * + * @return properties parsed from com.mysql.jdbc.testsuite.url + * + * @throws SQLException + * if parsing fails + */ + protected Properties getPropertiesFromTestsuiteUrl() throws SQLException { + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + + String hostname = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + if (hostname == null) { + props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + } else if (hostname.startsWith(":")) { + props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, hostname.substring(1)); + } + + String portNumber = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + if (portNumber == null) { + props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + } + + return props; + } + + protected Properties getHostFreePropertiesFromTestsuiteUrl() throws SQLException { + return getHostFreePropertiesFromTestsuiteUrl(null); + } + + protected Properties getHostFreePropertiesFromTestsuiteUrl(Properties props) throws SQLException { + Properties parsedProps = getPropertiesFromTestsuiteUrl(); + if (props != null) { + parsedProps.putAll(props); + } + removeHostRelatedProps(parsedProps); + return parsedProps; + } + + protected void removeHostRelatedProps(Properties props) { + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + + int numHosts = Integer.parseInt(props.getProperty(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY)); + + for (int i = 1; i <= numHosts; i++) { + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + "." + i); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + "." + i); + } + + props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + } + + protected int getRowCount(String tableName) throws SQLException { + ResultSet countRs = null; + + try { + countRs = this.stmt.executeQuery("SELECT COUNT(*) FROM " + tableName); + + countRs.next(); + + return countRs.getInt(1); + } finally { + if (countRs != null) { + countRs.close(); + } + } + } + + protected Object getSingleIndexedValueWithQuery(Connection c, int columnIndex, String query) throws SQLException { + ResultSet valueRs = null; + + Statement svStmt = null; + + try { + svStmt = c.createStatement(); + + valueRs = svStmt.executeQuery(query); + + if (!valueRs.next()) { + return null; + } + + return valueRs.getObject(columnIndex); + } finally { + if (valueRs != null) { + valueRs.close(); + } + + if (svStmt != null) { + svStmt.close(); + } + } + } + + protected Object getSingleIndexedValueWithQuery(int columnIndex, String query) throws SQLException { + return getSingleIndexedValueWithQuery(this.conn, columnIndex, query); + } + + protected Object getSingleValue(String tableName, String columnName, String whereClause) throws SQLException { + return getSingleValueWithQuery("SELECT " + columnName + " FROM " + tableName + ((whereClause == null) ? "" : " " + whereClause)); + } + + protected Object getSingleValueWithQuery(String query) throws SQLException { + return getSingleIndexedValueWithQuery(1, query); + } + + protected String getTableTypeDecl() throws SQLException { + if (versionMeetsMinimum(5, 0)) { + return "ENGINE"; + } + return "TYPE"; + } + + protected boolean isAdminConnectionConfigured() { + return System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME) != null; + } + + protected boolean isServerRunningOnWindows() throws SQLException { + return (getMysqlVariable("datadir").indexOf('\\') != -1); + } + + public void logDebug(String message) { + if (System.getProperty("com.mysql.jdbc.testsuite.noDebugOutput") == null) { + System.err.println(message); + } + } + + protected File newTempBinaryFile(String name, long size) throws IOException { + File tempFile = File.createTempFile(name, "tmp"); + tempFile.deleteOnExit(); + + cleanupTempFiles(tempFile, name); + + FileOutputStream fos = new FileOutputStream(tempFile); + BufferedOutputStream bos = new BufferedOutputStream(fos); + for (long i = 0; i < size; i++) { + bos.write((byte) i); + } + bos.close(); + assertTrue(tempFile.exists()); + assertEquals(size, tempFile.length()); + return tempFile; + } + + protected final boolean runLongTests() { + return runTestIfSysPropDefined("com.mysql.jdbc.testsuite.runLongTests"); + } + + /** + * Checks whether a certain system property is defined, in order to + * run/not-run certain tests + * + * @param propName + * the property name to check for + * + * @return true if the property is defined. + */ + protected boolean runTestIfSysPropDefined(String propName) { + String prop = System.getProperty(propName); + + return (prop != null) && (prop.length() > 0); + } + + protected boolean runMultiHostTests() { + return !runTestIfSysPropDefined(NO_MULTI_HOST_PROPERTY_NAME); + } + + /** + * Creates resources used by all tests. + * + * @throws Exception + * if an error occurs. + */ + @Override + public void setUp() throws Exception { + System.out.println("Loading JDBC driver '" + this.dbClass + "'"); + Class.forName(this.dbClass).newInstance(); + System.out.println("Done.\n"); + this.createdObjects = new ArrayList(); + + if (this.dbClass.equals("gwe.sql.gweMysqlDriver")) { + try { + this.conn = DriverManager.getConnection(dbUrl, "", ""); + this.sha256Conn = sha256Url == null ? null : DriverManager.getConnection(sha256Url, "", ""); + } catch (Exception ex) { + ex.printStackTrace(); + fail(); + } + } else { + try { + Properties props = new Properties(); + props.setProperty("useSSL", "false"); // testsuite is built upon non-SSL default connection + this.conn = DriverManager.getConnection(dbUrl, props); + + props.setProperty("allowPublicKeyRetrieval", "true"); + this.sha256Conn = sha256Url == null ? null : DriverManager.getConnection(sha256Url, props); + } catch (Exception ex) { + ex.printStackTrace(); + fail(); + } + } + + System.out.println("Done.\n"); + + this.stmt = this.conn.createStatement(); + + try { + if (dbUrl.indexOf("mysql") != -1) { + this.rs = this.stmt.executeQuery("SELECT VERSION()"); + this.rs.next(); + logDebug("Connected to " + this.rs.getString(1)); + } else { + logDebug("Connected to " + this.conn.getMetaData().getDatabaseProductName() + " / " + this.conn.getMetaData().getDatabaseProductVersion()); + } + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + + this.isOnCSFS = !this.conn.getMetaData().storesLowerCaseIdentifiers(); + + if (this.sha256Conn != null) { + this.sha256Stmt = this.sha256Conn.createStatement(); + + try { + if (sha256Url.indexOf("mysql") != -1) { + this.sha256Rs = this.sha256Stmt.executeQuery("SELECT VERSION()"); + this.sha256Rs.next(); + logDebug("Connected to " + this.sha256Rs.getString(1)); + } else { + logDebug("Connected to " + this.sha256Conn.getMetaData().getDatabaseProductName() + " / " + + this.sha256Conn.getMetaData().getDatabaseProductVersion()); + } + } finally { + if (this.sha256Rs != null) { + this.sha256Rs.close(); + this.sha256Rs = null; + } + } + } + } + + /** + * Destroys resources created during the test case. + */ + @Override + public void tearDown() throws Exception { + if (this.rs != null) { + try { + this.rs.close(); + } catch (SQLException SQLE) { + } + } + + if (this.sha256Rs != null) { + try { + this.sha256Rs.close(); + } catch (SQLException SQLE) { + } + } + + if (System.getProperty("com.mysql.jdbc.testsuite.retainArtifacts") == null) { + Statement st = this.conn == null || this.conn.isClosed() ? getNewConnection().createStatement() : this.conn.createStatement(); + Statement sha256st; + if (this.sha256Conn == null || this.sha256Conn.isClosed()) { + Connection c = getNewSha256Connection(); + sha256st = c == null ? null : c.createStatement(); + } else { + sha256st = this.sha256Conn.createStatement(); + } + + for (int i = 0; i < this.createdObjects.size(); i++) { + String[] objectInfo = this.createdObjects.get(i); + + try { + dropSchemaObject(st, objectInfo[0], objectInfo[1]); + } catch (SQLException SQLE) { + } + + try { + dropSchemaObject(sha256st, objectInfo[0], objectInfo[1]); + } catch (SQLException SQLE) { + } + } + st.close(); + if (sha256st != null) { + sha256st.close(); + } + } + + if (this.stmt != null) { + try { + this.stmt.close(); + } catch (SQLException SQLE) { + } + } + + if (this.sha256Stmt != null) { + try { + this.sha256Stmt.close(); + } catch (SQLException SQLE) { + } + } + + if (this.pstmt != null) { + try { + this.pstmt.close(); + } catch (SQLException SQLE) { + } + } + + if (this.conn != null) { + try { + this.conn.close(); + } catch (SQLException SQLE) { + } + } + + if (this.sha256Conn != null) { + try { + this.sha256Conn.close(); + } catch (SQLException SQLE) { + } + } + } + + /** + * Checks whether the database we're connected to meets the given version + * minimum + * + * @param major + * the major version to meet + * @param minor + * the minor version to meet + * + * @return boolean if the major/minor is met + * + * @throws SQLException + * if an error occurs. + */ + protected boolean versionMeetsMinimum(int major, int minor) throws SQLException { + return versionMeetsMinimum(major, minor, 0); + } + + /** + * Checks whether the database we're connected to meets the given version + * minimum + * + * @param major + * the major version to meet + * @param minor + * the minor version to meet + * + * @return boolean if the major/minor is met + * + * @throws SQLException + * if an error occurs. + */ + protected boolean versionMeetsMinimum(int major, int minor, int subminor) throws SQLException { + return (((com.mysql.jdbc.Connection) this.conn).versionMeetsMinimum(major, minor, subminor)); + } + + /** + * Checks whether the server we're connected to is a MySQL Community edition + */ + protected boolean isCommunityEdition() { + return Util.isCommunityEdition(((MySQLConnection) this.conn).getServerVersion()); + } + + /** + * Checks whether the server we're connected to is an MySQL Enterprise edition + */ + protected boolean isEnterpriseEdition() { + return Util.isEnterpriseEdition(((MySQLConnection) this.conn).getServerVersion()); + } + + protected boolean isClassAvailable(String classname) { + try { + Class.forName(classname); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + protected boolean isRunningOnJRockit() { + String vmVendor = System.getProperty("java.vm.vendor"); + + return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith("BEA")); + } + + protected String randomString() { + int length = (int) (Math.random() * 32); + + StringBuilder buf = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + buf.append((char) ((Math.random() * 26) + 'a')); + } + + return buf.toString(); + } + + protected void cleanupTempFiles(final File exampleTempFile, final String tempfilePrefix) { + + File tempfilePath = exampleTempFile.getParentFile(); + + File[] possibleFiles = tempfilePath.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return (name.indexOf(tempfilePrefix) != -1 && !exampleTempFile.getName().equals(name)); + } + }); + + if (possibleFiles != null) { + for (int i = 0; i < possibleFiles.length; i++) { + try { + possibleFiles[i].delete(); + } catch (Throwable t) { + // ignore, we're only making a best effort cleanup attempt here + } + } + } + } + + protected void assertResultSetLength(ResultSet rset, int len) throws Exception { + int oldRowPos = rset.getRow(); + rset.last(); + assertEquals("Result set length", len, rset.getRow()); + if (oldRowPos > 0) { + rset.absolute(oldRowPos); + } else { + rset.beforeFirst(); + } + } + + protected void assertResultSetsEqual(ResultSet control, ResultSet test) throws Exception { + int controlNumCols = control.getMetaData().getColumnCount(); + int testNumCols = test.getMetaData().getColumnCount(); + assertEquals(controlNumCols, testNumCols); + + StringBuilder rsAsString = new StringBuilder(); + + while (control.next()) { + test.next(); + rsAsString.append("\n"); + for (int i = 0; i < controlNumCols; i++) { + Object controlObj = control.getObject(i + 1); + Object testObj = test.getObject(i + 1); + + rsAsString.append("" + controlObj); + rsAsString.append("\t = \t"); + rsAsString.append("" + testObj); + rsAsString.append(", "); + + if (controlObj == null) { + assertNull("Expected null, see last row: \n" + rsAsString.toString(), testObj); + } else { + assertNotNull("Expected non-null, see last row: \n" + rsAsString.toString(), testObj); + } + + if (controlObj instanceof Float) { + assertEquals("Float comparison failed, see last row: \n" + rsAsString.toString(), ((Float) controlObj).floatValue(), + ((Float) testObj).floatValue(), 0.1); + } else if (controlObj instanceof Double) { + assertEquals("Double comparison failed, see last row: \n" + rsAsString.toString(), ((Double) controlObj).doubleValue(), + ((Double) testObj).doubleValue(), 0.1); + } else { + assertEquals("Value comparison failed, see last row: \n" + rsAsString.toString(), controlObj, testObj); + } + } + } + + int howMuchMore = 0; + + while (test.next()) { + rsAsString.append("\n"); + howMuchMore++; + for (int i = 0; i < controlNumCols; i++) { + rsAsString.append("\t = \t"); + rsAsString.append("" + test.getObject(i + 1)); + rsAsString.append(", "); + } + } + + assertTrue("Found " + howMuchMore + " extra rows in result set to be compared: ", howMuchMore == 0); + } + + protected static EX assertThrows(Class throwable, Callable testRoutine) { + try { + testRoutine.call(); + } catch (Throwable t) { + if (!throwable.isAssignableFrom(t.getClass())) { + fail("Expected exception of type '" + throwable.getName() + "' but instead a exception of type '" + t.getClass().getName() + "' was thrown."); + } + + return throwable.cast(t); + } + fail("Expected exception of type '" + throwable.getName() + "'."); + + // never reaches here + return null; + } + + protected static EX assertThrows(Class throwable, String msgMatchesRegex, Callable testRoutine) { + try { + testRoutine.call(); + } catch (Throwable t) { + if (!throwable.isAssignableFrom(t.getClass())) { + fail("Expected exception of type '" + throwable.getName() + "' but instead a exception of type '" + t.getClass().getName() + "' was thrown."); + } + + if (!t.getMessage().matches(msgMatchesRegex)) { + fail("The error message «" + t.getMessage() + "» was expected to match «" + msgMatchesRegex + "»."); + } + + return throwable.cast(t); + } + fail("Expected exception of type '" + throwable.getName() + "'."); + + // never reaches here + return null; + } + + protected void assertByteArrayEquals(String message, byte[] expected, byte[] actual) { + assertEquals(message + " - array lenght", expected.length, actual.length); + for (int i = 0, s = expected.length; i < s; i++) { + assertEquals(message + " - element at " + i, expected[i], actual[i]); + } + } + + /** + * Asserts the most recent history of connection attempts from the global data in UnreliableSocketFactory. + * + * @param expectedConnectionsHistory + * The list of expected events. Use UnreliableSocketFactory.getHostConnectedStatus(String), UnreliableSocketFactory.getHostFailedStatus(String) + * and UnreliableSocketFactory.getHostUnknownStatus(String) to build proper syntax for host+status identification. + */ + protected static void assertConnectionsHistory(String... expectedConnectionsHistory) { + List actualConnectionsHistory = UnreliableSocketFactory.getHostsFromLastConnections(expectedConnectionsHistory.length); + + int i = 1; + String delimiter = ""; + StringBuilder expectedHist = new StringBuilder(""); + for (String hostInfo : expectedConnectionsHistory) { + expectedHist.append(delimiter).append(i++).append(hostInfo); + delimiter = " ~ "; + } + + i = 1; + delimiter = ""; + StringBuilder actualHist = new StringBuilder(""); + for (String hostInfo : actualConnectionsHistory) { + actualHist.append(delimiter).append(i++).append(hostInfo); + delimiter = " ~ "; + } + + assertEquals("Connections history", expectedHist.toString(), actualHist.toString()); + } + + /* + * Set default values for primitives. (prevents NPE in Java 1.4 when calling via reflection) + */ + protected void fillPrimitiveDefaults(Class types[], Object vals[], int count) { + for (int i = 0; i < count; ++i) { + if (vals[i] != null) { + continue; + } + String type = types[i].toString(); + if (type.equals("short")) { + vals[i] = new Short((short) 0); + } else if (type.equals("int")) { + vals[i] = new Integer(0); + } else if (type.equals("long")) { + vals[i] = new Long(0); + } else if (type.equals("boolean")) { + vals[i] = new Boolean(false); + } else if (type.equals("byte")) { + vals[i] = new Byte((byte) 0); + } else if (type.equals("double")) { + vals[i] = new Double(0.0); + } else if (type.equals("float")) { + vals[i] = new Float(0.0); + } + } + } + + /** + * Retrieve the current system time in milliseconds, using the nanosecond + * time if possible. + */ + protected static final long currentTimeMillis() { + try { + Method mNanoTime = System.class.getDeclaredMethod("nanoTime", (Class[]) null); + return ((Long) mNanoTime.invoke(null, (Object[]) null)).longValue() / 1000000; + } catch (Exception ex) { + return System.currentTimeMillis(); + } + } + + protected Connection getFailoverConnection() throws SQLException { + return getFailoverConnection(null); + } + + protected Connection getFailoverConnection(Properties props) throws SQLException { + return DriverManager.getConnection(getMasterSlaveUrl(), getHostFreePropertiesFromTestsuiteUrl(props)); + } + + protected Connection getMasterSlaveReplicationConnection() throws SQLException { + return getMasterSlaveReplicationConnection(null); + } + + protected Connection getMasterSlaveReplicationConnection(Properties props) throws SQLException { + return new ReplicationDriver().connect(getMasterSlaveUrl(), getHostFreePropertiesFromTestsuiteUrl(props)); + } + + protected String getMasterSlaveUrl() throws SQLException { + Properties defaultProps = getPropertiesFromTestsuiteUrl(); + String hostname = defaultProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + + if (NonRegisteringDriver.isHostPropertiesList(hostname)) { + String url = String.format("jdbc:mysql://%s,%s/", hostname, hostname); + + return url; + } + + StringBuilder urlBuf = new StringBuilder("jdbc:mysql://"); + + String portNumber = defaultProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + hostname = (hostname == null ? "localhost" : hostname); + + for (int i = 0; i < 2; i++) { + urlBuf.append(hostname); + urlBuf.append(":"); + urlBuf.append(portNumber); + + if (i == 0) { + urlBuf.append(","); + } + } + urlBuf.append("/"); + + return urlBuf.toString(); + } + + protected Connection getLoadBalancedConnection(int customHostLocation, String customHost, Properties props) throws SQLException { + Properties parsedProps = new NonRegisteringDriver().parseURL(dbUrl, null); + + String defaultHost = parsedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + + if (!NonRegisteringDriver.isHostPropertiesList(defaultHost)) { + String port = parsedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + defaultHost = defaultHost + ":" + port; + } + removeHostRelatedProps(parsedProps); + + if (customHost != null && customHost.length() > 0) { + customHost = customHost + ","; + } else { + customHost = ""; + } + + String hostsString = null; + + switch (customHostLocation) { + case 1: + hostsString = customHost + defaultHost; + break; + case 2: + hostsString = defaultHost + "," + customHost + defaultHost; + break; + case 3: + hostsString = defaultHost + "," + customHost; + hostsString = hostsString.substring(0, hostsString.length() - 1); + break; + default: + throw new IllegalArgumentException(); + } + + if (props != null) { + parsedProps.putAll(props); + } + + Connection lbConn = DriverManager.getConnection("jdbc:mysql:loadbalance://" + hostsString, parsedProps); + + return lbConn; + } + + protected Connection getLoadBalancedConnection() throws SQLException { + return getLoadBalancedConnection(1, "", null); + } + + protected Connection getLoadBalancedConnection(Properties props) throws SQLException { + return getLoadBalancedConnection(1, "", props); + } + + protected String getPort(Properties props, NonRegisteringDriver d) throws SQLException { + String port = d.parseURL(BaseTestCase.dbUrl, props).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + if (port == null) { + port = "3306"; + } + return port; + } + + protected String getPortFreeHostname(Properties props, NonRegisteringDriver d) throws SQLException { + String host = d.parseURL(BaseTestCase.dbUrl, props).getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + + if (host == null) { + host = "localhost"; + } + + host = host.split(":")[0]; + return host; + } + + protected Connection getUnreliableMultiHostConnection(String haMode, String[] hostNames, Properties props, Set downedHosts) throws Exception { + if (downedHosts == null) { + downedHosts = new HashSet(); + } + + NonRegisteringDriver driver = new NonRegisteringDriver(); + + props = getHostFreePropertiesFromTestsuiteUrl(props); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties parsedProps = driver.parseURL(BaseTestCase.dbUrl, props); + String db = parsedProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + String port = parsedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + String host = getPortFreeHostname(parsedProps, driver); + + UnreliableSocketFactory.flushAllStaticData(); + + StringBuilder hostString = new StringBuilder(); + String delimiter = ""; + for (String hostName : hostNames) { + UnreliableSocketFactory.mapHost(hostName, host); + hostString.append(delimiter); + delimiter = ","; + hostString.append(hostName + ":" + (port == null ? "3306" : port)); + + if (downedHosts.contains(hostName)) { + UnreliableSocketFactory.downHost(hostName); + } + } + + if (haMode == null) { + haMode = ""; + } else if (haMode.length() > 0) { + haMode += ":"; + } + + return getConnectionWithProps("jdbc:mysql:" + haMode + "//" + hostString.toString() + "/" + db, props); + } + + protected Connection getUnreliableFailoverConnection(String[] hostNames, Properties props) throws Exception { + return getUnreliableFailoverConnection(hostNames, props, null); + } + + protected Connection getUnreliableFailoverConnection(String[] hostNames, Properties props, Set downedHosts) throws Exception { + return getUnreliableMultiHostConnection(null, hostNames, props, downedHosts); + } + + protected Connection getUnreliableLoadBalancedConnection(String[] hostNames, Properties props) throws Exception { + return getUnreliableLoadBalancedConnection(hostNames, props, null); + } + + protected Connection getUnreliableLoadBalancedConnection(String[] hostNames, Properties props, Set downedHosts) throws Exception { + return getUnreliableMultiHostConnection("loadbalance", hostNames, props, downedHosts); + } + + protected ReplicationConnection getUnreliableReplicationConnection(String[] hostNames, Properties props) throws Exception { + return getUnreliableReplicationConnection(hostNames, props, null); + } + + protected ReplicationConnection getUnreliableReplicationConnection(String[] hostNames, Properties props, Set downedHosts) throws Exception { + return (ReplicationConnection) getUnreliableMultiHostConnection("replication", hostNames, props, downedHosts); + } + + public static class MockConnectionConfiguration { + String hostName; + String port; + String serverType; + boolean isDowned = false; + + public MockConnectionConfiguration(String hostName, String serverType, String port, boolean isDowned) { + this.hostName = hostName; + this.serverType = serverType; + this.isDowned = isDowned; + this.port = port; + } + + public String getAddress(boolean withTrailingPort) { + return "address=(protocol=tcp)(host=" + this.hostName + ")(port=" + this.port + ")(type=" + this.serverType + ")" + + (withTrailingPort ? (":" + this.port) : ""); + } + + public String getAddress() { + return getAddress(false); + } + } + + protected ReplicationConnection getUnreliableReplicationConnection(Set configs, Properties props) throws Exception { + NonRegisteringDriver d = new NonRegisteringDriver(); + props = getHostFreePropertiesFromTestsuiteUrl(props); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties parsed = d.parseURL(BaseTestCase.dbUrl, props); + String db = parsed.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + String port = parsed.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + String host = getPortFreeHostname(parsed, d); + + UnreliableSocketFactory.flushAllStaticData(); + + StringBuilder hostString = new StringBuilder(); + String glue = ""; + for (MockConnectionConfiguration config : configs) { + UnreliableSocketFactory.mapHost(config.hostName, host); + hostString.append(glue); + glue = ","; + if (config.port == null) { + config.port = (port == null ? "3306" : port); + } + hostString.append(config.getAddress()); + if (config.isDowned) { + UnreliableSocketFactory.downHost(config.hostName); + } + } + + return (ReplicationConnection) getConnectionWithProps("jdbc:mysql:replication://" + hostString.toString() + "/" + db, props); + } + + protected boolean assertEqualsFSAware(String matchStr, String inStr) throws Exception { + if (this.isOnCSFS) { + return matchStr.equals(inStr); + } + return matchStr.equalsIgnoreCase(inStr); + } + + protected String removeSqlMode(String mode, String fromStr) throws Exception { + String res = fromStr; + if (res != null && mode != null) { + res = res.replaceFirst("'" + mode + "'", "").replaceFirst(mode, "").replaceFirst(",,", ","); + } + return res; + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/UnreliableSocketFactory.java b/mysql-connector-java-5.1.40/src/testsuite/UnreliableSocketFactory.java new file mode 100644 index 0000000..0d3e325 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/UnreliableSocketFactory.java @@ -0,0 +1,573 @@ +/* + Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.SocketFactory; +import com.mysql.jdbc.StandardSocketFactory; + +/** + * Configure "socketFactory" to use this class in your JDBC URL, and it will operate as normal, unless you map some host aliases to actual IP addresses, and + * then have the test driver call hangOnConnect/Read/Write() which simulate the given failure condition for the host with the alias argument, and will + * honor connect or socket timeout properties. + * + * You can also cause a host to be immediately-downed by calling downHost() with an alias. + * + * ATTENTION! This class is *NOT* thread safe. + */ +public class UnreliableSocketFactory extends StandardSocketFactory { + public static final String STATUS_UNKNOWN = "?"; + public static final String STATUS_CONNECTED = "/"; + public static final String STATUS_FAILED = "\\"; + + public static final long DEFAULT_TIMEOUT_MILLIS = 10 * 60 * 1000; // ugh + + private static final Map MAPPED_HOSTS = new HashMap(); + static final Set HUNG_READ_HOSTS = new HashSet(); + static final Set HUNG_WRITE_HOSTS = new HashSet(); + static final Set HUNG_CONNECT_HOSTS = new HashSet(); + static final Set IMMEDIATELY_DOWNED_HOSTS = new HashSet(); + static final List CONNECTION_ATTEMPTS = new LinkedList(); + + private String hostname; + private int portNumber; + private Properties props; + + public static String getHostConnectedStatus(String host) { + return STATUS_CONNECTED + host; + } + + public static String getHostFailedStatus(String host) { + return STATUS_FAILED + host; + } + + public static String getHostUnknownStatus(String host) { + return STATUS_FAILED + host; + } + + public static void flushAllStaticData() { + IMMEDIATELY_DOWNED_HOSTS.clear(); + HUNG_CONNECT_HOSTS.clear(); + HUNG_READ_HOSTS.clear(); + HUNG_WRITE_HOSTS.clear(); + flushConnectionAttempts(); + } + + public static void flushConnectionAttempts() { + CONNECTION_ATTEMPTS.clear(); + } + + public static void mapHost(String alias, String orig) { + MAPPED_HOSTS.put(alias, orig); + } + + public static void hangOnRead(String hostname) { + HUNG_READ_HOSTS.add(hostname); + } + + public static void dontHangOnRead(String hostname) { + HUNG_READ_HOSTS.remove(hostname); + } + + public static void hangOnWrite(String hostname) { + HUNG_WRITE_HOSTS.add(hostname); + } + + public static void dontHangOnWrite(String hostname) { + HUNG_WRITE_HOSTS.remove(hostname); + } + + public static void hangOnConnect(String hostname) { + HUNG_CONNECT_HOSTS.add(hostname); + } + + public static void dontHangOnConnect(String hostname) { + HUNG_CONNECT_HOSTS.remove(hostname); + } + + public static void downHost(String hostname) { + IMMEDIATELY_DOWNED_HOSTS.add(hostname); + + } + + public static void dontDownHost(String hostname) { + IMMEDIATELY_DOWNED_HOSTS.remove(hostname); + } + + public static String getHostFromLastConnection() { + return getHostFromPastConnection(1); + } + + public static String getHostFromPastConnection(int pos) { + pos = Math.abs(pos); + if (pos == 0 || CONNECTION_ATTEMPTS.isEmpty() || CONNECTION_ATTEMPTS.size() < pos) { + return null; + } + return CONNECTION_ATTEMPTS.get(CONNECTION_ATTEMPTS.size() - pos); + } + + public static List getHostsFromAllConnections() { + return getHostsFromLastConnections(CONNECTION_ATTEMPTS.size()); + } + + public static List getHostsFromLastConnections(int count) { + count = Math.abs(count); + int lBound = Math.max(0, CONNECTION_ATTEMPTS.size() - count); + return CONNECTION_ATTEMPTS.subList(lBound, CONNECTION_ATTEMPTS.size()); + } + + public static boolean isConnected() { + String lastHost = getHostFromLastConnection(); + return lastHost == null ? false : lastHost.startsWith(STATUS_CONNECTED); + } + + @Override + public Socket connect(String host_name, int port_number, Properties prop) throws SocketException, IOException { + this.hostname = host_name; + this.portNumber = port_number; + this.props = prop; + + Socket socket = null; + String result = STATUS_UNKNOWN; + try { + socket = getNewSocket(); + result = STATUS_CONNECTED; + } catch (SocketException e) { + result = STATUS_FAILED; + throw e; + } catch (IOException e) { + result = STATUS_FAILED; + throw e; + } finally { + CONNECTION_ATTEMPTS.add(result + host_name); + } + return socket; + } + + private Socket getNewSocket() throws SocketException, IOException { + if (IMMEDIATELY_DOWNED_HOSTS.contains(this.hostname)) { + sleepMillisForProperty(this.props, "connectTimeout"); + + throw new SocketTimeoutException(); + } + + String hostnameToConnectTo = MAPPED_HOSTS.get(this.hostname); + + if (hostnameToConnectTo == null) { + hostnameToConnectTo = this.hostname; + } + + if (NonRegisteringDriver.isHostPropertiesList(hostnameToConnectTo)) { + Properties hostSpecificProps = NonRegisteringDriver.expandHostKeyValues(hostnameToConnectTo); + + String protocol = hostSpecificProps.getProperty(NonRegisteringDriver.PROTOCOL_PROPERTY_KEY); + + if ("unix".equalsIgnoreCase(protocol)) { + SocketFactory factory; + try { + factory = (SocketFactory) Class.forName("org.newsclub.net.mysql.AFUNIXDatabaseSocketFactory").newInstance(); + } catch (InstantiationException e) { + throw new SocketException(e.getMessage()); + } catch (IllegalAccessException e) { + throw new SocketException(e.getMessage()); + } catch (ClassNotFoundException e) { + throw new SocketException(e.getMessage()); + } + + String path = hostSpecificProps.getProperty(NonRegisteringDriver.PATH_PROPERTY_KEY); + + if (path != null) { + hostSpecificProps.setProperty("junixsocket.file", path); + } + + return new HangingSocket(factory.connect(hostnameToConnectTo, this.portNumber, hostSpecificProps), this.props, this.hostname); + } + + } + + return new HangingSocket(super.connect(hostnameToConnectTo, this.portNumber, this.props), this.props, this.hostname); + } + + @Override + public Socket afterHandshake() throws SocketException, IOException { + return getNewSocket(); + } + + @Override + public Socket beforeHandshake() throws SocketException, IOException { + return getNewSocket(); + } + + static void sleepMillisForProperty(Properties props, String name) { + try { + Thread.sleep(Long.parseLong(props.getProperty(name, String.valueOf(DEFAULT_TIMEOUT_MILLIS)))); + } catch (NumberFormatException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + // ignore + } + } + + class HangingSocket extends Socket { + @Override + public void bind(SocketAddress bindpoint) throws IOException { + this.underlyingSocket.bind(bindpoint); + } + + @Override + public synchronized void close() throws IOException { + this.underlyingSocket.close(); + } + + @Override + public SocketChannel getChannel() { + return this.underlyingSocket.getChannel(); + } + + @Override + public InetAddress getInetAddress() { + return this.underlyingSocket.getInetAddress(); + } + + @Override + public InputStream getInputStream() throws IOException { + return new HangingInputStream(this.underlyingSocket.getInputStream(), this.props, this.aliasedHostname); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return this.underlyingSocket.getKeepAlive(); + } + + @Override + public InetAddress getLocalAddress() { + return this.underlyingSocket.getLocalAddress(); + } + + @Override + public int getLocalPort() { + return this.underlyingSocket.getLocalPort(); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return this.underlyingSocket.getLocalSocketAddress(); + } + + @Override + public boolean getOOBInline() throws SocketException { + return this.underlyingSocket.getOOBInline(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return new HangingOutputStream(this.underlyingSocket.getOutputStream(), this.props, this.aliasedHostname); + } + + @Override + public int getPort() { + return this.underlyingSocket.getPort(); + } + + @Override + public synchronized int getReceiveBufferSize() throws SocketException { + return this.underlyingSocket.getReceiveBufferSize(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return this.underlyingSocket.getRemoteSocketAddress(); + } + + @Override + public boolean getReuseAddress() throws SocketException { + return this.underlyingSocket.getReuseAddress(); + } + + @Override + public synchronized int getSendBufferSize() throws SocketException { + return this.underlyingSocket.getSendBufferSize(); + } + + @Override + public int getSoLinger() throws SocketException { + return this.underlyingSocket.getSoLinger(); + } + + @Override + public synchronized int getSoTimeout() throws SocketException { + return this.underlyingSocket.getSoTimeout(); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return this.underlyingSocket.getTcpNoDelay(); + } + + @Override + public int getTrafficClass() throws SocketException { + return this.underlyingSocket.getTrafficClass(); + } + + @Override + public boolean isBound() { + return this.underlyingSocket.isBound(); + } + + @Override + public boolean isClosed() { + return this.underlyingSocket.isClosed(); + } + + @Override + public boolean isConnected() { + return this.underlyingSocket.isConnected(); + } + + @Override + public boolean isInputShutdown() { + return this.underlyingSocket.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return this.underlyingSocket.isOutputShutdown(); + } + + @Override + public void sendUrgentData(int data) throws IOException { + this.underlyingSocket.sendUrgentData(data); + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + this.underlyingSocket.setKeepAlive(on); + } + + @Override + public void setOOBInline(boolean on) throws SocketException { + this.underlyingSocket.setOOBInline(on); + } + + @Override + public synchronized void setReceiveBufferSize(int size) throws SocketException { + this.underlyingSocket.setReceiveBufferSize(size); + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + this.underlyingSocket.setReuseAddress(on); + } + + @Override + public synchronized void setSendBufferSize(int size) throws SocketException { + this.underlyingSocket.setSendBufferSize(size); + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + this.underlyingSocket.setSoLinger(on, linger); + } + + @Override + public synchronized void setSoTimeout(int timeout) throws SocketException { + this.underlyingSocket.setSoTimeout(timeout); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + this.underlyingSocket.setTcpNoDelay(on); + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + this.underlyingSocket.setTrafficClass(tc); + } + + @Override + public void shutdownInput() throws IOException { + this.underlyingSocket.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + this.underlyingSocket.shutdownOutput(); + } + + @Override + public String toString() { + return this.underlyingSocket.toString(); + } + + final Socket underlyingSocket; + final Properties props; + final String aliasedHostname; + + HangingSocket(Socket realSocket, Properties props, String aliasedHostname) { + this.underlyingSocket = realSocket; + this.props = props; + this.aliasedHostname = aliasedHostname; + } + } + + static class HangingInputStream extends InputStream { + final InputStream underlyingInputStream; + final Properties props; + final String aliasedHostname; + + HangingInputStream(InputStream realInputStream, Properties props, String aliasedHostname) { + this.underlyingInputStream = realInputStream; + this.props = props; + this.aliasedHostname = aliasedHostname; + } + + @Override + public int available() throws IOException { + return this.underlyingInputStream.available(); + } + + @Override + public void close() throws IOException { + this.underlyingInputStream.close(); + } + + @Override + public synchronized void mark(int readlimit) { + this.underlyingInputStream.mark(readlimit); + } + + @Override + public boolean markSupported() { + return this.underlyingInputStream.markSupported(); + } + + @Override + public synchronized void reset() throws IOException { + this.underlyingInputStream.reset(); + } + + @Override + public long skip(long n) throws IOException { + failIfRequired(); + + return this.underlyingInputStream.skip(n); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + failIfRequired(); + + return this.underlyingInputStream.read(b, off, len); + } + + @Override + public int read(byte[] b) throws IOException { + failIfRequired(); + + return this.underlyingInputStream.read(b); + } + + @Override + public int read() throws IOException { + failIfRequired(); + + return this.underlyingInputStream.read(); + } + + private void failIfRequired() throws SocketTimeoutException { + if (HUNG_READ_HOSTS.contains(this.aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(this.aliasedHostname)) { + sleepMillisForProperty(this.props, "socketTimeout"); + + throw new SocketTimeoutException(); + } + } + } + + static class HangingOutputStream extends OutputStream { + + final Properties props; + final String aliasedHostname; + final OutputStream underlyingOutputStream; + + HangingOutputStream(OutputStream realOutputStream, Properties props, String aliasedHostname) { + this.underlyingOutputStream = realOutputStream; + this.props = props; + this.aliasedHostname = aliasedHostname; + } + + @Override + public void close() throws IOException { + failIfRequired(); + this.underlyingOutputStream.close(); + } + + @Override + public void flush() throws IOException { + this.underlyingOutputStream.flush(); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + failIfRequired(); + this.underlyingOutputStream.write(b, off, len); + } + + @Override + public void write(byte[] b) throws IOException { + failIfRequired(); + this.underlyingOutputStream.write(b); + } + + @Override + public void write(int b) throws IOException { + failIfRequired(); + this.underlyingOutputStream.write(b); + } + + private void failIfRequired() throws SocketTimeoutException { + if (HUNG_WRITE_HOSTS.contains(this.aliasedHostname) || IMMEDIATELY_DOWNED_HOSTS.contains(this.aliasedHostname)) { + sleepMillisForProperty(this.props, "socketTimeout"); + + throw new SocketTimeoutException(); + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/BaseFabricTestCase.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/BaseFabricTestCase.java new file mode 100644 index 0000000..579509e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/BaseFabricTestCase.java @@ -0,0 +1,86 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import junit.framework.TestCase; + +public abstract class BaseFabricTestCase extends TestCase { + protected String hostname = System.getProperty("com.mysql.fabric.testsuite.hostname"); + protected String portString = System.getProperty("com.mysql.fabric.testsuite.port"); + protected String fabricUsername = System.getProperty("com.mysql.fabric.testsuite.fabricUsername"); + protected String fabricPassword = System.getProperty("com.mysql.fabric.testsuite.fabricPassword"); + protected String username = System.getProperty("com.mysql.fabric.testsuite.username"); + protected String password = System.getProperty("com.mysql.fabric.testsuite.password"); + protected String database = System.getProperty("com.mysql.fabric.testsuite.database"); + + protected String globalHost = System.getProperty("com.mysql.fabric.testsuite.global.host"); + protected String globalPort = System.getProperty("com.mysql.fabric.testsuite.global.port"); + protected String shard1Host = System.getProperty("com.mysql.fabric.testsuite.shard1.host"); + protected String shard1Port = System.getProperty("com.mysql.fabric.testsuite.shard1.port"); + protected String shard2Host = System.getProperty("com.mysql.fabric.testsuite.shard2.host"); + protected String shard2Port = System.getProperty("com.mysql.fabric.testsuite.shard2.port"); + + protected int port = 0; + protected String fabricUrl; + protected String baseJdbcUrl; + + protected boolean isSetForFabricTest = false; + + public BaseFabricTestCase() throws Exception { + super(); + + this.isSetForFabricTest = (this.hostname != null && this.hostname.trim().length() > 0) + || (this.portString != null && this.portString.trim().length() > 0) || (this.fabricUsername != null && this.fabricUsername.trim().length() > 0) + || (this.fabricPassword != null && this.fabricPassword.trim().length() > 0) || (this.username != null && this.username.trim().length() > 0) + || (this.password != null && this.password.trim().length() > 0) || (this.database != null && this.database.trim().length() > 0) + || (this.globalHost != null && this.globalHost.trim().length() > 0) || (this.globalPort != null && this.globalPort.trim().length() > 0) + || (this.shard1Host != null && this.shard1Host.trim().length() > 0) || (this.shard1Port != null && this.shard1Port.trim().length() > 0) + || (this.shard2Host != null && this.shard2Host.trim().length() > 0) || (this.shard2Port != null && this.shard2Port.trim().length() > 0); + + if (this.isSetForFabricTest) { + Class.forName("com.mysql.fabric.jdbc.FabricMySQLDriver"); + + if (this.portString != null && this.portString.trim().length() > 0) { + this.port = Integer.valueOf(this.portString); + } + + this.fabricUrl = "http://" + this.hostname + ":" + this.port; + this.baseJdbcUrl = "jdbc:mysql:fabric://" + this.hostname + ":" + this.port + "/" + this.database; + this.baseJdbcUrl = this.baseJdbcUrl + "?fabricUsername=" + this.fabricUsername + "&fabricPassword=" + this.fabricPassword; + } + + } + + protected FabricMySQLDataSource getNewDefaultDataSource() { + FabricMySQLDataSource ds = new FabricMySQLDataSource(); + ds.setServerName(this.hostname); + ds.setPort(this.port); + ds.setDatabaseName(this.database); + ds.setFabricUsername(this.fabricUsername); + ds.setFabricPassword(this.fabricPassword); + return ds; + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/SetupFabricTestsuite.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/SetupFabricTestsuite.java new file mode 100644 index 0000000..fa29545 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/SetupFabricTestsuite.java @@ -0,0 +1,51 @@ +/* + Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; + +public class SetupFabricTestsuite { + + public static void main(String args[]) throws Exception { + String hostname = System.getProperty("com.mysql.fabric.testsuite.global.host"); + String port = System.getProperty("com.mysql.fabric.testsuite.global.port"); + String username = System.getProperty("com.mysql.fabric.testsuite.username"); + String password = System.getProperty("com.mysql.fabric.testsuite.password"); + + // Load the driver if running under Java 5 + if (!com.mysql.jdbc.Util.isJdbc4()) { + Class.forName("com.mysql.jdbc.Driver"); + } + + // Create database employees + Connection c = DriverManager.getConnection("jdbc:mysql://" + hostname + ":" + port + "/mysql", username, password); + Statement statement = c.createStatement(); + statement.executeUpdate("create database if not exists employees"); + statement.close(); + c.close(); + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/TestAdminCommands.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestAdminCommands.java new file mode 100644 index 0000000..74c4ddf --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestAdminCommands.java @@ -0,0 +1,61 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import com.mysql.fabric.FabricCommunicationException; +import com.mysql.fabric.proto.xmlrpc.XmlRpcClient; + +/** + * Tests for admin commands. + */ +public class TestAdminCommands extends BaseFabricTestCase { + + private XmlRpcClient client; + + public TestAdminCommands() throws Exception { + super(); + } + + @Override + public void setUp() throws Exception { + if (this.isSetForFabricTest) { + this.client = new XmlRpcClient(this.fabricUrl, this.fabricUsername, this.fabricPassword); + } + } + + public void testCreateGroup() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + String testGroupName = "CJ-testGroupName"; + try { + this.client.destroyGroup(testGroupName); + } catch (FabricCommunicationException ex) { + } + + this.client.createGroup(testGroupName); + // will throw an exception if the group wasn't created + this.client.destroyGroup(testGroupName); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/TestDumpCommands.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestDumpCommands.java new file mode 100644 index 0000000..21966fe --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestDumpCommands.java @@ -0,0 +1,121 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import com.mysql.fabric.Server; +import com.mysql.fabric.ServerGroup; +import com.mysql.fabric.ServerMode; +import com.mysql.fabric.ServerRole; +import com.mysql.fabric.ShardMapping; +import com.mysql.fabric.proto.xmlrpc.XmlRpcClient; + +/** + * Tests for `mysqlfabric dump.*'. + * + * Depends on standard Fabric test setup (which as of yet is not defined). + * Updates to this test setup will require changes to the tests. + */ +public class TestDumpCommands extends BaseFabricTestCase { + + private XmlRpcClient client; + + public TestDumpCommands() throws Exception { + super(); + } + + @Override + public void setUp() throws Exception { + if (this.isSetForFabricTest) { + this.client = new XmlRpcClient(this.fabricUrl, this.fabricUsername, this.fabricPassword); + } + } + + public static Comparator serverHostnamePortSorter = new Comparator() { + public int compare(Server s1, Server s2) { + int l; + l = s1.getHostname().compareTo(s2.getHostname()); + if (l != 0) { + return l; + } + + l = ((Integer) s1.getPort()).compareTo(s2.getPort()); + return l; + } + }; + + /** + * Test the Client.getServers() without a match pattern (all servers). + */ + public void testDumpServersAll() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + Set serverGroups = this.client.getServerGroups().getData(); + List servers = new ArrayList(); + for (ServerGroup g : serverGroups) { + servers.addAll(g.getServers()); + } + Collections.sort(servers, serverHostnamePortSorter); + assertEquals(3, servers.size()); + + assertEquals("fabric_test1_global", servers.get(0).getGroupName()); + assertEquals("fabric_test1_shard1", servers.get(1).getGroupName()); + assertEquals("fabric_test1_shard2", servers.get(2).getGroupName()); + + assertEquals((this.globalHost != null ? this.globalHost : "127.0.0.1"), servers.get(0).getHostname()); + assertEquals((this.shard1Host != null ? this.shard1Host : "127.0.0.1"), servers.get(1).getHostname()); + assertEquals((this.shard2Host != null ? this.shard2Host : "127.0.0.1"), servers.get(2).getHostname()); + + assertEquals((this.globalPort != null ? Integer.valueOf(this.globalPort) : 3401), servers.get(0).getPort()); + assertEquals((this.shard1Port != null ? Integer.valueOf(this.shard1Port) : 3402), servers.get(1).getPort()); + assertEquals((this.shard2Port != null ? Integer.valueOf(this.shard2Port) : 3403), servers.get(2).getPort()); + + assertEquals(ServerMode.READ_WRITE, servers.get(0).getMode()); + assertEquals(ServerRole.PRIMARY, servers.get(0).getRole()); + assertEquals(ServerMode.READ_WRITE, servers.get(1).getMode()); + assertEquals(ServerRole.PRIMARY, servers.get(1).getRole()); + assertEquals(ServerMode.READ_WRITE, servers.get(2).getMode()); + assertEquals(ServerRole.PRIMARY, servers.get(2).getRole()); + } + + /** + * Test the Client.getShardMaps() without a match pattern (all maps). + */ + public void testDumpShardMapsAll() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + Set shardMappings = this.client.getShardMappings().getData(); + assertEquals(1, shardMappings.size()); + ShardMapping shardMapping = shardMappings.iterator().next(); + + assertEquals(1, shardMapping.getShardTables().size()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/TestResultSetParser.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestResultSetParser.java new file mode 100644 index 0000000..e71684d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestResultSetParser.java @@ -0,0 +1,98 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mysql.fabric.proto.xmlrpc.ResultSetParser; + +import junit.framework.TestCase; + +/** + * Tests for Fabric XML-RPC ResultSetParser. + */ +public class TestResultSetParser extends TestCase { + // Example response data represented in tests: + // [1, 5ca1ab1e-a007-feed-f00d-cab3fe13249e, 0, , + // [ + // {rows= + // [[5e26a7ab-de84-11e2-a885-df73a3d95316, fabric_test1_global, 127.0.0.1, 3401, 3, 3, 1.0], + // [07eee140-d466-11e3-abdf-dfb2de41aa92, fabric_test1_shard1, 127.0.0.1, 3402, 1, 2, 1.0]], + // info={names=[server_uuid, group_id, host, port, mode, status, weight]}} + // ]] + List> exampleServersResultSet; + + public TestResultSetParser(String name) { + super(name); + } + + @SuppressWarnings("unchecked") + @Override + public void setUp() throws Exception { + final Map columns = new HashMap>() { + private static final long serialVersionUID = 1L; + + { + put("names", Arrays.asList(new String[] { "server_uuid", "group_id", "host", "port", "mode", "status", "weight" })); + } + }; + final List row1 = Arrays.asList(new Object[] { "5e26a7ab-de84-11e2-a885-df73a3d95316", "fabric_test1_global", "127.0.0.1", 3401, 3, 3, 1.0 }); + final List row2 = Arrays.asList(new Object[] { "07eee140-d466-11e3-abdf-dfb2de41aa92", "fabric_test1_shard1", "127.0.0.1", 3402, 1, 2, 1.0 }); + final List rows = Arrays.asList(new List[] { row1, row2 }); + Map resultData = new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("info", columns); + put("rows", rows); + } + }; + this.exampleServersResultSet = new ResultSetParser().parse((Map) resultData.get("info"), (List>) resultData.get("rows")); + } + + public void testExampleData() throws Exception { + Map row = this.exampleServersResultSet.get(0); + assertEquals("5e26a7ab-de84-11e2-a885-df73a3d95316", row.get("server_uuid")); + assertEquals("fabric_test1_global", row.get("group_id")); + assertEquals("127.0.0.1", row.get("host")); + assertEquals(3401, row.get("port")); + assertEquals(3, row.get("mode")); + assertEquals(3, row.get("status")); + assertEquals(1.0, row.get("weight")); + + row = this.exampleServersResultSet.get(1); + assertEquals("07eee140-d466-11e3-abdf-dfb2de41aa92", row.get("server_uuid")); + assertEquals("fabric_test1_shard1", row.get("group_id")); + assertEquals("127.0.0.1", row.get("host")); + assertEquals(3402, row.get("port")); + assertEquals(1, row.get("mode")); + assertEquals(2, row.get("status")); + assertEquals(1.0, row.get("weight")); + + assertEquals(2, this.exampleServersResultSet.size()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/TestShardMapping.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestShardMapping.java new file mode 100644 index 0000000..24a2675 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestShardMapping.java @@ -0,0 +1,123 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import java.util.HashSet; +import java.util.Set; + +import com.mysql.fabric.HashShardMapping; +import com.mysql.fabric.RangeShardMapping; +import com.mysql.fabric.ShardIndex; +import com.mysql.fabric.ShardMapping; +import com.mysql.fabric.ShardingType; + +import junit.framework.TestCase; + +/** + * Tests for shard mappings. + */ +public class TestShardMapping extends TestCase { + /** + * Test that a range based shard mapping looks up groups for keys correctly. + */ + public void testRangeShardMappingKeyLookup() throws Exception { + final String globalGroupName = "My global group"; + + final int lowerBounds[] = new int[] { 1, 10000, 1001, 400, 1000, 470 }; + + final int lowestLowerBound = 1; + + // setup the mapping + Set shardIndices = new HashSet(); + int shardId = 0; // shard id's added sequentially increasing from 0 + for (Integer lowerBound : lowerBounds) { + ShardIndex i = new ShardIndex(String.valueOf(lowerBound), shardId, "shard_group_" + shardId); + shardId++; + shardIndices.add(i); + } + ShardMapping mapping = new RangeShardMapping(5000, ShardingType.RANGE, globalGroupName, null, shardIndices); + + // try adding a second shard index with a lower bound that conflicts with an existing one this should be prohibited + // mapping.addShardIndex(new ShardIndex(mapping, "1", 0, "")); + + // test looking up a key out of range doesn't work + try { + mapping.getGroupNameForKey(String.valueOf(lowestLowerBound - 1)); + fail("Looking up a key with a value below the lowest bound is invalid"); + } catch (Exception ex) { + } + try { + mapping.getGroupNameForKey(String.valueOf(lowestLowerBound - 1000)); + fail("Looking up a key with a value below the lowest bound is invalid"); + } catch (Exception ex) { + } + + // test key lookups for lower bound values + for (shardId = 0; shardId < lowerBounds.length; ++shardId) { + int lowerBound = lowerBounds[shardId]; + String groupName = mapping.getGroupNameForKey(String.valueOf(lowerBound)); + assertEquals("Exact lookup for key " + lowerBound, "shard_group_" + shardId, groupName); + } + } + + public void testHashShardMappingKeyLookup() throws Exception { + final String globalGroupName = "My global group"; + + final String lowerBounds[] = new String[] { /* 0 = */"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", /* 1 = */"66666666666666666666666666666666", + /* 2 = */"2809A05A22A4A9C1882A580BCC0AD8A6", /* 3 = */"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" }; + + // setup the mapping + Set shardIndices = new HashSet(); + int shardId = 0; // shard id's added sequentially increasing from 0 + for (String lowerBound : lowerBounds) { + ShardIndex i = new ShardIndex(lowerBound, shardId, "server_group_" + shardId); + shardId++; + shardIndices.add(i); + } + ShardMapping mapping = new HashShardMapping(5000, ShardingType.HASH, globalGroupName, null, shardIndices); + + // test lookups mapping of test value to the group it maps to test values are hashed with MD5 and compared to lowerBounds values + String testPairs[][] = new String[][] { + // exact match should be in that shard + new String[] { "Jess", "server_group_2" }, // hash = 2809a05a22a4a9c1882a580bcc0ad8a6 + new String[] { "x", "server_group_1" }, // hash = 9dd4e461268c8034f5c8564e155c67a6 + new String[] { "X", "server_group_3" }, // hash = 02129bb861061d1a052c592e2dc6b383 + new String[] { "Y", "server_group_2" }, // hash = 57cec4137b614c87cb4e24a3d003a3e0 + new String[] { "g", "server_group_0" }, // hash = b2f5ff47436671b6e533d8dc3614845d + // leading zeroes + new String[] { "168", "server_group_3" }, // hash = 006f52e9102a8d3be2fe5614f42ba989 + }; + + for (String[] testPair : testPairs) { + String key = testPair[0]; + String serverGroup = testPair[1]; + assertEquals(serverGroup, mapping.getGroupNameForKey(key)); + } + + // test a random set of values. we should never return null + for (int i = 0; i < 1000; ++i) { + assertNotNull(mapping.getGroupNameForKey("" + i)); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/TestXmlRpcCore.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestXmlRpcCore.java new file mode 100644 index 0000000..454b5d3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/TestXmlRpcCore.java @@ -0,0 +1,127 @@ +/* + Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric; + +import java.util.Map; + +import com.mysql.fabric.proto.xmlrpc.DigestAuthentication; +import com.mysql.fabric.xmlrpc.Client; +import com.mysql.fabric.xmlrpc.base.MethodCall; +import com.mysql.fabric.xmlrpc.base.MethodResponse; +import com.mysql.fabric.xmlrpc.base.Param; +import com.mysql.fabric.xmlrpc.base.Params; +import com.mysql.fabric.xmlrpc.base.Value; + +public class TestXmlRpcCore extends BaseFabricTestCase { + + public TestXmlRpcCore() throws Exception { + super(); + } + + public void generateAuthHeaders(Client client) throws Exception { + if ("".equals(this.fabricUsername)) { + // no auth needed if no username + return; + } + + String authenticateHeader = DigestAuthentication.getChallengeHeader(this.fabricUrl); + + Map digestChallenge = DigestAuthentication.parseDigestChallenge(authenticateHeader); + + String authorizationHeader = DigestAuthentication.generateAuthorizationHeader(digestChallenge, this.fabricUsername, this.fabricPassword); + + client.setHeader("Authorization", authorizationHeader); + } + + public void testProtocol() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + MethodCall mc = new MethodCall(); + MethodResponse resp = null; + + Client client = new Client(this.fabricUrl); + + Params pms = new Params(); + pms.addParam(new Param(new Value(0))); + /* + * pms.addParam(new Param(new Value("abc"))); + * pms.addParam(new Param(new Value(false))); + * pms.addParam(new Param(new Value(-23.345D))); + * pms.addParam(new Param(new Value((GregorianCalendar) GregorianCalendar.getInstance()))); + * // TODO base64 + * + * Struct s2 = new Struct(); + * s2.addMember(new Member("mem 2.1", new Value("qq"))); + * s2.addMember(new Member("mem 2.2", new Value(22.22))); + * Struct s1 = new Struct(); + * s1.addMember(new Member("mem 1.1", new Value(false))); + * s1.addMember(new Member("mem 1.2", new Value(22))); + * s1.addMember(new Member("mem 1.3", new Value(s2))); + * pms.addParam(new Param(new Value(s1))); + * + * Array a = new Array(); + * a.addValue(new Value(true)); + * a.addValue(new Value(s1)); + * pms.addParam(new Param(new Value(a))); + */ + mc.setMethodName("dump.servers"); + mc.setParams(pms); + + generateAuthHeaders(client); + resp = client.execute(mc); + System.out.println("Servers: " + resp.toString()); + System.out.println(); + + mc = new MethodCall(); // to clean params + mc.setMethodName("sharding.list_definitions"); + generateAuthHeaders(client); + resp = client.execute(mc); + System.out.println("Definitions: " + resp.toString()); + System.out.println(); + + mc.setMethodName("dump.fabric_nodes"); + generateAuthHeaders(client); + resp = client.execute(mc); + System.out.println("Fabrics: " + resp.toString()); + System.out.println(); + + mc.setMethodName("group.lookup_groups"); + generateAuthHeaders(client); + resp = client.execute(mc); + System.out.println("Groups: " + resp.toString()); + System.out.println(); + + pms = new Params(); + pms.addParam(new Param(new Value("fabric_test1_global"))); + mc.setMethodName("group.lookup_servers"); + mc.setParams(pms); + generateAuthHeaders(client); + resp = client.execute(mc); + System.out.println("Servers: " + resp.toString()); + System.out.println(); + + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestBasicConnection.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestBasicConnection.java new file mode 100644 index 0000000..56a4a0a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestBasicConnection.java @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; + +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import testsuite.fabric.BaseFabricTestCase; + +public class TestBasicConnection extends BaseFabricTestCase { + + public TestBasicConnection() throws Exception { + super(); + } + + /** + * Test that we can make a connection with a URL, given a server group name + */ + public void testConnectionUrl() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + String url = this.baseJdbcUrl + "&fabricServerGroup=fabric_test1_global"; + Connection c = DriverManager.getConnection(url, this.username, this.password); + ResultSet rs = c.createStatement().executeQuery("select user()"); + rs.next(); + String userFromDb = rs.getString(1).split("@")[0]; + assertEquals(this.username, userFromDb); + rs.close(); + c.close(); + } + + /** + * Test that we can connect with the data source, given a server group name + */ + public void testConnectionDataSource() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + FabricMySQLDataSource ds = getNewDefaultDataSource(); + ds.setFabricServerGroup("fabric_test1_global"); + Connection c = ds.getConnection(this.username, this.password); + // same as above + ResultSet rs = c.createStatement().executeQuery("select user()"); + rs.next(); + String userFromDb = rs.getString(1).split("@")[0]; + assertEquals(this.username, userFromDb); + rs.close(); + c.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestFabricMySQLConnectionSharding.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestFabricMySQLConnectionSharding.java new file mode 100644 index 0000000..6bb6d27 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestFabricMySQLConnectionSharding.java @@ -0,0 +1,202 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric.jdbc; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mysql.fabric.jdbc.FabricMySQLConnection; +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import testsuite.fabric.BaseFabricTestCase; + +public class TestFabricMySQLConnectionSharding extends BaseFabricTestCase { + private FabricMySQLDataSource ds; + private FabricMySQLConnection conn; + + public TestFabricMySQLConnectionSharding() throws Exception { + super(); + if (this.isSetForFabricTest) { + this.ds = getNewDefaultDataSource(); + } + } + + @Override + public void setUp() throws Exception { + if (this.isSetForFabricTest) { + this.conn = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + } + } + + @Override + public void tearDown() throws Exception { + if (this.isSetForFabricTest) { + this.conn.close(); + } + } + + /** + * Test that we can create a table in the global group and see + * it in other groups. + */ + public void testGlobalTableCreation() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + // get to the global group + this.conn.setServerGroupName("fabric_test1_global"); + assertEquals("fabric_test1_global", this.conn.getCurrentServerGroup().getName()); + + // create the table + Statement stmt = this.conn.createStatement(); + stmt.executeUpdate("drop table if exists testGlobalTableCreation"); + stmt.executeUpdate("create table testGlobalTableCreation (x int)"); + stmt.executeUpdate("insert into testGlobalTableCreation values (999), (752)"); + stmt.close(); + + // we have to wait for replication. This is not reliable + // because replication can take an arbitrary amount of time to + // provide data access on the slave + Thread.sleep(3000); + + // check other groups for table + ResultSet rs; + String groupsToTest[] = new String[] { "fabric_test1_shard1", "fabric_test1_shard2", "fabric_test1_global" }; + for (String groupName : groupsToTest) { + System.out.println("Testing data present in group `" + groupName + "'"); + this.conn.setServerGroupName(groupName); + rs = this.conn.createStatement().executeQuery("select x from testGlobalTableCreation order by 1"); + assertTrue(rs.next()); // If test fails here, check replication wait above + assertEquals("752", rs.getString(1)); + assertTrue(rs.next()); + assertEquals(999, rs.getInt(1)); + assertFalse(rs.next()); + rs.close(); + } + + // cleanup + this.conn.setServerGroupName("fabric_test1_global"); + this.conn.createStatement().executeUpdate("drop table testGlobalTableCreation"); + } + + /** + * Test that sharding works by creating data in two shards and making sure it's + * only visible in the respective server group. + */ + public void testShardSelection() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + Statement stmt; + ResultSet rs; + + // create table globally + this.conn.setServerGroupName("fabric_test1_global"); + stmt = this.conn.createStatement(); + stmt.executeUpdate("drop table if exists employees"); + stmt.executeUpdate("create table employees (emp_no INT PRIMARY KEY, first_name CHAR(40), last_name CHAR(40))"); + + // begin by inserting data directly in groups + this.conn.setAutoCommit(false); + + this.conn.setServerGroupName("fabric_test1_shard1"); + stmt.executeUpdate("insert into employees values (1, 'Mickey', 'Mouse')"); + stmt.executeUpdate("insert into employees values (2, 'Donald', 'Duck')"); + this.conn.commit(); + + this.conn.setServerGroupName("fabric_test1_shard2"); + stmt.executeUpdate("insert into employees values (10001, 'Jerry', 'Garcia')"); + stmt.executeUpdate("insert into employees values (10002, 'Jimmy', 'Page')"); + this.conn.commit(); + + // insert more data using shard selection + this.conn.setAutoCommit(true); + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees.employees"); + + this.conn.setShardKey("3"); + assertEquals("fabric_test1_shard1", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (3, 'Daffy', 'Duck')"); + + this.conn.setShardKey("10003"); + assertEquals("fabric_test1_shard2", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (10003, 'Jim', 'Morrison')"); + + // check data by shard selection + this.conn.setShardKey("1"); + assertEquals("fabric_test1_shard1", this.conn.getCurrentServerGroup().getName()); + rs = stmt.executeQuery("select * from employees where emp_no = 1"); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals("Mickey", rs.getString(2)); + assertEquals("Mouse", rs.getString(3)); + assertFalse(rs.next()); + rs.close(); + this.conn.setShardKey("10001"); // emp_no=1 should NOT be present + assertEquals("fabric_test1_shard2", this.conn.getCurrentServerGroup().getName()); + rs = stmt.executeQuery("select * from employees where emp_no = 1"); + assertFalse(rs.next()); + rs.close(); + // TODO check additional values + + // check data by group selection + // TODO + + // cleanup + this.conn.setServerGroupName("fabric_test1_global"); + this.conn.createStatement().executeUpdate("drop table employees"); + } + + /** + * Test that providing the query table(s) to the connection chooses + * the proper shard mapping/table. + */ + public void testQueryTableShardSelection() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + this.conn.setCatalog("employees"); + this.conn.addQueryTable("name of non-existing table"); + try { + this.conn.createStatement(); + fail("Cannot do anything without a mapping/server group"); + } catch (SQLException ex) { + assertEquals(com.mysql.jdbc.SQLError.SQL_STATE_CONNECTION_REJECTED, ex.getSQLState()); + } + this.conn.addQueryTable("employees"); + this.conn.createStatement(); + + this.conn.clearQueryTables(); + } + + /** + * Test that providing several tables with conflicting shard mappings + * prevents a cross-shard query. + */ + public void testQueryTablesPreventCrossShardQuery() throws Exception { + // TODO - need a test env with a configuration having + // different shard mappings + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHABasics.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHABasics.java new file mode 100644 index 0000000..0daf441 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHABasics.java @@ -0,0 +1,252 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric.jdbc; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mysql.fabric.jdbc.FabricMySQLConnection; +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import testsuite.fabric.BaseFabricTestCase; + +/** + * TODO: document required setup for this test + */ +public class TestHABasics extends BaseFabricTestCase { + private FabricMySQLDataSource ds; + private FabricMySQLConnection conn; + private String masterPort = System.getProperty("com.mysql.fabric.testsuite.global.port"); + + public TestHABasics() throws Exception { + super(); + if (this.isSetForFabricTest) { + this.ds = getNewDefaultDataSource(); + } + } + + @Override + public void setUp() throws Exception { + if (this.isSetForFabricTest) { + this.conn = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + this.conn.setServerGroupName("ha_config1_group"); + } + } + + @Override + public void tearDown() throws Exception { + if (this.isSetForFabricTest) { + this.conn.close(); + } + } + + private String getPort() throws Exception { + ResultSet rs = this.conn.createStatement().executeQuery("show variables like 'port'"); + assertTrue(rs.next()); + String port1 = rs.getString(2); + rs.close(); + return port1; + } + + /** + * Test that writes go to the master and reads go to the slave(s). + */ + public void testReadWriteSplitting() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + // make sure we start on the master + assertEquals(this.masterPort, getPort()); + + Statement s = this.conn.createStatement(); + s.executeUpdate("drop table if exists fruits"); + s.executeUpdate("create table fruits (name varchar(30))"); + s.executeUpdate("insert into fruits values ('Rambutan'), ('Starfruit')"); + + // go to the slave and verify + this.conn.setReadOnly(true); + assertTrue(!this.masterPort.equals(getPort())); + + // allow a little replication lag and check for data + Thread.sleep(3000); + ResultSet rs = s.executeQuery("select name from fruits order by 1"); + assertTrue(rs.next()); + assertEquals("Rambutan", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("Starfruit", rs.getString(1)); + assertFalse(rs.next()); + rs.close(); + + this.conn.setReadOnly(false); + s.executeUpdate("drop table fruits"); + } + + /** + * Test for failover that requires manual intervention. + */ + public void manualTestFailover() throws Exception { + Statement s = this.conn.createStatement(); + ResultSet rs = s.executeQuery("show variables like 'port'"); + rs.next(); + System.err.println("Starting master Port: " + rs.getString(2)); + rs.close(); + + int secs = 15000; + System.err.println("Sleeping " + (secs / 1000.0) + " seconds.... Please perform manual failover"); + Thread.sleep(secs); + System.err.println("Continuing"); + try { + rs = s.executeQuery("show variables like 'port'"); + rs.close(); + fail("Master should be unavailable"); + } catch (SQLException ex) { + rs = s.executeQuery("show variables like 'port'"); + rs.next(); + System.err.println("New master Port: " + rs.getString(2)); + rs.close(); + s.close(); + } + } + + /** + * Test that new connections get the state changes from the Fabric node. The current implementation queries the Fabric node on every new connection and + * therefore always has the latest state. Future shared state implementation may loosen this constraint. + */ + public void manualTestNewConnHasNewState() throws Exception { + FabricMySQLConnection conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + + String query = "SELECT CONCAT(@@hostname, ':', @@port) AS 'Value'"; + ResultSet rs = conn1.createStatement().executeQuery(query); + rs.next(); + String startingMaster = rs.getString(1); + System.err.println("Starting master: " + startingMaster); + rs.close(); + conn1.close(); + + // allow time to perform manual failover + int secs = 15000; + System.err.println("Sleeping " + (secs / 1000.0) + " seconds.... Please perform manual failover"); + Thread.sleep(secs); + System.err.println("Continuing"); + + // do the exact same thing and expect it to connect to the new master without an exception + conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + + rs = conn1.createStatement().executeQuery(query); + rs.next(); + String newMaster = rs.getString(1); + System.err.println("New master: " + newMaster); + assertFalse(startingMaster.equals(newMaster)); + conn1.close(); + } + + /** + * Test that partially failed over connections (those that failed but could not immediately get a new connection) don't impact the creation of new + * connections by being a part of the replication connection group. + */ + public void manualTestFailedOldMasterDoesntBlockNewConnections() throws Exception { + FabricMySQLConnection conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + + Statement s = conn1.createStatement(); + + // run a query until a failure happens. this will cause the master connection in the replication connection to be closed + try { + while (true) { + s.executeUpdate("set @x = 1"); + try { + Thread.sleep(500); + } catch (Exception ex) { + } + } + } catch (SQLException ex) { + System.err.println("Failure encountered: " + ex.getMessage()); + System.err.println("Waiting 10 seconds before trying a new connection"); + try { + Thread.sleep(10 * 1000); + } catch (Exception ex2) { + } + } + + // we leave the conn *open* and therefore in the connection group to make sure it doesn't prevent changing master which would happen if the + // removeMasterHost() call failed + + // make sure a new connection is successful + conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + + ResultSet rs = conn1.createStatement().executeQuery("SELECT CONCAT(@@hostname, ':', @@port) AS 'Value'"); + rs.next(); + System.err.println("New master: " + rs.getString(1)); + rs.close(); + conn1.close(); + } + + /** + * Same as test `manualTestFailedOldMasterDoesntBlockNewConnections' for slaves. There must be only one slave in the HA group for this test. + */ + public void manualTestFailedSingleSlaveDoesntBlockNewConnections() throws Exception { + FabricMySQLConnection conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + conn1.setReadOnly(true); + + Statement s = conn1.createStatement(); + + // run a query until a failure happens. this will cause the slaves connection in the replication connection to be closed + try { + while (true) { + ResultSet rs = s.executeQuery("select 1"); + rs.close(); + try { + Thread.sleep(500); + } catch (Exception ex) { + } + } + } catch (SQLException ex) { + System.err.println("Failure encountered: " + ex.getMessage()); + System.err.println("Waiting 10 seconds before trying a new connection"); + try { + Thread.sleep(10 * 1000); + } catch (Exception ex2) { + } + } + + // we leave the conn *open* and therefore in the connection group to make sure it doesn't prevent changing SLAVE which would happen if the + // removeSlaveHost() call failed + + // make sure a new connection is successful + conn1 = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + conn1.setServerGroupName("ha_config1_group"); + conn1.setReadOnly(true); + + ResultSet rs = conn1.createStatement().executeQuery("SELECT CONCAT(@@hostname, ':', @@port) AS 'Value'"); + rs.next(); + System.err.println("New slave: " + rs.getString(1)); + rs.close(); + conn1.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHashSharding.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHashSharding.java new file mode 100644 index 0000000..79320b9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestHashSharding.java @@ -0,0 +1,222 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric.jdbc; + +import java.sql.ResultSet; +import java.sql.Statement; + +import com.mysql.fabric.jdbc.FabricMySQLConnection; +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import testsuite.fabric.BaseFabricTestCase; + +/** + * @todo this hash sharding test is incompatible with the + * default Fabric configuration for these C/J Fabric tests. + */ +public class TestHashSharding extends BaseFabricTestCase { + private FabricMySQLDataSource ds; + private FabricMySQLConnection conn; + + public TestHashSharding() throws Exception { + super(); + if (this.isSetForFabricTest) { + this.ds = getNewDefaultDataSource(); + } + } + + @Override + public void setUp() throws Exception { + if (this.isSetForFabricTest) { + this.conn = (FabricMySQLConnection) this.ds.getConnection(this.username, this.password); + + // create table globally + this.conn.setServerGroupName("fabric_test1_global"); + Statement stmt = this.conn.createStatement(); + stmt.executeUpdate("drop table if exists employees"); + stmt.executeUpdate("create table employees (emp_no INT PRIMARY KEY, first_name CHAR(40), last_name CHAR(40))"); + this.conn.clearServerSelectionCriteria(); + } + } + + @Override + public void tearDown() throws Exception { + if (this.isSetForFabricTest) { + this.conn.close(); + } + } + + /** + * Run the given query and assert that the result contains at + * least one row. + */ + protected void checkRowExists(String sql) throws Exception { + Statement s = this.conn.createStatement(); + ResultSet rs = s.executeQuery(sql); + assertTrue("Row should exist", rs.next()); + rs.close(); + s.close(); + } + + /** + * Run the given query and assert that the result set is empty. + */ + protected void checkRowDoesntExist(String sql) throws Exception { + Statement s = this.conn.createStatement(); + ResultSet rs = s.executeQuery(sql); + assertFalse("Row should not exist", rs.next()); + rs.close(); + s.close(); + } + + /** + * Assert that a row exists in the given server group. + */ + protected void checkRowExistsInServerGroup(String sql, String groupName) throws Exception { + this.conn.clearServerSelectionCriteria(); + this.conn.setServerGroupName(groupName); + + checkRowExists(sql); + } + + /** + * Assert that a row exists in the given shard determined by the key. + */ + protected void checkRowExistsInKeyGroup(String sql, String key) throws Exception { + this.conn.setShardKey(key); + + checkRowExists(sql); + } + + /** + * Assert that no row exists in the given server group. + */ + protected void checkRowDoesntExistInServerGroup(String sql, String groupName) throws Exception { + this.conn.clearServerSelectionCriteria(); + this.conn.setServerGroupName(groupName); + + checkRowDoesntExist(sql); + } + + /** + * Assert that no row exists in the given shard determined by the key. + */ + public void checkRowDoesntExistInKeyGroup(String sql, String key) throws Exception { + this.conn.setShardKey(key); + + checkRowDoesntExist(sql); + } + + /** + * Check data used by basic tests is in proper groups. + * Test both by direct group selection and shard table/key selection. + */ + protected void assertBasicDataIsInProperPlaces() throws Exception { + String sql; + sql = "select * from employees where emp_no = 1"; + checkRowExistsInServerGroup(sql, "fabric_test1_shard2"); + checkRowDoesntExistInServerGroup(sql, "fabric_test1_shard1"); + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees"); + checkRowExistsInKeyGroup(sql, "1"); + checkRowDoesntExistInKeyGroup(sql, "9"); + sql = "select * from employees where emp_no = 6"; // repeat with key = 6 + checkRowExistsInServerGroup(sql, "fabric_test1_shard2"); + checkRowDoesntExistInServerGroup(sql, "fabric_test1_shard1"); + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees"); + checkRowExistsInKeyGroup(sql, "6"); + checkRowDoesntExistInKeyGroup(sql, "19"); + + sql = "select * from employees where emp_no = 9"; // other shard + checkRowExistsInServerGroup(sql, "fabric_test1_shard1"); + checkRowDoesntExistInServerGroup(sql, "fabric_test1_shard2"); + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees"); + checkRowExistsInKeyGroup(sql, "9"); + checkRowDoesntExistInKeyGroup(sql, "6"); + sql = "select * from employees where emp_no = 19"; // repeat with key = 19 + checkRowExistsInServerGroup(sql, "fabric_test1_shard1"); + checkRowDoesntExistInServerGroup(sql, "fabric_test1_shard2"); + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees"); + checkRowExistsInKeyGroup(sql, "19"); + checkRowDoesntExistInKeyGroup(sql, "1"); + } + + /** + * Basic tests for shard selection with a HASH shard mapping. + */ + public void testBasicInsertByGroup() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + Statement stmt; + + // insert data directly by group selection + this.conn.setServerGroupName("fabric_test1_shard2"); + stmt = this.conn.createStatement(); + stmt.executeUpdate("insert into employees values (1, 'William', 'Gisbon')"); + stmt.executeUpdate("insert into employees values (6, 'Samuel', 'Delany')"); + + this.conn.setServerGroupName("fabric_test1_shard1"); + stmt.executeUpdate("insert into employees values (9, 'William', 'Turner')"); + stmt.executeUpdate("insert into employees values (19, 'Albrecht', 'Durer')"); + + // check everything is where it should be + assertBasicDataIsInProperPlaces(); + } + + /** + * Basic tests for shard selection with a HASH shard mapping. + */ + public void testBasicInsertByKey() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + Statement stmt; + + // insert data using shard selection + this.conn.clearServerSelectionCriteria(); + this.conn.setShardTable("employees"); + stmt = this.conn.createStatement(); + + this.conn.setShardKey("1"); // hash = c4ca4238a0b923820dcc509a6f75849b + assertEquals("fabric_test1_shard2", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (1, 'William', 'Gisbon')"); + this.conn.setShardKey("6"); // hash = 1679091c5a880faf6fb5e6087eb1b2dc + assertEquals("fabric_test1_shard2", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (6, 'Samuel', 'Delany')"); + + this.conn.setShardKey("9"); // hash = 45c48cce2e2d7fbdea1afc51c7c6ad26 + assertEquals("fabric_test1_shard1", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (9, 'William', 'Turner')"); + this.conn.setShardKey("19"); // hash = 1f0e3dad99908345f7439f8ffabdffc4 + assertEquals("fabric_test1_shard1", this.conn.getCurrentServerGroup().getName()); + stmt.executeUpdate("insert into employees values (19, 'Albrecht', 'Durer')"); + + // check everything is where it should be + assertBasicDataIsInProperPlaces(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestRegressions.java b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestRegressions.java new file mode 100644 index 0000000..2263d67 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/fabric/jdbc/TestRegressions.java @@ -0,0 +1,329 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.fabric.jdbc; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.concurrent.TimeUnit; + +import com.mysql.fabric.FabricConnection; +import com.mysql.fabric.Server; +import com.mysql.fabric.jdbc.FabricMySQLConnection; +import com.mysql.fabric.jdbc.FabricMySQLDataSource; + +import testsuite.fabric.BaseFabricTestCase; + +/** + * Testsuite for C/J Fabric regression tests. + */ +public class TestRegressions extends BaseFabricTestCase { + private FabricMySQLConnection conn; + + public TestRegressions() throws Exception { + super(); + } + + /** + * Test for Bug#73070 - prepareCall() throws NPE + * + * To test this, we create a basic stored procedure with a + * parameter, call it and check the result. + */ + public void testBug73070() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + this.conn = (FabricMySQLConnection) getNewDefaultDataSource().getConnection(this.username, this.password); + this.conn.setServerGroupName("fabric_test1_global"); + + this.conn.createStatement().executeUpdate("drop procedure if exists bug73070"); + this.conn.createStatement().executeUpdate("create procedure bug73070(in x integer) select x"); + CallableStatement stmt = this.conn.prepareCall("{call bug73070(?)}"); + stmt.setInt(1, 42); + ResultSet rs = stmt.executeQuery(); + rs.next(); + assertEquals(42, rs.getInt(1)); + rs.close(); + stmt.close(); + this.conn.createStatement().executeUpdate("drop procedure bug73070"); + + this.conn.close(); + } + + /** + * Test Bug#75080 - NPE when setting a timestamp on a Fabric connection + */ + public void testBug75080() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + class TestBugInternal { + @SuppressWarnings("synthetic-access") + void test(FabricMySQLDataSource ds) throws Exception { + TestRegressions.this.conn = (FabricMySQLConnection) ds.getConnection(TestRegressions.this.username, TestRegressions.this.password); + TestRegressions.this.conn.setServerGroupName("fabric_test1_global"); + + PreparedStatement ps = TestRegressions.this.conn.prepareStatement("select ?"); + Timestamp ts = new Timestamp(System.currentTimeMillis()); + ps.setTimestamp(1, ts); + ResultSet rs = ps.executeQuery(); + rs.next(); + Timestamp tsResult = rs.getTimestamp(1); + assertEquals(ts, tsResult); + rs.close(); + ps.close(); + TestRegressions.this.conn.close(); + } + } + + FabricMySQLDataSource ds = getNewDefaultDataSource(); + + // test includes both "legacy" and "new" datetime code + ds.setUseLegacyDatetimeCode(false); + new TestBugInternal().test(ds); + ds.setUseLegacyDatetimeCode(true); + new TestBugInternal().test(ds); + } + + /** + * Test Bug#77217 - ClassCastException when executing a PreparedStatement with Fabric (using a streaming result with timeout set) + */ + public void testBug77217() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + this.conn = (FabricMySQLConnection) getNewDefaultDataSource().getConnection(this.username, this.password); + this.conn.setServerGroupName("ha_config1_group"); + + PreparedStatement ps = this.conn.prepareStatement("select ? from dual"); + ps.setFetchSize(Integer.MIN_VALUE); + ps.setString(1, "abc"); + ResultSet rs = ps.executeQuery(); + rs.next(); + assertEquals("abc", rs.getString(1)); + rs.close(); + ps.close(); + this.conn.close(); + } + + public void testBug21876798() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + FabricMySQLDataSource ds = getNewDefaultDataSource(); + ds.setRewriteBatchedStatements(true); + + this.conn = (FabricMySQLConnection) ds.getConnection(this.username, this.password); + this.conn.setServerGroupName("ha_config1_group"); + + this.conn.createStatement().executeUpdate("drop table if exists bug21876798"); + this.conn.createStatement().executeUpdate("create table bug21876798(x varchar(100))"); + PreparedStatement ps = this.conn.prepareStatement("update bug21876798 set x = ?"); + ps.setString(1, "abc"); + ps.addBatch(); + ps.setString(1, "def"); + ps.addBatch(); + ps.setString(1, "def"); + ps.addBatch(); + ps.setString(1, "def"); + ps.addBatch(); + ps.setString(1, "def"); + ps.addBatch(); + // this would throw a ClassCastException + ps.executeBatch(); + } + + /** + * Test Bug#21296840 - CONNECTION DATA IS NOT UPDATED DURING FAILOVER. + * Test Bug#17910835 - SERVER INFORMATION FROM FABRIC NOT REFRESHED WITH SHORTER TTL. + * + * Test that the local cache is refreshed after expired TTL. This test connects to the master of "ha_config1_group" and requires the master to be changed + * manually during the wait period. The Fabric must also be setup to communicate a TTL of less than 10s to the client. + */ + public void manualTestRefreshFabricStateCache() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + this.conn = (FabricMySQLConnection) getNewDefaultDataSource().getConnection(this.username, this.password); + this.conn.setServerGroupName("ha_config1_group"); + this.conn.setReadOnly(false); + this.conn.setAutoCommit(false); + + Statement stmt = this.conn.createStatement(); + + ResultSet rs = stmt.executeQuery("show variables like 'server_uuid'"); + rs.next(); + String firstServerUuid = rs.getString(2); + rs.close(); + this.conn.commit(); + + // sleep for TTL+1 secs + int seconds = 10; + System.err.println("Waiting " + seconds + " seconds for new master to be chosen"); + Thread.sleep(TimeUnit.SECONDS.toMillis(1 + seconds)); + + // force the LB proxy to pick a new connection + this.conn.rollback(); + + // verify change is seen by client + rs = stmt.executeQuery("show variables like 'server_uuid'"); + rs.next(); + String secondServerUuid = rs.getString(2); + rs.close(); + + System.err.println("firstServerUuid=" + firstServerUuid + "\nsecondServerUuid=" + secondServerUuid); + if (firstServerUuid.equals(secondServerUuid)) { + fail("Server ID should change to reflect new topology"); + } + + this.conn.close(); + } + + /** + * Test Bug#82094 - ConcurrentModificationException on Fabric connections after topology changes. + * + * This test requires a Fabric instance running with a HA group (ha_config1_group) containing three servers, one promoted to master and failure detection + * turned on. + * Note that removing one or the other secondary server is a distinct case and only one of them causes the reported failure. This is so because of the order + * the elements on the slave servers HashSet, from the ReplicationConnectionGroup object, are iterated. So, we remove one at a time in this test to make + * sure we cover both cases. + */ + public void testBug82094() throws Exception { + if (!this.isSetForFabricTest) { + return; + } + + FabricMySQLDataSource ds = getNewDefaultDataSource(); + ds.setFabricServerGroup("ha_config1_group"); + this.conn = (FabricMySQLConnection) ds.getConnection(this.username, this.password); + this.conn.createStatement().close(); // Make sure there is an internal ReplicationConnection. + + FabricConnection fabricConn = new FabricConnection(this.fabricUrl, this.fabricUsername, this.fabricPassword); + + for (Server server : fabricConn.getServerGroup("ha_config1_group").getServers()) { + if (server.isSlave()) { + try { + this.conn.transactionCompleted(); + + // Remove Secondary server. + fabricConn.getClient().removeServerFromGroup(server.getGroupName(), server.getHostname(), server.getPort()); + // Make sure the TTL expires before moving on. + fabricConn.refreshState(); + while (!fabricConn.isStateExpired()) { + Thread.sleep(1000); + } + + this.conn.transactionCompleted(); + } finally { + // Add Secondary server back. + fabricConn.getClient().addServerToGroup(server.getGroupName(), server.getHostname(), server.getPort()); + // Make sure the TTL expires before moving on. + fabricConn.refreshState(); + while (!fabricConn.isStateExpired()) { + Thread.sleep(1000); + } + } + } + } + this.conn.close(); + } + + /** + * Test Bug#22750465 - CONNECTOR/J HANGS WHEN FABRIC NODE IS DOWN. + * + * This test connects to the master of "ha_config1_group" and requires the master to be changed manually during the first wait period. The fabric node must + * be shut down during the second wait period. The Fabric must also be setup to communicate a TTL of less than 10s to the client. + */ + public void manualTestBug22750465() throws Exception { + this.conn = (FabricMySQLConnection) getNewDefaultDataSource().getConnection(this.username, this.password); + this.conn.setServerGroupName("ha_config1_group"); + + String initialMasterUuid = this.conn.getCurrentServerGroup().getMaster().getUuid(); + + Statement stmt = this.conn.createStatement(); + ResultSet rs = stmt.executeQuery("SHOW VARIABLES LIKE 'server_uuid'"); + rs.next(); + String firstServerUuid = rs.getString(2); + rs.close(); + + assertEquals(initialMasterUuid, firstServerUuid); + manualTestBug22750465SomeReadWriteOperations(this.conn); + + // Promote new primary server. + int seconds = 10; + System.err.println("Waiting " + seconds + " seconds for new master to be chosen (execute: 'mysqlfabric group promote ha_config1_group')"); + Thread.sleep(TimeUnit.SECONDS.toMillis(1 + seconds)); + + rs = stmt.executeQuery("SHOW VARIABLES LIKE 'server_uuid'"); + rs.next(); + String secondServerUuid = rs.getString(2); + rs.close(); + + assertFalse(initialMasterUuid.equals(secondServerUuid)); + manualTestBug22750465SomeReadWriteOperations(this.conn); + + // Shutdown the Fabric node. + System.err.println("Waiting " + seconds + " seconds for Fabric node shutdown (execute: 'mysqlfabric group manage stop')"); + Thread.sleep(TimeUnit.SECONDS.toMillis(1 + seconds)); + + rs = stmt.executeQuery("SHOW VARIABLES LIKE 'server_uuid'"); + rs.next(); + String thirdServerUuid = rs.getString(2); + rs.close(); + + assertEquals(secondServerUuid, thirdServerUuid); + manualTestBug22750465SomeReadWriteOperations(this.conn); + + this.conn.close(); + + try { + getNewDefaultDataSource().getConnection(this.username, this.password); + fail("Exception was expected when trying to connect to a non-running Fabric node."); + } catch (SQLException e) { + assertEquals("Unable to establish connection to the Fabric server", e.getMessage()); + } + } + + private void manualTestBug22750465SomeReadWriteOperations(Connection testConn) throws Exception { + Statement stmt = testConn.createStatement(); + try { + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS testBug22750465 (id INT)"); + stmt.executeUpdate("INSERT INTO testBug22750465 VALUES (1)"); + ResultSet rs = stmt.executeQuery("SELECT * FROM testBug22750465"); + assertTrue(rs.next()); + } finally { + stmt.executeUpdate("DROP TABLE IF EXISTS testBug22750465"); + stmt.close(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/perf/BasePerfTest.java b/mysql-connector-java-5.1.40/src/testsuite/perf/BasePerfTest.java new file mode 100644 index 0000000..898c9f5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/perf/BasePerfTest.java @@ -0,0 +1,208 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.perf; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import testsuite.BaseTestCase; + +/** + * Base class for performance test cases. Handles statistics. + */ +public abstract class BasePerfTest extends BaseTestCase { + /** + * Confidence interval lookup table, indexed by degrees of freedom at 95%. + */ + private static final double[] T95 = { 12.706, 4.303, 3.182, 2.776, 2.571, 2.447, 2.365, 2.306, 2.262, 2.228, 2.201, 2.179, 2.160, 2.145, 2.131, 2.120, + 2.110, 2.101, 2.093, 2.086, 2.080, 2.074, 2.069, 2.064, 2.060, 2.056, 2.052, 2.048, 2.045, 2.042 }; + + /** + * Confidence interval lookup table, indexed by degrees of freedom at 99%. + */ + private static final double[] T99 = { 63.657, 9.925, 5.841, 4.604, 4.032, 3.707, 3.499, 3.355, 3.250, 3.169, 3.106, 3.055, 3.012, 2.977, 2.947, 2.921, + 2.898, 2.878, 2.861, 2.845, 2.831, 2.819, 2.807, 2.797, 2.787, 2.779, 2.771, 2.763, 2.756, 2.750 }; + + static NumberFormat numberFormatter = NumberFormat.getInstance(); + + static { + numberFormatter.setMaximumFractionDigits(4); + numberFormatter.setMinimumFractionDigits(4); + } + + /** + * List of values for each iteration + */ + private List testValuesList = new ArrayList(); + + private double confidenceLevel = 95; // 95% by default + + private double confidenceValue = 0; + + private double intervalWidth = 0.1; + + private double meanValue = 0; + + private double squareSumValue = 0; + + private double sumValue = 0; + + private double variationValue = 0; + + /** + * The number of iterations that we have performed + */ + private int numIterations = 0; + + /** + * Creates a new BasePerfTest object. + * + * @param name + * the testcase name to perform. + */ + public BasePerfTest(String name) { + super(name); + } + + /** + * Returns the meanValue. + * + * @return double + */ + public double getMeanValue() { + return this.meanValue; + } + + /** + * Sub-classes should override this to perform the operation to be measured. + * + * @throws Exception + * if an error occurs. + */ + protected abstract void doOneIteration() throws Exception; + + /** + * Returns the current confidence level. + * + * @return the current confindence level. + */ + protected double getCurrentConfidence() { + return (this.intervalWidth - this.confidenceValue) * 100; + } + + /** + * Returns the current margin of error. + * + * @return the current margin of error. + */ + protected double getMarginOfError() { + return getConfidenceLookup() * (getStandardDeviationP() / Math.sqrt(this.numIterations)); + } + + /** + * Returns the current STDDEV. + * + * @return the current STDDEV + */ + protected double getStandardDeviationP() { + if (this.numIterations < 1) { + return 0; + } + + return Math.sqrt(((this.numIterations * this.squareSumValue) - (this.sumValue * this.sumValue)) / (this.numIterations * this.numIterations)); + } + + /** + * Adds one test result to the statistics. + * + * @param value + * a single result representing the value being measured in the + * test. + */ + protected void addResult(double value) { + this.numIterations++; + this.testValuesList.add(new Double(value)); + + this.sumValue += value; + this.squareSumValue += (value * value); + this.meanValue = this.sumValue / this.numIterations; + this.variationValue = (this.squareSumValue / this.numIterations) - (this.meanValue * this.meanValue); + + // Can only have confidence when more than one test has been completed + if (this.numIterations > 1) { + this.confidenceValue = this.intervalWidth + - ((2.0 * getConfidenceLookup() * Math.sqrt(this.variationValue / (this.numIterations - 1.0))) / this.meanValue); + } + } + + /** + * Calls doIteration() the numIterations times, displaying + * the mean, std, margin of error and confidence level. + * + * @param num_iterations + * the number of iterations to perform ( < 30) + * @throws Exception + * if an error occurs. + */ + protected void doIterations(int num_iterations) throws Exception { + for (int i = 0; i < num_iterations; i++) { + doOneIteration(); + } + } + + /** + * Reports the current results to STDOUT, preceeded by additionalMessage if not null. + * + * @param additionalMessage + * the additional message to print, or null if no message. + */ + protected synchronized void reportResults(String additionalMessage) { + StringBuilder messageBuf = new StringBuilder(); + + if (additionalMessage != null) { + messageBuf.append(additionalMessage); + messageBuf.append(": "); + } + + messageBuf.append(" mean: "); + messageBuf.append(numberFormatter.format(this.meanValue)); + messageBuf.append(" stdevp: "); + messageBuf.append(numberFormatter.format(getStandardDeviationP())); + messageBuf.append(" m-o-e: "); + messageBuf.append(numberFormatter.format(getMarginOfError())); + + System.out.println(messageBuf.toString()); + } + + private double getConfidenceLookup() { + if (this.confidenceLevel == 95) { + return T95[this.numIterations - 1]; + } else if (this.confidenceLevel == 99) { + return T99[this.numIterations - 1]; + } else { + throw new IllegalArgumentException("Confidence level must be 95 or 99"); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/perf/LoadStorePerfTest.java b/mysql-connector-java-5.1.40/src/testsuite/perf/LoadStorePerfTest.java new file mode 100644 index 0000000..6ed4327 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/perf/LoadStorePerfTest.java @@ -0,0 +1,334 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.perf; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.text.NumberFormat; + +import testsuite.BaseTestCase; + +/** + * Simple performance testing unit test. + */ +public class LoadStorePerfTest extends BasePerfTest { + /** The table type to use (only for MySQL), 'HEAP' by default */ + private String tableType = "HEAP"; + + private boolean takeMeasurements = false; + + private boolean useColumnNames = false; + + private boolean largeResults = false; + + /** + * Constructor for LoadStorePerfTest. + * + * @param name + * the name of the test to run + */ + public LoadStorePerfTest(String name) { + super(name); + + String newTableType = System.getProperty("com.mysql.jdbc.test.tabletype"); + + this.largeResults = "TRUE".equalsIgnoreCase(System.getProperty("com.mysql.jdbc.testsuite.loadstoreperf.useBigResults")); + + if ((newTableType != null) && (newTableType.length() > 0)) { + this.tableType = newTableType; + + System.out.println("Using specified table type of '" + this.tableType + "'"); + } + } + + /** + * Runs all tests in this test case + * + * @param args + * ignored + * + * @throws Exception + * if an error occurs + */ + public static void main(String[] args) throws Exception { + new LoadStorePerfTest("test1000Transactions").run(); + } + + /** + * @see junit.framework.TestCase#setUp() + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + try { + this.stmt.executeUpdate("DROP TABLE perfLoadStore"); + } catch (SQLException sqlEx) { + // ignore + } + + String dateTimeType = "DATETIME"; + + if (BaseTestCase.dbUrl.indexOf("oracle") != -1) { + dateTimeType = "TIMESTAMP"; + } + + // + // Approximate a run-of-the-mill entity in a business application + // + String query = "CREATE TABLE perfLoadStore (priKey INT NOT NULL, fk1 INT NOT NULL, fk2 INT NOT NULL, dtField " + dateTimeType + + ", charField1 CHAR(32), charField2 CHAR(32), charField3 CHAR(32), charField4 CHAR(32), intField1 INT, intField2 INT, " + + "intField3 INT, intField4 INT, doubleField1 DECIMAL, doubleField2 DOUBLE, doubleField3 DOUBLE, doubleField4 DOUBLE," + + "PRIMARY KEY (priKey))"; + + if (BaseTestCase.dbUrl.indexOf("mysql") != -1) { + query += (getTableTypeDecl() + " =" + this.tableType); + } + + this.stmt.executeUpdate(query); + + String currentDateValue = "NOW()"; + + if (BaseTestCase.dbUrl.indexOf("sqlserver") != -1) { + currentDateValue = "GETDATE()"; + } + + if (BaseTestCase.dbUrl.indexOf("oracle") != -1) { + currentDateValue = "CURRENT_TIMESTAMP"; + } + + int numLoops = 1; + + if (this.largeResults) { + numLoops = 32; + } + + System.out.println("Inserting " + numLoops + " rows to retrieve..."); + + for (int i = 0; i < numLoops; i++) { + this.stmt.executeUpdate("INSERT INTO perfLoadStore (priKey, fk1, fk2, dtField, charField1, charField2, charField3, charField4, " + + "intField1, intField2, intField3, intField4, doubleField1, doubleField2, doubleField3, doubleField4) VALUES (" + i + "," // priKey + + "2," // fk1 + + "3," // fk2 + + currentDateValue + "," // dtField + + "'0123456789ABCDEF0123456789ABCDEF'," // charField1 + + "'0123456789ABCDEF0123456789ABCDEF'," // charField2 + + "'0123456789ABCDEF0123456789ABCDEF'," // charField3 + + "'0123456789ABCDEF0123456789ABCDEF'," // charField4 + + "7," // intField1 + + "8," // intField2 + + "9," // intField3 + + "10," // intField4 + + "1.20," // doubleField1 + + "2.30," // doubleField2 + + "3.40," // doubleField3 + + "4.50" // doubleField4 + + ")"); + } + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + @Override + public void tearDown() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE perfLoadStore"); + } catch (SQLException sqlEx) { + // ignore + } + + super.tearDown(); + } + + /** + * Tests and times 1000 load/store type transactions + * + * @throws Exception + * if an error occurs + */ + public void test1000Transactions() throws Exception { + this.takeMeasurements = false; + warmUp(); + this.takeMeasurements = true; + doIterations(29); + + reportResults("\n\nResults for instance # 1: "); + } + + /** + * Runs one iteration of the test. + * + * @see testsuite.perf.BasePerfTest#doOneIteration() + */ + @Override + protected void doOneIteration() throws Exception { + PreparedStatement pStmtStore = this.conn.prepareStatement("UPDATE perfLoadStore SET priKey = ?, fk1 = ?, fk2 = ?, dtField = ?, charField1 = ?, " + + "charField2 = ?, charField3 = ?, charField4 = ?, intField1 = ?, intField2 = ?, intField3 = ?, intField4 = ?, doubleField1 = ?," + + "doubleField2 = ?, doubleField3 = ?, doubleField4 = ? WHERE priKey=?"); + PreparedStatement pStmtCheck = this.conn.prepareStatement("SELECT COUNT(*) FROM perfLoadStore WHERE priKey=?"); + PreparedStatement pStmtLoad = null; + + if (this.largeResults) { + pStmtLoad = this.conn.prepareStatement("SELECT priKey, fk1, fk2, dtField, charField1, charField2, charField3, charField4, intField1, " + + "intField2, intField3, intField4, doubleField1, doubleField2, doubleField3, doubleField4 FROM perfLoadStore"); + } else { + pStmtLoad = this.conn.prepareStatement("SELECT priKey, fk1, fk2, dtField, charField1, charField2, charField3, charField4, intField1, " + + "intField2, intField3, intField4, doubleField1, doubleField2, doubleField3, doubleField4 FROM perfLoadStore WHERE priKey=?"); + } + + NumberFormat numFormatter = NumberFormat.getInstance(); + numFormatter.setMaximumFractionDigits(4); + numFormatter.setMinimumFractionDigits(4); + + int transactionCount = 5000; + + if (this.largeResults) { + transactionCount = 50; + } + + long begin = System.currentTimeMillis(); + + for (int i = 0; i < transactionCount; i++) { + this.conn.setAutoCommit(false); + pStmtCheck.setInt(1, 1); + this.rs = pStmtCheck.executeQuery(); + + while (this.rs.next()) { + this.rs.getInt(1); + } + + this.rs.close(); + + if (!this.largeResults) { + pStmtLoad.setInt(1, 1); + } + + this.rs = pStmtLoad.executeQuery(); + + if (this.rs.next()) { + int key = this.rs.getInt(1); + + if (!this.useColumnNames) { + pStmtStore.setInt(1, key); // priKey + pStmtStore.setInt(2, this.rs.getInt(2)); // fk1 + pStmtStore.setInt(3, this.rs.getInt(3)); // fk2 + pStmtStore.setTimestamp(4, this.rs.getTimestamp(4)); // dtField + pStmtStore.setString(5, this.rs.getString(5)); // charField1 + pStmtStore.setString(6, this.rs.getString(7)); // charField2 + pStmtStore.setString(7, this.rs.getString(7)); // charField3 + pStmtStore.setString(8, this.rs.getString(8)); // charField4 + pStmtStore.setInt(9, this.rs.getInt(9)); // intField1 + pStmtStore.setInt(10, this.rs.getInt(10)); // intField2 + pStmtStore.setInt(11, this.rs.getInt(11)); // intField3 + pStmtStore.setInt(12, this.rs.getInt(12)); // intField4 + pStmtStore.setDouble(13, this.rs.getDouble(13)); // doubleField1 + pStmtStore.setDouble(14, this.rs.getDouble(14)); // doubleField2 + pStmtStore.setDouble(15, this.rs.getDouble(15)); // doubleField3 + pStmtStore.setDouble(16, this.rs.getDouble(16)); // doubleField4 + + pStmtStore.setInt(17, key); + } else { + /* + * "UPDATE perfLoadStore SET " + "priKey = ?, " + "fk1 = ?, " + + * "fk2 = ?, " + "dtField = ?, " + "charField1 = ?, " + + * "charField2 = ?, " + "charField3 = ?, " + "charField4 = ?, " + + * "intField1 = ?, " + "intField2 = ?, " + "intField3 = ?, " + + * "intField4 = ?, " + "doubleField1 = ?," + "doubleField2 = + * ?," + "doubleField3 = ?," + "doubleField4 = ?" + " WHERE + * priKey=?"); + */ + pStmtStore.setInt(1, key); // priKey + pStmtStore.setInt(2, this.rs.getInt("fk1")); // fk1 + pStmtStore.setInt(3, this.rs.getInt("fk2")); // fk2 + pStmtStore.setTimestamp(4, this.rs.getTimestamp("dtField")); // dtField + pStmtStore.setString(5, this.rs.getString("charField1")); // charField1 + pStmtStore.setString(6, this.rs.getString("charField2")); // charField2 + pStmtStore.setString(7, this.rs.getString("charField3")); // charField3 + pStmtStore.setString(8, this.rs.getString("charField4")); // charField4 + pStmtStore.setInt(9, this.rs.getInt("intField1")); // intField1 + pStmtStore.setInt(10, this.rs.getInt("intField2")); // intField2 + pStmtStore.setInt(11, this.rs.getInt("intField3")); // intField3 + pStmtStore.setInt(12, this.rs.getInt("intField4")); // intField4 + pStmtStore.setDouble(13, this.rs.getDouble("doubleField1")); // doubleField1 + pStmtStore.setDouble(14, this.rs.getDouble("doubleField2")); // doubleField2 + pStmtStore.setDouble(15, this.rs.getDouble("doubleField3")); // doubleField3 + pStmtStore.setDouble(16, this.rs.getDouble("doubleField4")); // doubleField4 + + pStmtStore.setInt(17, key); + } + + pStmtStore.executeUpdate(); + } + + this.rs.close(); + + this.conn.commit(); + this.conn.setAutoCommit(true); + } + + pStmtStore.close(); + pStmtCheck.close(); + pStmtLoad.close(); + + long end = System.currentTimeMillis(); + + long timeElapsed = (end - begin); + + double timeElapsedSeconds = (double) timeElapsed / 1000; + double tps = transactionCount / timeElapsedSeconds; + + if (this.takeMeasurements) { + addResult(tps); + System.out.print("1 [ " + numFormatter.format(getMeanValue()) + " ] "); + } else { + System.out.println("Warm-up: " + tps + " trans/sec"); + } + } + + /** + * Runs the test 10 times to get JIT going, and GC going + * + * @throws Exception + * if an error occurs. + */ + protected void warmUp() throws Exception { + try { + System.out.print("Warm-up period (10 iterations)"); + + for (int i = 0; i < 10; i++) { + doOneIteration(); + System.out.print("."); + } + + System.out.println(); + System.out.println("Warm-up period ends"); + System.out.println("\nUnits for this test are transactions/sec."); + } catch (Exception ex) { + ex.printStackTrace(); + + throw ex; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/perf/RetrievalPerfTest.java b/mysql-connector-java-5.1.40/src/testsuite/perf/RetrievalPerfTest.java new file mode 100644 index 0000000..e2c056b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/perf/RetrievalPerfTest.java @@ -0,0 +1,208 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.perf; + +import testsuite.BaseTestCase; + +/** + * Simplistic test for performance regression. + */ +public class RetrievalPerfTest extends BaseTestCase { + private static final int NUM_TESTS = 10000; + + private static final int NUM_ROWS = 80; + + /** + * Constructor for RetrievalPerfTest. + * + * @param name + * name of the test to run + */ + public RetrievalPerfTest(String name) { + super(name); + } + + /** + * Runs all tests. + * + * @param args + * ignored + */ + public static void main(String[] args) { + new RetrievalPerfTest("testRetrievalMyIsam").run(); + new RetrievalPerfTest("testRetrievalHeap").run(); + new RetrievalPerfTest("testRetrievalCached").run(); + } + + /** + * @see junit.framework.TestCase#setUp() + */ + @Override + public void setUp() throws Exception { + super.setUp(); + createTable("retrievalPerfTestHeap", "(priKey INT NOT NULL PRIMARY KEY, charField VARCHAR(80)) ", "HEAP"); + createTable("retrievalPerfTestMyIsam", "(priKey INT NOT NULL PRIMARY KEY, charField VARCHAR(80)) ", "MyISAM"); + + for (int i = 0; i < NUM_ROWS; i++) { + this.stmt.executeUpdate( + "INSERT INTO retrievalPerfTestHeap (priKey, charField) VALUES (" + i + ",'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')"); + this.stmt.executeUpdate( + "INSERT INTO retrievalPerfTestMyIsam (priKey, charField) VALUES (" + i + ",'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')"); + } + } + + /** + * Tests retrieval from the query cache + * + * @throws Exception + * if an error occurs + */ + public void testRetrievalCached() throws Exception { + this.stmt.executeUpdate("SET QUERY_CACHE_TYPE = DEMAND"); + + double fullBegin = System.currentTimeMillis(); + double averageQueryTimeMs = 0; + double averageTraversalTimeMs = 0; + + for (int i = 0; i < NUM_TESTS; i++) { + long queryBegin = System.currentTimeMillis(); + this.rs = this.stmt.executeQuery("SELECT SQL_CACHE * FROM retrievalPerfTestHeap"); + + long queryEnd = System.currentTimeMillis(); + averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS); + + long traverseBegin = System.currentTimeMillis(); + + while (this.rs.next()) { + this.rs.getInt(1); + this.rs.getString(2); + } + + long traverseEnd = System.currentTimeMillis(); + averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS); + } + + double fullEnd = System.currentTimeMillis(); + double fullTime = (fullEnd - fullBegin) / 1000; + double queriesPerSec = NUM_TESTS / fullTime; + double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime; + System.out.println("\nQuery Cache From Heap Retrieval\n"); + System.out.println("Full test took: " + fullTime + " seconds."); + System.out.println("Queries/second: " + queriesPerSec); + System.out.println("Rows/second: " + rowsPerSec); + System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs + " ms"); + System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs + " ms"); + + // We're doing something wrong if we can't beat 45 seconds :( + assertTrue(fullTime < 45); + } + + /** + * Tests retrieval from HEAP tables + * + * @throws Exception + * if an error occurs + */ + public void testRetrievalHeap() throws Exception { + double fullBegin = System.currentTimeMillis(); + double averageQueryTimeMs = 0; + double averageTraversalTimeMs = 0; + + for (int i = 0; i < NUM_TESTS; i++) { + long queryBegin = System.currentTimeMillis(); + this.rs = this.stmt.executeQuery("SELECT * FROM retrievalPerfTestHeap"); + + long queryEnd = System.currentTimeMillis(); + averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS); + + long traverseBegin = System.currentTimeMillis(); + + while (this.rs.next()) { + this.rs.getInt(1); + this.rs.getString(2); + } + + long traverseEnd = System.currentTimeMillis(); + averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS); + } + + double fullEnd = System.currentTimeMillis(); + double fullTime = (fullEnd - fullBegin) / 1000; + double queriesPerSec = NUM_TESTS / fullTime; + double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime; + System.out.println("\nHEAP Table Retrieval\n"); + System.out.println("Full test took: " + fullTime + " seconds."); + System.out.println("Queries/second: " + queriesPerSec); + System.out.println("Rows/second: " + rowsPerSec); + System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs + " ms"); + System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs + " ms"); + + // We're doing something wrong if we can't beat 45 seconds :( + assertTrue(fullTime < 45); + } + + /** + * Tests retrieval speed from MyISAM type tables + * + * @throws Exception + * if an error occurs + */ + public void testRetrievalMyIsam() throws Exception { + double fullBegin = System.currentTimeMillis(); + double averageQueryTimeMs = 0; + double averageTraversalTimeMs = 0; + + for (int i = 0; i < NUM_TESTS; i++) { + long queryBegin = System.currentTimeMillis(); + this.rs = this.stmt.executeQuery("SELECT * FROM retrievalPerfTestMyIsam"); + + long queryEnd = System.currentTimeMillis(); + averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS); + + long traverseBegin = System.currentTimeMillis(); + + while (this.rs.next()) { + this.rs.getInt(1); + this.rs.getString(2); + } + + long traverseEnd = System.currentTimeMillis(); + averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS); + } + + double fullEnd = System.currentTimeMillis(); + double fullTime = (fullEnd - fullBegin) / 1000; + double queriesPerSec = NUM_TESTS / fullTime; + double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime; + System.out.println("\nMyIsam Retrieval\n"); + System.out.println("Full test took: " + fullTime + " seconds."); + System.out.println("Queries/second: " + queriesPerSec); + System.out.println("Rows/second: " + rowsPerSec); + System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs + " ms"); + System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs + " ms"); + + // We're doing something wrong if we can't beat 45 seconds :( + assertTrue(fullTime < 45); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/BlobRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/BlobRegressionTest.java new file mode 100644 index 0000000..a1aa617 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/BlobRegressionTest.java @@ -0,0 +1,407 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.StringReader; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.concurrent.Callable; + +import testsuite.BaseTestCase; + +/** + * Tests fixes for BLOB handling. + */ +public class BlobRegressionTest extends BaseTestCase { + /** + * Creates a new BlobRegressionTest. + * + * @param name + * name of the test to run + */ + public BlobRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(BlobRegressionTest.class); + } + + /** + * @throws Exception + */ + public void testBug2670() throws Exception { + byte[] blobData = new byte[32]; + + for (int i = 0; i < blobData.length; i++) { + blobData[i] = 1; + } + + createTable("testBug2670", "(blobField LONGBLOB)"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug2670 (blobField) VALUES (?)"); + pStmt.setBytes(1, blobData); + pStmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT blobField FROM testBug2670"); + this.rs.next(); + + Blob blob = this.rs.getBlob(1); + + // + // Test mid-point insertion + // + blob.setBytes(4, new byte[] { 2, 2, 2, 2 }); + + byte[] newBlobData = blob.getBytes(1L, (int) blob.length()); + + assertTrue("Blob changed length", blob.length() == blobData.length); + + assertTrue("New data inserted wrongly", ((newBlobData[3] == 2) && (newBlobData[4] == 2) && (newBlobData[5] == 2) && (newBlobData[6] == 2))); + + // + // Test end-point insertion + // + blob.setBytes(32, new byte[] { 2, 2, 2, 2 }); + + assertTrue("Blob length should be 3 larger", blob.length() == (blobData.length + 3)); + } + + /** + * http://bugs.mysql.com/bug.php?id=22891 + * + * @throws Exception + * ... + */ + public void testUpdateLongBlobGT16M() throws Exception { + if (versionMeetsMinimum(4, 0)) { + + byte[] blobData = new byte[18 * 1024 * 1024]; // 18M blob + + createTable("testUpdateLongBlob", "(blobField LONGBLOB)"); + this.stmt.executeUpdate("INSERT INTO testUpdateLongBlob (blobField) VALUES (NULL)"); + + this.pstmt = this.conn.prepareStatement("UPDATE testUpdateLongBlob SET blobField=?"); + this.pstmt.setBytes(1, blobData); + try { + this.pstmt.executeUpdate(); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max_allowed_packet") != -1) { + fail("You need to increase max_allowed_packet to at least 18M before running this test!"); + } + } + + } + } + + /** + * @throws Exception + */ + public void testUpdatableBlobsWithCharsets() throws Exception { + byte[] smallBlob = new byte[32]; + + for (byte i = 0; i < smallBlob.length; i++) { + smallBlob[i] = i; + } + + createTable("testUpdatableBlobsWithCharsets", "(pk INT NOT NULL PRIMARY KEY, field1 BLOB)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testUpdatableBlobsWithCharsets (pk, field1) VALUES (1, ?)"); + this.pstmt.setBinaryStream(1, new ByteArrayInputStream(smallBlob), smallBlob.length); + this.pstmt.executeUpdate(); + + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.rs = updStmt.executeQuery("SELECT pk, field1 FROM testUpdatableBlobsWithCharsets"); + System.out.println(this.rs); + this.rs.next(); + + for (byte i = 0; i < smallBlob.length; i++) { + smallBlob[i] = (byte) (i + 32); + } + + this.rs.updateBinaryStream(2, new ByteArrayInputStream(smallBlob), smallBlob.length); + this.rs.updateRow(); + + ResultSet newRs = this.stmt.executeQuery("SELECT field1 FROM testUpdatableBlobsWithCharsets"); + + newRs.next(); + + byte[] updatedBlob = newRs.getBytes(1); + + for (byte i = 0; i < smallBlob.length; i++) { + byte origValue = smallBlob[i]; + byte newValue = updatedBlob[i]; + + assertTrue("Original byte at position " + i + ", " + origValue + " != new value, " + newValue, origValue == newValue); + } + + } + + public void testBug5490() throws Exception { + + createTable("testBug5490", "(pk INT NOT NULL PRIMARY KEY, blobField BLOB)"); + String sql = "insert into testBug5490 values(?,?)"; + + int blobFileSize = 871; + File blobFile = newTempBinaryFile("Bug5490", blobFileSize); + + this.pstmt = this.conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + this.pstmt.setInt(1, 2); + FileInputStream fis = new FileInputStream(blobFile); + this.pstmt.setBinaryStream(2, fis, blobFileSize); + this.pstmt.execute(); + fis.close(); + this.pstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT blobField FROM testBug5490"); + + this.rs.next(); + + byte[] returned = this.rs.getBytes(1); + + assertEquals(blobFileSize, returned.length); + + } + + /** + * Tests BUG#8096 where emulated locators corrupt binary data when using + * server-side prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug8096() throws Exception { + int dataSize = 256; + + Properties props = new Properties(); + props.setProperty("emulateLocators", "true"); + Connection locatorConn = getConnectionWithProps(props); + + String select = "SELECT ID, 'DATA' AS BLOB_DATA FROM testBug8096 WHERE ID = ?"; + String insert = "INSERT INTO testBug8096 (ID, DATA) VALUES (?, '')"; + + String id = "1"; + byte[] testData = new byte[dataSize]; + + for (int i = 0; i < testData.length; i++) { + testData[i] = (byte) i; + } + + createTable("testBug8096", "(ID VARCHAR(10) PRIMARY KEY, DATA LONGBLOB)"); + this.pstmt = locatorConn.prepareStatement(insert); + this.pstmt.setString(1, id); + this.pstmt.execute(); + + this.pstmt = locatorConn.prepareStatement(select); + this.pstmt.setString(1, id); + + this.rs = this.pstmt.executeQuery(); + + if (this.rs.next()) { + Blob b = this.rs.getBlob("BLOB_DATA"); + b.setBytes(1, testData); + } + + this.rs.close(); + this.pstmt.close(); + + this.pstmt = locatorConn.prepareStatement(select); + this.pstmt.setString(1, id); + + this.rs = this.pstmt.executeQuery(); + + byte[] result = null; + if (this.rs.next()) { + Blob b = this.rs.getBlob("BLOB_DATA"); + + result = b.getBytes(1, dataSize - 1); + } + + this.rs.close(); + this.pstmt.close(); + + assertNotNull(result); + + for (int i = 0; i < result.length && i < testData.length; i++) { + // Will print out all of the values that don't match. + // All negative values will instead be replaced with 63. + if (result[i] != testData[i]) { + assertEquals("At position " + i, testData[i], result[i]); + } + } + } + + /** + * Tests fix for BUG#9040 - PreparedStatement.addBatch() doesn't work with + * server-side prepared statements and streaming BINARY data. + * + * @throws Exception + * if the test fails. + */ + public void testBug9040() throws Exception { + + createTable("testBug9040", "(primary_key int not null primary key, data mediumblob)"); + + this.pstmt = this.conn.prepareStatement("replace into testBug9040 (primary_key, data) values(?,?)"); + + int primaryKey = 1; + byte[] data = "First Row".getBytes(); + this.pstmt.setInt(1, primaryKey); + this.pstmt.setBinaryStream(2, new ByteArrayInputStream(data), data.length); + this.pstmt.addBatch(); + + primaryKey = 2; + data = "Second Row".getBytes(); + this.pstmt.setInt(1, primaryKey); + this.pstmt.setBinaryStream(2, new ByteArrayInputStream(data), data.length); + this.pstmt.addBatch(); + + this.pstmt.executeBatch(); + + } + + public void testBug10850() throws Exception { + String tableName = "testBug10850"; + + createTable(tableName, "(field1 TEXT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO " + + + tableName + " VALUES (?)"); + this.pstmt.setCharacterStream(1, new StringReader(""), 0); + this.pstmt.executeUpdate(); + + assertEquals("0", getSingleIndexedValueWithQuery(1, "SELECT LENGTH(field1) FROM " + tableName).toString()); + this.stmt.executeUpdate("TRUNCATE TABLE " + tableName); + + this.pstmt.clearParameters(); + this.pstmt.setBinaryStream(1, new ByteArrayInputStream(new byte[0]), 0); + this.pstmt.executeUpdate(); + + assertEquals("0", getSingleIndexedValueWithQuery(1, "SELECT LENGTH(field1) FROM " + tableName).toString()); + this.stmt.executeUpdate("TRUNCATE TABLE " + tableName); + + } + + public void testBug34677() throws Exception { + createTable("testBug34677", "(field1 BLOB)"); + this.stmt.executeUpdate("INSERT INTO testBug34677 VALUES ('abc')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34677"); + this.rs.next(); + Blob blob = this.rs.getBlob(1); + blob.truncate(0L); + assertEquals(0, blob.length()); + assertEquals(-1, blob.getBinaryStream().read()); + + } + + /** + * Tests fix for BUG#20453671 - CLOB.POSITION() API CALL WITH CLOB INPUT RETURNS EXCEPTION + * + * @throws Exception + * if the test fails. + */ + public void testBug20453671() throws Exception { + this.rs = this.stmt.executeQuery("select 'abcd', 'a', 'b', 'c', 'd', 'e'"); + this.rs.next(); + + final Clob in = this.rs.getClob(1); + final ResultSet locallyScopedRs = this.rs; + assertThrows(SQLException.class, "Illegal starting position for search, '0'", new Callable() { + public Void call() throws Exception { + in.position(locallyScopedRs.getClob(2), 0); + return null; + } + }); + assertThrows(SQLException.class, "Starting position for search is past end of CLOB", new Callable() { + public Void call() throws Exception { + in.position(locallyScopedRs.getClob(2), 10); + return null; + } + }); + + assertEquals(1, in.position(this.rs.getClob(2), 1)); + assertEquals(2, in.position(this.rs.getClob(3), 1)); + assertEquals(3, in.position(this.rs.getClob(4), 1)); + assertEquals(4, in.position(this.rs.getClob(5), 1)); + assertEquals(-1, in.position(this.rs.getClob(6), 1)); + } + + /** + * Tests fix for BUG#20453712 - CLOB.SETSTRING() WITH VALID INPUT RETURNS EXCEPTION + * server-side prepared statements and streaming BINARY data. + * + * @throws Exception + * if the test fails. + */ + public void testBug20453712() throws Exception { + final String s1 = "NewClobData"; + this.rs = this.stmt.executeQuery("select 'a'"); + this.rs.next(); + final Clob c1 = this.rs.getClob(1); + + // check with wrong position + assertThrows(SQLException.class, "Starting position can not be < 1", new Callable() { + public Void call() throws Exception { + c1.setString(0, s1, 7, 4); + return null; + } + }); + + // check with wrong substring index + assertThrows(SQLException.class, "String index out of range: 12", new Callable() { + public Void call() throws Exception { + c1.setString(1, s1, 8, 4); + return null; + } + }); + + // full replace + c1.setString(1, s1, 3, 4); + assertEquals("Clob", c1.getSubString(1L, (int) c1.length())); + + // add + c1.setString(5, s1, 7, 4); + assertEquals("ClobData", c1.getSubString(1L, (int) c1.length())); + + // replace middle chars + c1.setString(2, s1, 7, 4); + assertEquals("CDataata", c1.getSubString(1L, (int) c1.length())); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/CachedRowsetTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/CachedRowsetTest.java new file mode 100644 index 0000000..4aa4311 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/CachedRowsetTest.java @@ -0,0 +1,95 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.lang.reflect.Method; +import java.sql.ResultSet; + +import javax.sql.RowSet; + +import testsuite.BaseTestCase; + +/** + * Regression test cases for the ResultSet class. + */ +public class CachedRowsetTest extends BaseTestCase { + /** + * Creates a new CachedRowsetTest + * + * @param name + * the name of the test to run + */ + public CachedRowsetTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(CachedRowsetTest.class); + } + + /** + * Tests fix for BUG#5188, CachedRowSet errors using PreparedStatement. Uses + * Sun's "com.sun.rowset.CachedRowSetImpl" + * + * @throws Exception + */ + public void testBug5188() throws Exception { + String implClass = "com.sun.rowset.CachedRowSetImpl"; + Class c; + Method populate; + try { + c = Class.forName(implClass); + } catch (ClassNotFoundException e) { + System.out.println("skipping testBug5188. Requires: " + implClass); + return; + } + populate = c.getMethod("populate", new Class[] { ResultSet.class }); + + createTable("testBug5188", "(ID int NOT NULL AUTO_INCREMENT, datafield VARCHAR(64), PRIMARY KEY(ID))"); + + this.stmt.executeUpdate("INSERT INTO testBug5188(datafield) values('test data stuff !')"); + + String sql = "SELECT * FROM testBug5188 where ID = ?"; + this.pstmt = this.conn.prepareStatement(sql); + this.pstmt.setString(1, "1"); + this.rs = this.pstmt.executeQuery(); + + // create a CachedRowSet and populate it + RowSet cachedRowSet = (RowSet) c.newInstance(); + // cachedRowSet.populate(rs); + populate.invoke(cachedRowSet, new Object[] { this.rs }); + + // scroll through CachedRowSet ... + assertTrue(cachedRowSet.next()); + assertEquals("1", cachedRowSet.getString("ID")); + assertEquals("test data stuff !", cachedRowSet.getString("datafield")); + assertFalse(cachedRowSet.next()); + + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/CallableStatementRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/CallableStatementRegressionTest.java new file mode 100644 index 0000000..951e4ce --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/CallableStatementRegressionTest.java @@ -0,0 +1,1596 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.Properties; + +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.SQLError; + +import testsuite.BaseTestCase; + +/** + * Tests fixes for bugs in CallableStatement code. + */ +public class CallableStatementRegressionTest extends BaseTestCase { + public CallableStatementRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + * ignored + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(CallableStatementRegressionTest.class); + } + + /** + * Tests fix for BUG#3539 getProcedures() does not return any procedures in + * result set + * + * @throws Exception + * if an error occurs. + */ + public void testBug3539() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug3539", "()\nBEGIN\nSELECT 1;end\n"); + + this.rs = this.conn.getMetaData().getProcedures(null, null, "testBug3539"); + + assertTrue(this.rs.next()); + assertTrue("testBug3539".equals(this.rs.getString(3))); + } + + /** + * Tests fix for BUG#3540 getProcedureColumns doesn't work with wildcards + * for procedure name + * + * @throws Exception + * if an error occurs. + */ + public void testBug3540() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug3540", "(x int, out y int)\nBEGIN\nSELECT 1;end\n"); + + this.rs = this.conn.getMetaData().getProcedureColumns(null, null, "testBug3540%", "%"); + + assertTrue(this.rs.next()); + assertEquals("testBug3540", this.rs.getString(3)); + assertEquals("x", this.rs.getString(4)); + + assertTrue(this.rs.next()); + assertEquals("testBug3540", this.rs.getString(3)); + assertEquals("y", this.rs.getString(4)); + + assertTrue(!this.rs.next()); + } + + /** + * Tests fix for BUG#7026 - DBMD.getProcedures() doesn't respect catalog + * parameter + * + * @throws Exception + * if the test fails. + */ + public void testBug7026() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug7026", "(x int, out y int)\nBEGIN\nSELECT 1;end\n"); + + // + // Should be found this time. + // + this.rs = this.conn.getMetaData().getProcedures(this.conn.getCatalog(), null, "testBug7026"); + + assertTrue(this.rs.next()); + assertTrue("testBug7026".equals(this.rs.getString(3))); + + assertTrue(!this.rs.next()); + + // + // This time, shouldn't be found, because not associated with this (bogus) catalog + // + this.rs = this.conn.getMetaData().getProcedures("abfgerfg", null, "testBug7026"); + assertTrue(!this.rs.next()); + + // + // Should be found this time as well, as we haven't specified a catalog. + // + this.rs = this.conn.getMetaData().getProcedures(null, null, "testBug7026"); + + assertTrue(this.rs.next()); + assertTrue("testBug7026".equals(this.rs.getString(3))); + + assertTrue(!this.rs.next()); + } + + /** + * Tests fix for BUG#9319 -- Stored procedures with same name in different + * databases confuse the driver when it tries to determine parameter + * counts/types. + * + * @throws Exception + * if the test fails + */ + public void testBug9319() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + boolean doASelect = true; // SELECT currently causes the server to hang on the last execution of this testcase, filed as BUG#9405 + + if (isAdminConnectionConfigured()) { + Connection db2Connection = null; + Connection db1Connection = null; + + db2Connection = getAdminConnection(); + db1Connection = getAdminConnection(); + + Statement db1st = db1Connection.createStatement(); + Statement db2st = db2Connection.createStatement(); + + createDatabase(db2st, "db_9319_2"); + db2Connection.setCatalog("db_9319_2"); + createProcedure(db2st, "COMPROVAR_USUARI", + "(IN p_CodiUsuari VARCHAR(10),\nIN p_contrasenya VARCHAR(10),\nOUT p_userId INTEGER," + + "\nOUT p_userName VARCHAR(30),\nOUT p_administrador VARCHAR(1),\nOUT p_idioma VARCHAR(2))\nBEGIN" + + (doASelect ? "\nselect 2;" : "\nSELECT 2 INTO p_administrador;") + "\nEND"); + + createDatabase(db1st, "db_9319_1"); + db1Connection.setCatalog("db_9319_1"); + createProcedure(db1st, "COMPROVAR_USUARI", + "(IN p_CodiUsuari VARCHAR(10),\nIN p_contrasenya VARCHAR(10),\nOUT p_userId INTEGER," + + "\nOUT p_userName VARCHAR(30),\nOUT p_administrador VARCHAR(1))\nBEGIN" + + (doASelect ? "\nselect 1;" : "\nSELECT 1 INTO p_administrador;") + "\nEND"); + + CallableStatement cstmt = db2Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); + cstmt.setString(1, "abc"); + cstmt.setString(2, "def"); + cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + + cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); + + cstmt.execute(); + + if (doASelect) { + this.rs = cstmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + } else { + assertEquals(2, cstmt.getInt(5)); + } + + cstmt = db1Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); + cstmt.setString(1, "abc"); + cstmt.setString(2, "def"); + cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + + try { + cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); + fail("Should've thrown an exception"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + cstmt = db1Connection.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) }"); + cstmt.setString(1, "abc"); + cstmt.setString(2, "def"); + cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + + cstmt.execute(); + + if (doASelect) { + this.rs = cstmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + } else { + assertEquals(1, cstmt.getInt(5)); + } + + String quoteChar = db2Connection.getMetaData().getIdentifierQuoteString(); + + cstmt = db2Connection.prepareCall( + "{ call " + quoteChar + db1Connection.getCatalog() + quoteChar + "." + quoteChar + "COMPROVAR_USUARI" + quoteChar + "(?, ?, ?, ?, ?) }"); + cstmt.setString(1, "abc"); + cstmt.setString(2, "def"); + cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + + cstmt.execute(); + + if (doASelect) { + this.rs = cstmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + } else { + assertEquals(1, cstmt.getInt(5)); + } + } + } + + /* + * public void testBug9319() throws Exception { boolean doASelect = false; + * // SELECT currently causes the server to hang on the // last execution of + * this testcase, filed as BUG#9405 + * + * if (versionMeetsMinimum(5, 0, 2)) { if (isAdminConnectionConfigured()) { + * Connection db2Connection = null; Connection db1Connection = null; + * + * try { db2Connection = getAdminConnection(); + * + * db2Connection.createStatement().executeUpdate( "CREATE DATABASE IF NOT + * EXISTS db_9319"); db2Connection.setCatalog("db_9319"); + * + * db2Connection.createStatement().executeUpdate( "DROP PROCEDURE IF EXISTS + * COMPROVAR_USUARI"); + * + * db2Connection.createStatement().executeUpdate( "CREATE PROCEDURE + * COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10)," + "\nIN p_contrasenya + * VARCHAR(10)," + "\nOUT p_userId INTEGER," + "\nOUT p_userName + * VARCHAR(30)," + "\nOUT p_administrador VARCHAR(1)," + "\nOUT p_idioma + * VARCHAR(2))" + "\nBEGIN" + (doASelect ? "\nselect 2;" : "\nSELECT 2 INTO + * p_administrador;" ) + "\nEND"); + * + * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI"); + * this.stmt .executeUpdate("CREATE PROCEDURE COMPROVAR_USUARI(IN + * p_CodiUsuari VARCHAR(10)," + "\nIN p_contrasenya VARCHAR(10)," + "\nOUT + * p_userId INTEGER," + "\nOUT p_userName VARCHAR(30)," + "\nOUT + * p_administrador VARCHAR(1))" + "\nBEGIN" + (doASelect ? "\nselect 1;" : + * "\nSELECT 1 INTO p_administrador;" ) + "\nEND"); + * + * CallableStatement cstmt = db2Connection .prepareCall("{ call + * COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); + * cstmt.setString(2, "def"); cstmt.registerOutParameter(3, + * java.sql.Types.INTEGER); cstmt.registerOutParameter(4, + * java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, + * java.sql.Types.VARCHAR); + * + * cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); + * + * cstmt.execute(); + * + * if (doASelect) { this.rs = cstmt.getResultSet(); + * assertTrue(this.rs.next()); assertEquals(2, this.rs.getInt(1)); } else { + * assertEquals(2, cstmt.getInt(5)); } + * + * cstmt = this.conn .prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) + * }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); + * cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + * cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + * cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + * + * try { cstmt.registerOutParameter(6, java.sql.Types.VARCHAR); + * fail("Should've thrown an exception"); } catch (SQLException sqlEx) { + * assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx .getSQLState()); + * } + * + * cstmt = this.conn .prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) + * }"); cstmt.setString(1, "abc"); cstmt.setString(2, "def"); + * cstmt.registerOutParameter(3, java.sql.Types.INTEGER); + * cstmt.registerOutParameter(4, java.sql.Types.VARCHAR); + * cstmt.registerOutParameter(5, java.sql.Types.VARCHAR); + * + * cstmt.execute(); + * + * if (doASelect) { this.rs = cstmt.getResultSet(); + * assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { + * assertEquals(1, cstmt.getInt(5)); } + * + * String quoteChar = + * db2Connection.getMetaData().getIdentifierQuoteString(); + * + * cstmt = db2Connection .prepareCall("{ call " + quoteChar + + * this.conn.getCatalog() + quoteChar + "." + quoteChar + "COMPROVAR_USUARI" + * + quoteChar + "(?, ?, ?, ?, ?) }"); cstmt.setString(1, "abc"); + * cstmt.setString(2, "def"); cstmt.registerOutParameter(3, + * java.sql.Types.INTEGER); cstmt.registerOutParameter(4, + * java.sql.Types.VARCHAR); cstmt.registerOutParameter(5, + * java.sql.Types.VARCHAR); + * + * cstmt.execute(); + * + * if (doASelect) { this.rs = cstmt.getResultSet(); + * assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else { + * assertEquals(1, cstmt.getInt(5)); } } finally { if (db2Connection != + * null) { db2Connection.createStatement().executeUpdate( "DROP PROCEDURE IF + * EXISTS COMPROVAR_USUARI"); // + * db2Connection.createStatement().executeUpdate( // "DROP DATABASE IF + * EXISTS db_9319"); } + * + * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI"); } + * } } } + */ + + /** + * Tests fix for BUG#9682 - Stored procedures with DECIMAL parameters with + * storage specifications that contained "," in them would fail. + * + * @throws Exception + * if the test fails. + */ + public void testBug9682() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug9682", "(decimalParam DECIMAL(18,0))\nBEGIN\n SELECT 1;\nEND"); + + CallableStatement cStmt = null; + + try { + cStmt = this.conn.prepareCall("Call testBug9682(?)"); + cStmt.setDouble(1, 18.0); + cStmt.execute(); + } finally { + if (cStmt != null) { + cStmt.close(); + } + } + } + + /** + * Tests fix forBUG#10310 - Driver doesn't support {?=CALL(...)} for calling + * stored functions. This involved adding support for function retrieval to + * DatabaseMetaData.getProcedures() and getProcedureColumns() as well. + * + * @throws Exception + * if the test fails. + */ + public void testBug10310() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + CallableStatement cStmt = null; + + try { + this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310"); + this.stmt.executeUpdate("CREATE FUNCTION testBug10310(a float, b bigint, c int) RETURNS INT NO SQL\nBEGIN\nRETURN a;\nEND"); + cStmt = this.conn.prepareCall("{? = CALL testBug10310(?,?,?)}"); + cStmt.registerOutParameter(1, Types.INTEGER); + cStmt.setFloat(2, 2); + cStmt.setInt(3, 1); + cStmt.setInt(4, 1); + + assertEquals(4, cStmt.getParameterMetaData().getParameterCount()); + assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1)); + + java.sql.DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = ((com.mysql.jdbc.DatabaseMetaData) dbmd).getFunctionColumns(this.conn.getCatalog(), null, "testBug10310", "%"); + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertEquals(17, rsmd.getColumnCount()); + assertEquals("FUNCTION_CAT", rsmd.getColumnName(1)); + assertEquals("FUNCTION_SCHEM", rsmd.getColumnName(2)); + assertEquals("FUNCTION_NAME", rsmd.getColumnName(3)); + assertEquals("COLUMN_NAME", rsmd.getColumnName(4)); + assertEquals("COLUMN_TYPE", rsmd.getColumnName(5)); + assertEquals("DATA_TYPE", rsmd.getColumnName(6)); + assertEquals("TYPE_NAME", rsmd.getColumnName(7)); + assertEquals("PRECISION", rsmd.getColumnName(8)); + assertEquals("LENGTH", rsmd.getColumnName(9)); + assertEquals("SCALE", rsmd.getColumnName(10)); + assertEquals("RADIX", rsmd.getColumnName(11)); + assertEquals("NULLABLE", rsmd.getColumnName(12)); + assertEquals("REMARKS", rsmd.getColumnName(13)); + assertEquals("CHAR_OCTET_LENGTH", rsmd.getColumnName(14)); + assertEquals("ORDINAL_POSITION", rsmd.getColumnName(15)); + assertEquals("IS_NULLABLE", rsmd.getColumnName(16)); + assertEquals("SPECIFIC_NAME", rsmd.getColumnName(17)); + + this.rs.close(); + + assertFalse(cStmt.execute()); + assertEquals(2f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + assertEquals(-1, cStmt.executeUpdate()); + assertEquals(2f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + cStmt.setFloat("a", 4); + cStmt.setInt("b", 1); + cStmt.setInt("c", 1); + + assertFalse(cStmt.execute()); + assertEquals(4f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + assertEquals(-1, cStmt.executeUpdate()); + assertEquals(4f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + // Check metadata while we're at it + + this.rs = dbmd.getProcedures(this.conn.getCatalog(), null, "testBug10310"); + this.rs.next(); + assertEquals("testBug10310", this.rs.getString("PROCEDURE_NAME")); + assertEquals(java.sql.DatabaseMetaData.procedureReturnsResult, this.rs.getShort("PROCEDURE_TYPE")); + cStmt.setNull(2, Types.FLOAT); + cStmt.setInt(3, 1); + cStmt.setInt(4, 1); + + assertFalse(cStmt.execute()); + assertEquals(0f, cStmt.getInt(1), .001); + assertEquals(true, cStmt.wasNull()); + assertEquals(null, cStmt.getObject(1)); + assertEquals(true, cStmt.wasNull()); + + assertEquals(-1, cStmt.executeUpdate()); + assertEquals(0f, cStmt.getInt(1), .001); + assertEquals(true, cStmt.wasNull()); + assertEquals(null, cStmt.getObject(1)); + assertEquals(true, cStmt.wasNull()); + + // Check with literals, not all parameters filled! + cStmt = this.conn.prepareCall("{? = CALL testBug10310(4,5,?)}"); + cStmt.registerOutParameter(1, Types.INTEGER); + cStmt.setInt(2, 1); + + assertFalse(cStmt.execute()); + assertEquals(4f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + assertEquals(-1, cStmt.executeUpdate()); + assertEquals(4f, cStmt.getInt(1), .001); + assertEquals("java.lang.Integer", cStmt.getObject(1).getClass().getName()); + + assertEquals(2, cStmt.getParameterMetaData().getParameterCount()); + assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1)); + assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(2)); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + + if (cStmt != null) { + cStmt.close(); + } + + this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310"); + } + } + + /** + * Tests fix for Bug#12417 - stored procedure catalog name is case-sensitive + * on Windows (this is actually a server bug, but we have a workaround in + * place for it now). + * + * @throws Exception + * if the test fails. + */ + public void testBug12417() throws Exception { + if (serverSupportsStoredProcedures() && isServerRunningOnWindows()) { + + createProcedure("testBug12417", "()\nBEGIN\nSELECT 1;end\n"); + + Connection ucCatalogConn = null; + + try { + ucCatalogConn = getConnectionWithProps((Properties) null); + ucCatalogConn.setCatalog(this.conn.getCatalog().toUpperCase()); + ucCatalogConn.prepareCall("{call testBug12417()}"); + } finally { + if (ucCatalogConn != null) { + ucCatalogConn.close(); + } + } + } + } + + public void testBug15121() throws Exception { + if (!this.DISABLED_testBug15121 /* needs to be fixed on server */) { + if (versionMeetsMinimum(5, 0)) { + createProcedure("p_testBug15121", "()\nBEGIN\nSELECT * from idonotexist;\nEND"); + + Properties props = new Properties(); + props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, ""); + + Connection noDbConn = null; + + try { + noDbConn = getConnectionWithProps(props); + + StringBuilder queryBuf = new StringBuilder("{call "); + String quotedId = this.conn.getMetaData().getIdentifierQuoteString(); + queryBuf.append(quotedId); + queryBuf.append(this.conn.getCatalog()); + queryBuf.append(quotedId); + queryBuf.append(".p_testBug15121()}"); + + noDbConn.prepareCall(queryBuf.toString()).execute(); + } finally { + if (noDbConn != null) { + noDbConn.close(); + } + } + } + } + } + + /** + * Tests fix for BUG#15464 - INOUT parameter does not store IN value. + * + * @throws Exception + * if the test fails + */ + + public void testBug15464() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testInOutParam", + "(IN p1 VARCHAR(255), INOUT p2 INT)\nbegin\n DECLARE z INT;\n" + "SET z = p2 + 1;\nSET p2 = z;\nSELECT p1;\nSELECT CONCAT('zyxw', p1);\nend\n"); + + CallableStatement storedProc = null; + + storedProc = this.conn.prepareCall("{call testInOutParam(?, ?)}"); + + storedProc.setString(1, "abcd"); + storedProc.setInt(2, 4); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + assertEquals(5, storedProc.getInt(2)); + } + + /** + * Tests fix for BUG#17898 - registerOutParameter not working when some + * parameters pre-populated. Still waiting for feedback from JDBC experts + * group to determine what correct parameter count from getMetaData() should + * be, however. + * + * @throws Exception + * if the test fails + */ + public void testBug17898() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug17898", "(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\n" + "SELECT 1 INTO rtn;\nSET param2=rtn;\nEND"); + + CallableStatement cstmt = this.conn.prepareCall("{CALL testBug17898('foo', ?)}"); + cstmt.registerOutParameter(1, Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + + cstmt.clearParameters(); + cstmt.registerOutParameter("param2", Types.INTEGER); + cstmt.execute(); + assertEquals(1, cstmt.getInt(1)); + } + + /** + * Tests fix for BUG#21462 - JDBC (and ODBC) specifications allow + * no-parenthesis CALL statements for procedures with no arguments, MySQL + * server does not. + * + * @throws Exception + * if the test fails. + */ + public void testBug21462() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug21462", "() BEGIN SELECT 1; END"); + + CallableStatement cstmt = null; + + try { + cstmt = this.conn.prepareCall("{CALL testBug21462}"); + cstmt.execute(); + } finally { + if (cstmt != null) { + cstmt.close(); + } + } + + } + + /** + * Tests fix for BUG#22024 - Newlines causing whitespace to span confuse + * procedure parser when getting parameter metadata for stored procedures. + * + * @throws Exception + * if the test fails + */ + public void testBug22024() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug22024_1", "(\r\n)\r\n BEGIN SELECT 1; END"); + createProcedure("testBug22024_2", "(\r\na INT)\r\n BEGIN SELECT 1; END"); + + CallableStatement cstmt = null; + + try { + cstmt = this.conn.prepareCall("{CALL testBug22024_1()}"); + cstmt.execute(); + + cstmt = this.conn.prepareCall("{CALL testBug22024_2(?)}"); + cstmt.setInt(1, 1); + cstmt.execute(); + } finally { + if (cstmt != null) { + cstmt.close(); + } + } + + } + + /** + * Tests workaround for server crash when calling stored procedures via a + * server-side prepared statement (driver now detects prepare(stored + * procedure) and substitutes client-side prepared statement). + * + * @throws Exception + * if the test fails + */ + public void testBug22297() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createTable("tblTestBug2297_1", "(id varchar(20) NOT NULL default '',Income double(19,2) default NULL)"); + + createTable("tblTestBug2297_2", "(id varchar(20) NOT NULL default '',CreatedOn datetime default NULL)"); + + createProcedure("testBug22297", "(pcaseid INT) BEGIN\nSET @sql = \"DROP TEMPORARY TABLE IF EXISTS tmpOrders\";" + + " PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;" + + "\nSET @sql = \"CREATE TEMPORARY TABLE tmpOrders SELECT id, 100 AS Income FROM tblTestBug2297_1 GROUP BY id\"; PREPARE stmt FROM @sql;" + + " EXECUTE stmt; DEALLOCATE PREPARE stmt;\n SELECT id, Income FROM (SELECT e.id AS id ,COALESCE(prof.Income,0) AS Income" + + "\n FROM tblTestBug2297_2 e LEFT JOIN tmpOrders prof ON e.id = prof.id\n WHERE e.CreatedOn > '2006-08-01') AS Final ORDER BY id;\nEND"); + + this.stmt.executeUpdate("INSERT INTO tblTestBug2297_1 (`id`,`Income`) VALUES ('a',4094.00),('b',500.00),('c',3462.17), ('d',500.00), ('e',600.00)"); + + this.stmt.executeUpdate("INSERT INTO tblTestBug2297_2 (`id`,`CreatedOn`) VALUES ('d','2006-08-31 00:00:00'),('e','2006-08-31 00:00:00')," + + "('b','2006-08-31 00:00:00'),('c','2006-08-31 00:00:00'),('a','2006-08-31 00:00:00')"); + + this.pstmt = this.conn.prepareStatement("{CALL testBug22297(?)}"); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + + String[] ids = new String[] { "a", "b", "c", "d", "e" }; + int pos = 0; + + while (this.rs.next()) { + assertEquals(ids[pos++], this.rs.getString(1)); + assertEquals(100, this.rs.getInt(2)); + } + + assertTrue(this.pstmt.getClass().getName().indexOf("Server") == -1); + } + + public void testHugeNumberOfParameters() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + StringBuilder procDef = new StringBuilder("(OUT param_0 VARCHAR(32)"); + StringBuilder placeholders = new StringBuilder("?"); + + for (int i = 1; i < 274; i++) { + procDef.append(", OUT param_" + i + " VARCHAR(32)"); + placeholders.append(",?"); + } + procDef.append(")\nBEGIN\nSELECT 1;\nEND"); + + createProcedure("testHugeNumberOfParameters", procDef.toString()); + + CallableStatement cStmt = null; + + try { + cStmt = this.conn.prepareCall("{call testHugeNumberOfParameters(" + placeholders.toString() + ")}"); + cStmt.registerOutParameter(274, Types.VARCHAR); + + cStmt.execute(); + } finally { + if (cStmt != null) { + cStmt.close(); + } + } + } + + public void testPrepareOfMultiRs() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("p", "() begin select 1; select 2; end;"); + + PreparedStatement ps = null; + + try { + ps = this.conn.prepareStatement("call p()"); + + ps.execute(); + this.rs = ps.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertTrue(ps.getMoreResults()); + this.rs = ps.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertTrue(!ps.getMoreResults()); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + + if (ps != null) { + ps.close(); + } + } + + } + + /** + * Tests fix for BUG#25379 - INOUT parameters in CallableStatements get + * doubly-escaped. + * + * @throws Exception + * if the test fails. + */ + public void testBug25379() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createTable("testBug25379", "(col char(40))"); + + createProcedure("sp_testBug25379", "(INOUT invalue char(255))\nBEGIN" + "\ninsert into testBug25379(col) values(invalue);\nEND"); + + CallableStatement cstmt = this.conn.prepareCall("{call sp_testBug25379(?)}"); + cstmt.setString(1, "'john'"); + cstmt.executeUpdate(); + assertEquals("'john'", cstmt.getString(1)); + assertEquals("'john'", getSingleValue("testBug25379", "col", "").toString()); + } + + /** + * Tests fix for BUG#25715 - CallableStatements with OUT/INOUT parameters + * that are "binary" have extra 7 bytes (which happens to be the _binary + * introducer!) + * + * @throws Exception + * if the test fails. + */ + public void testBug25715() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; // no stored procs + } + + createProcedure("spbug25715", "(INOUT mblob MEDIUMBLOB) BEGIN SELECT 1 FROM DUAL WHERE 1=0;\nEND"); + CallableStatement cstmt = null; + + try { + cstmt = this.conn.prepareCall("{call spbug25715(?)}"); + + byte[] buf = new byte[65]; + for (int i = 0; i < 65; i++) { + buf[i] = 1; + } + int il = buf.length; + + int[] typesToTest = new int[] { Types.BIT, Types.BINARY, Types.BLOB, Types.JAVA_OBJECT, Types.LONGVARBINARY, Types.VARBINARY }; + + for (int i = 0; i < typesToTest.length; i++) { + + cstmt.setBinaryStream("mblob", new ByteArrayInputStream(buf), buf.length); + cstmt.registerOutParameter("mblob", typesToTest[i]); + + cstmt.executeUpdate(); + + InputStream is = cstmt.getBlob("mblob").getBinaryStream(); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int bytesRead = 0; + byte[] readBuf = new byte[256]; + + while ((bytesRead = is.read(readBuf)) != -1) { + bOut.write(readBuf, 0, bytesRead); + } + + byte[] fromSelectBuf = bOut.toByteArray(); + + int ol = fromSelectBuf.length; + + assertEquals(il, ol); + } + + cstmt.close(); + } finally { + + if (cstmt != null) { + cstmt.close(); + } + } + + } + + protected boolean serverSupportsStoredProcedures() throws SQLException { + return versionMeetsMinimum(5, 0); + } + + public void testBug26143() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; // no stored procedure support + } + + try { + + dropProcedure("testBug26143"); + + this.stmt.executeUpdate("CREATE DEFINER=CURRENT_USER PROCEDURE testBug26143(I INT) COMMENT 'abcdefg'\nBEGIN\nSELECT I * 10;\nEND"); + + this.conn.prepareCall("{call testBug26143(?)").close(); + + } finally { + dropProcedure("testBug26143"); + } + } + + /** + * Tests fix for BUG#26959 - comments confuse procedure parser. + * + * @throws Exception + * if the test fails + */ + public void testBug26959() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createProcedure("testBug26959", + "(_ACTION varchar(20),\n`/*dumb-identifier-1*/` int,\n`#dumb-identifier-2` int,\n`--dumb-identifier-3` int," + + "\n_CLIENT_ID int, -- ABC\n_LOGIN_ID int, # DEF\n_WHERE varchar(2000),\n_SORT varchar(2000)," + + "\n out _SQL varchar(/* inline right here - oh my gosh! */ 8000),\n _SONG_ID int,\n _NOTES varchar(2000),\n out _RESULT varchar(10)" + + "\n /*\n , -- Generic result parameter" + + "\n out _PERIOD_ID int, -- Returns the period_id. Useful when using @PREDEFLINK to return which is the last period" + + "\n _SONGS_LIST varchar(8000),\n _COMPOSERID int,\n _PUBLISHERID int," + + "\n _PREDEFLINK int -- If the user is accessing through a predefined link: 0=none 1=last period\n */) BEGIN SELECT 1; END"); + + createProcedure("testBug26959_1", "(`/*id*/` /* before type 1 */ varchar(20)," + + "/* after type 1 */ OUT result2 DECIMAL(/*size1*/10,/*size2*/2) /* p2 */)BEGIN SELECT action, result; END"); + + this.conn.prepareCall("{call testBug26959(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}").close(); + this.rs = this.conn.getMetaData().getProcedureColumns(this.conn.getCatalog(), null, "testBug26959", "%"); + + String[] parameterNames = new String[] { "_ACTION", "/*dumb-identifier-1*/", "#dumb-identifier-2", "--dumb-identifier-3", "_CLIENT_ID", "_LOGIN_ID", + "_WHERE", "_SORT", "_SQL", "_SONG_ID", "_NOTES", "_RESULT" }; + + int[] parameterTypes = new int[] { Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.VARCHAR, + Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR, Types.VARCHAR }; + + int[] direction = new int[] { java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, + java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, + java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, + java.sql.DatabaseMetaData.procedureColumnOut, java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnIn, + java.sql.DatabaseMetaData.procedureColumnOut }; + + int[] precision = new int[] { 20, 10, 10, 10, 10, 10, 2000, 2000, 8000, 10, 2000, 10 }; + + int index = 0; + + while (this.rs.next()) { + assertEquals(parameterNames[index], this.rs.getString("COLUMN_NAME")); + assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE")); + + switch (index) { + case 0: + case 6: + case 7: + case 8: + case 10: + case 11: + assertEquals(precision[index], this.rs.getInt("LENGTH")); + break; + default: + assertEquals(precision[index], this.rs.getInt("PRECISION")); + } + + assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE")); + index++; + } + + this.rs.close(); + + index = 0; + parameterNames = new String[] { "/*id*/", "result2" }; + parameterTypes = new int[] { Types.VARCHAR, Types.DECIMAL }; + precision = new int[] { 20, 10 }; + direction = new int[] { java.sql.DatabaseMetaData.procedureColumnIn, java.sql.DatabaseMetaData.procedureColumnOut }; + int[] scale = new int[] { 0, 2 }; + + this.conn.prepareCall("{call testBug26959_1(?, ?)}").close(); + + this.rs = this.conn.getMetaData().getProcedureColumns(this.conn.getCatalog(), null, "testBug26959_1", "%"); + + while (this.rs.next()) { + assertEquals(parameterNames[index], this.rs.getString("COLUMN_NAME")); + assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE")); + switch (index) { + case 0: + case 6: + case 7: + case 8: + case 10: + case 11: + assertEquals(precision[index], this.rs.getInt("LENGTH")); + break; + default: + assertEquals(precision[index], this.rs.getInt("PRECISION")); + } + assertEquals(scale[index], this.rs.getInt("SCALE")); + assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE")); + + index++; + } + } + + /** + * Tests fix for BUG#27400 - CALL [comment] some_proc() doesn't work + */ + public void testBug27400() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; // SPs not supported + } + + createProcedure("testBug27400", "(a INT, b VARCHAR(32)) BEGIN SELECT 1; END"); + + CallableStatement cStmt = null; + + try { + cStmt = this.conn.prepareCall("{CALL /* SOME COMMENT */ testBug27400( /* does this work too? */ ?, ?)} # and a commented ? here too"); + assertTrue(cStmt.toString().indexOf("/*") != -1); // we don't want + // to strip the + // comments + cStmt.setInt(1, 1); + cStmt.setString(2, "bleh"); + cStmt.execute(); + } finally { + if (cStmt != null) { + cStmt.close(); + } + } + } + + /** + * Tests fix for BUG#28689 - CallableStatement.executeBatch() doesn't work + * when connection property "noAccessToProcedureBodies" has been set to + * "true". + * + * The fix involves changing the behavior of "noAccessToProcedureBodies", in + * that the driver will now report all paramters as "IN" paramters but allow + * callers to call registerOutParameter() on them. + * + * @throws Exception + */ + public void testBug28689() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; // no stored procedures + } + + createTable("testBug28689", "(" + + + "`id` int(11) NOT NULL auto_increment,`usuario` varchar(255) default NULL,PRIMARY KEY (`id`))"); + + this.stmt.executeUpdate("INSERT INTO testBug28689 (usuario) VALUES ('AAAAAA')"); + + createProcedure("sp_testBug28689", "(tid INT)\nBEGIN\nUPDATE testBug28689 SET usuario = 'BBBBBB' WHERE id = tid;\nEND"); + + Connection noProcedureBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true"); + CallableStatement cStmt = null; + + try { + cStmt = noProcedureBodiesConn.prepareCall("{CALL sp_testBug28689(?)}"); + cStmt.setInt(1, 1); + cStmt.addBatch(); + cStmt.executeBatch(); + + assertEquals("BBBBBB", getSingleIndexedValueWithQuery(noProcedureBodiesConn, 1, "SELECT `usuario` FROM testBug28689 WHERE id=1")); + } finally { + if (cStmt != null) { + cStmt.close(); + } + + if (noProcedureBodiesConn != null) { + noProcedureBodiesConn.close(); + } + } + } + + /** + * Tests fix for Bug#31823 - CallableStatement.setNull() on a stored + * function would throw an ArrayIndexOutOfBounds when setting the last + * parameter to null when calling setNull(). + * + * @throws Exception + */ + public void testBug31823() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; // no stored functions + } + + createTable("testBug31823", "(value_1 BIGINT PRIMARY KEY,value_2 VARCHAR(20))"); + + createFunction("f_testBug31823", "(value_1_v BIGINT,value_2_v VARCHAR(20)) RETURNS BIGINT " + + "DETERMINISTIC MODIFIES SQL DATA BEGIN INSERT INTO testBug31823 VALUES (value_1_v,value_2_v); RETURN value_1_v; END;"); + + // Prepare the function call + CallableStatement callable = null; + + try { + callable = this.conn.prepareCall("{? = call f_testBug31823(?,?)}"); + + callable.registerOutParameter(1, Types.BIGINT); + + // Add row with non-null value + callable.setLong(2, 1); + callable.setString(3, "Non-null value"); + callable.executeUpdate(); + assertEquals(1, callable.getLong(1)); + + // Add row with null value + callable.setLong(2, 2); + callable.setNull(3, Types.VARCHAR); + callable.executeUpdate(); + assertEquals(2, callable.getLong(1)); + + Method[] setters = CallableStatement.class.getMethods(); + + for (int i = 0; i < setters.length; i++) { + if (setters[i].getName().startsWith("set")) { + Class[] args = setters[i].getParameterTypes(); + + if (args.length == 2 && args[0].equals(Integer.TYPE)) { + if (!args[1].isPrimitive()) { + try { + setters[i].invoke(callable, new Object[] { new Integer(2), null }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + } else { + if (args[1].getName().equals("boolean")) { + try { + setters[i].invoke(callable, new Object[] { new Integer(2), Boolean.FALSE }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + } + + if (args[1].getName().equals("byte")) { + + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Byte((byte) 0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + + } + + if (args[1].getName().equals("double")) { + + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Double(0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + + } + + if (args[1].getName().equals("float")) { + + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Float(0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + + } + + if (args[1].getName().equals("int")) { + + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Integer(0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + + } + + if (args[1].getName().equals("long")) { + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Long(0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + } + + if (args[1].getName().equals("short")) { + try { + setters[i].invoke(callable, new Object[] { new Integer(2), new Short((short) 0) }); + } catch (InvocationTargetException ive) { + if (!(ive.getCause() instanceof com.mysql.jdbc.NotImplemented + || ive.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException"))) { + throw ive; + } + } + } + } + } + } + } + } finally { + if (callable != null) { + callable.close(); + } + } + } + + /** + * Tests fix for Bug#32246 - When unpacking rows directly, we don't hand off + * error message packets to the internal method which decodes them + * correctly, so no exception is rasied, and the driver than hangs trying to + * read rows that aren't there... + * + * @throws Exception + * if the test fails + */ + public void testBug32246() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + dropTable("test_table_2"); + dropTable("test_table_1"); + doBug32246(this.conn); + dropTable("test_table_2"); + dropTable("test_table_1"); + doBug32246(getConnectionWithProps("useDirectRowUnpack=false")); + } + + private void doBug32246(Connection aConn) throws SQLException { + createTable("test_table_1", "(value_1 BIGINT PRIMARY KEY) ENGINE=InnoDB"); + this.stmt.executeUpdate("INSERT INTO test_table_1 VALUES (1)"); + createTable("test_table_2", "(value_2 BIGINT PRIMARY KEY) ENGINE=InnoDB"); + this.stmt.executeUpdate("DROP FUNCTION IF EXISTS test_function"); + createFunction("test_function", "() RETURNS BIGINT DETERMINISTIC MODIFIES SQL DATA BEGIN DECLARE max_value BIGINT; " + + "SELECT MAX(value_1) INTO max_value FROM test_table_2; RETURN max_value; END;"); + + CallableStatement callable = null; + + try { + callable = aConn.prepareCall("{? = call test_function()}"); + + callable.registerOutParameter(1, Types.BIGINT); + + try { + callable.executeUpdate(); + fail("impossible; we should never get here."); + } catch (SQLException sqlEx) { + assertEquals("42S22", sqlEx.getSQLState()); + } + + createTable("test_table_1", "(value_1 BIGINT PRIMARY KEY) ENGINE=InnoDB"); + this.stmt.executeUpdate("INSERT INTO test_table_1 VALUES (1)"); + createTable("test_table_2", + "(value_2 BIGINT PRIMARY KEY, " + " FOREIGN KEY (value_2) REFERENCES test_table_1 (value_1) ON DELETE CASCADE) ENGINE=InnoDB"); + createFunction("test_function", + "(value BIGINT) RETURNS BIGINT DETERMINISTIC MODIFIES SQL DATA BEGIN " + "INSERT INTO test_table_2 VALUES (value); RETURN value; END;"); + + callable = aConn.prepareCall("{? = call test_function(?)}"); + callable.registerOutParameter(1, Types.BIGINT); + + callable.setLong(2, 1); + callable.executeUpdate(); + + callable.setLong(2, 2); + + try { + callable.executeUpdate(); + fail("impossible; we should never get here."); + } catch (SQLException sqlEx) { + assertEquals("23000", sqlEx.getSQLState()); + } + } finally { + if (callable != null) { + callable.close(); + } + } + } + + public void testBitSp() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("`Bit_Tab`", "( `MAX_VAL` tinyint(1) default NULL, `MIN_VAL` tinyint(1) default NULL, `NULL_VAL` tinyint(1) default NULL)"); + + createProcedure("Bit_Proc", "(out MAX_PARAM TINYINT, out MIN_PARAM TINYINT, out NULL_PARAM TINYINT)" + + "begin select MAX_VAL, MIN_VAL, NULL_VAL into MAX_PARAM, MIN_PARAM, NULL_PARAM from Bit_Tab; end"); + + Boolean minBooleanVal; + Boolean oRetVal; + + String Min_Val_Query = "SELECT MIN_VAL from Bit_Tab"; + //String sMaxBooleanVal = "1"; + // sMaxBooleanVal = "true"; + //Boolean bool = Boolean.valueOf("true"); + String Min_Insert = "insert into Bit_Tab values(1,0,null)"; + // System.out.println("Value to insert=" + extractVal(Min_Insert,1)); + CallableStatement cstmt; + + this.stmt.executeUpdate("delete from Bit_Tab"); + this.stmt.executeUpdate(Min_Insert); + cstmt = this.conn.prepareCall("{call Bit_Proc(?,?,?)}"); + + System.out.println("register the output parameters"); + cstmt.registerOutParameter(1, java.sql.Types.BIT); + cstmt.registerOutParameter(2, java.sql.Types.BIT); + cstmt.registerOutParameter(3, java.sql.Types.BIT); + + System.out.println("execute the procedure"); + cstmt.executeUpdate(); + + System.out.println("invoke getBoolean method"); + boolean bRetVal = cstmt.getBoolean(2); + oRetVal = new Boolean(bRetVal); + minBooleanVal = new Boolean("false"); + this.rs = this.stmt.executeQuery(Min_Val_Query); + if (oRetVal.equals(minBooleanVal)) { + System.out.println("getBoolean returns the Minimum value "); + } else { + System.out.println("getBoolean() did not return the Minimum value, getBoolean Failed!"); + + } + } + + public void testNotReallyCallableStatement() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + CallableStatement cstmt = null; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testNotReallyCallableStatement"); + cstmt = this.conn.prepareCall("CREATE TABLE testNotReallyCallableStatement(field1 INT)"); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testNotReallyCallableStatement"); + + if (cstmt != null) { + cstmt.close(); + } + } + } + + public void testBug35199() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createFunction("test_function", "(a varchar(40), b bigint(20), c varchar(80)) RETURNS bigint(20) LANGUAGE SQL DETERMINISTIC " + + "MODIFIES SQL DATA COMMENT 'bbb' BEGIN RETURN 1; END; "); + + CallableStatement callable = null; + try { + callable = this.conn.prepareCall("{? = call test_function(?,101,?)}"); + callable.registerOutParameter(1, Types.BIGINT); + + callable.setString(2, "FOO"); + callable.setString(3, "BAR"); + callable.executeUpdate(); + } finally { + if (callable != null) { + callable.close(); + } + } + } + + public void testBug49831() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + createTable("testBug49831", "(val varchar(32))"); + + createProcedure("pTestBug49831", "(testval varchar(32)) BEGIN insert into testBug49831 (val) values (testval);END;"); + + execProcBug49831(this.conn); + this.stmt.execute("TRUNCATE TABLE testBug49831"); + assertEquals(0, getRowCount("testBug49831")); + Connection noBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true,jdbcCompliantTruncation=false,characterEncoding=utf8,useUnicode=yes"); + try { + execProcBug49831(noBodiesConn); + } finally { + noBodiesConn.close(); + } + + } + + private void execProcBug49831(Connection c) throws Exception { + CallableStatement cstmt = c.prepareCall("{call pTestBug49831(?)}"); + cstmt.setObject(1, "abc", Types.VARCHAR, 32); + cstmt.addBatch(); + cstmt.setObject(1, "def", Types.VARCHAR, 32); + cstmt.addBatch(); + cstmt.executeBatch(); + assertEquals(2, getRowCount("testBug49831")); + this.rs = this.stmt.executeQuery("SELECT * from testBug49831 ORDER BY VAL ASC"); + this.rs.next(); + assertEquals("abc", this.rs.getString(1)); + this.rs.next(); + assertEquals("def", this.rs.getString(1)); + } + + public void testBug43576() throws Exception { + createTable("TMIX91P", + "(F01SMALLINT SMALLINT NOT NULL, F02INTEGER INTEGER,F03REAL REAL," + + "F04FLOAT FLOAT,F05NUMERIC31X4 NUMERIC(31,4), F06NUMERIC16X16 NUMERIC(16,16), F07CHAR_10 CHAR(10)," + + " F08VARCHAR_10 VARCHAR(10), F09CHAR_20 CHAR(20), F10VARCHAR_20 VARCHAR(20), F11DATE DATE," + + " F12DATETIME DATETIME, PRIMARY KEY (F01SMALLINT))"); + + this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (1,1,1234567.12,1234567.12,111111111111111111111111111.1111,.111111111111111,'1234567890'," + + "'1234567890','CHAR20CHAR20','VARCHAR20ABCD','2001-01-01','2001-01-01 01:01:01.111')"); + + this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (7,1,1234567.12,1234567.12,22222222222.0001,.99999999999,'1234567896','1234567896','CHAR20'," + + "'VARCHAR20ABCD','2001-01-01','2001-01-01 01:01:01.111')"); + + this.stmt.executeUpdate("INSERT INTO TMIX91P VALUES (12,12,1234567.12,1234567.12,111222333.4444,.1234567890,'2234567891','2234567891','CHAR20'," + + "'VARCHAR20VARCHAR20','2001-01-01','2001-01-01 01:01:01.111')"); + + createProcedure("MSQSPR100", + "\n( p1_in INTEGER , p2_in CHAR(20), OUT p3_out INTEGER, OUT p4_out CHAR(11))\nBEGIN " + + "\n SELECT F01SMALLINT,F02INTEGER, F11DATE,F12DATETIME,F03REAL \n FROM TMIX91P WHERE F02INTEGER = p1_in; " + + "\n SELECT F02INTEGER,F07CHAR_10,F08VARCHAR_10,F09CHAR_20 \n FROM TMIX91P WHERE F09CHAR_20 = p2_in ORDER BY F02INTEGER ; " + + "\n SET p3_out = 144; \n SET p4_out = 'CHARACTER11'; \n SELECT p3_out, p4_out; END"); + + String sql = "{call MSQSPR100(1,'CHAR20',?,?)}"; + + CallableStatement cs = this.conn.prepareCall(sql); + + cs.registerOutParameter(1, Types.INTEGER); + cs.registerOutParameter(2, Types.CHAR); + + cs.execute(); + cs.close(); + + createProcedure("bug43576_1", "(OUT nfact VARCHAR(100), IN ccuenta VARCHAR(100),\nOUT ffact VARCHAR(100),\nOUT fdoc VARCHAR(100))\nBEGIN" + + "\nSET nfact = 'ncfact string';\nSET ffact = 'ffact string';\nSET fdoc = 'fdoc string';\nEND"); + + createProcedure("bug43576_2", "(IN ccuent1 VARCHAR(100), IN ccuent2 VARCHAR(100),\nOUT nfact VARCHAR(100),\nOUT ffact VARCHAR(100)," + + "\nOUT fdoc VARCHAR(100))\nBEGIN\nSET nfact = 'ncfact string';\nSET ffact = 'ffact string';\nSET fdoc = 'fdoc string';\nEND"); + + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "true"); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + conn1 = getConnectionWithProps(props); + try { + CallableStatement callSt = conn1.prepareCall("{ call bug43576_1(?, ?, ?, ?) }"); + callSt.setString(2, "xxx"); + callSt.registerOutParameter(1, java.sql.Types.VARCHAR); + callSt.registerOutParameter(3, java.sql.Types.VARCHAR); + callSt.registerOutParameter(4, java.sql.Types.VARCHAR); + callSt.execute(); + + assertEquals("ncfact string", callSt.getString(1)); + assertEquals("ffact string", callSt.getString(3)); + assertEquals("fdoc string", callSt.getString(4)); + + CallableStatement callSt2 = conn1.prepareCall("{ call bug43576_2(?, ?, ?, ?, ?) }"); + callSt2.setString(1, "xxx"); + callSt2.setString(2, "yyy"); + callSt2.registerOutParameter(3, java.sql.Types.VARCHAR); + callSt2.registerOutParameter(4, java.sql.Types.VARCHAR); + callSt2.registerOutParameter(5, java.sql.Types.VARCHAR); + callSt2.execute(); + + assertEquals("ncfact string", callSt2.getString(3)); + assertEquals("ffact string", callSt2.getString(4)); + assertEquals("fdoc string", callSt2.getString(5)); + + CallableStatement callSt3 = conn1.prepareCall("{ call bug43576_2(?, 'yyy', ?, ?, ?) }"); + callSt3.setString(1, "xxx"); + // callSt3.setString(2, "yyy"); + callSt3.registerOutParameter(2, java.sql.Types.VARCHAR); + callSt3.registerOutParameter(3, java.sql.Types.VARCHAR); + callSt3.registerOutParameter(4, java.sql.Types.VARCHAR); + callSt3.execute(); + + assertEquals("ncfact string", callSt3.getString(2)); + assertEquals("ffact string", callSt3.getString(3)); + assertEquals("fdoc string", callSt3.getString(4)); + } finally { + conn1.close(); + } + } + + /** + * Tests fix for Bug#57022 - cannot execute a store procedure with output + * parameters Problem was in CallableStatement.java, private void + * determineParameterTypes() throws SQLException if (procName.indexOf(".") + * == -1) { useCatalog = true; } The fix will be to "sanitize" db.sp call + * just like in noAccessToProcedureBodies. + * + * @throws Exception + * if the test fails + */ + + public void testBug57022() throws Exception { + if (!serverSupportsStoredProcedures()) { + return; + } + + String originalCatalog = this.conn.getCatalog(); + + createDatabase("bug57022"); + + createProcedure("bug57022.procbug57022", "(x int, out y int)\nbegin\ndeclare z int;\nset z = x+1, y = z;\nend\n"); + + CallableStatement cStmt = null; + try { + cStmt = this.conn.prepareCall("{call `bug57022`.`procbug57022`(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + cStmt.clearParameters(); + cStmt.close(); + + this.conn.setCatalog("bug57022"); + cStmt = this.conn.prepareCall("{call bug57022.procbug57022(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + cStmt.clearParameters(); + cStmt.close(); + + this.conn.setCatalog("mysql"); + cStmt = this.conn.prepareCall("{call `bug57022`.`procbug57022`(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + } finally { + if (cStmt != null) { + cStmt.clearParameters(); + cStmt.close(); + } + this.conn.setCatalog(originalCatalog); + } + + } + + /** + * Tests fix for BUG#60816 - Cannot pass NULL to an INOUT procedure parameter + * + * @throws Exception + */ + public void testBug60816() throws Exception { + + createProcedure("test60816_1", "(INOUT x INTEGER)\nBEGIN\nSET x = x + 1;\nEND"); + createProcedure("test60816_2", "(x INTEGER, OUT y INTEGER)\nBEGIN\nSET y = x + 1;\nEND"); + createProcedure("test60816_3", "(INOUT x INTEGER)\nBEGIN\nSET x = 10;\nEND"); + + CallableStatement call = this.conn.prepareCall("{ call test60816_1(?) }"); + call.setInt(1, 1); + call.registerOutParameter(1, Types.INTEGER); + call.execute(); + assertEquals(2, call.getInt(1)); + + call = this.conn.prepareCall("{ call test60816_2(?, ?) }"); + call.setInt(1, 1); + call.registerOutParameter(2, Types.INTEGER); + call.execute(); + assertEquals(2, call.getInt(2)); + + call = this.conn.prepareCall("{ call test60816_2(?, ?) }"); + call.setNull(1, Types.INTEGER); + call.registerOutParameter(2, Types.INTEGER); + call.execute(); + assertEquals(0, call.getInt(2)); + assertTrue(call.wasNull()); + + call = this.conn.prepareCall("{ call test60816_1(?) }"); + call.setNull(1, Types.INTEGER); + call.registerOutParameter(1, Types.INTEGER); + call.execute(); + assertEquals(0, call.getInt(1)); + assertTrue(call.wasNull()); + + call = this.conn.prepareCall("{ call test60816_3(?) }"); + call.setNull(1, Types.INTEGER); + call.registerOutParameter(1, Types.INTEGER); + call.execute(); + assertEquals(10, call.getInt(1)); + + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/CharsetRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/CharsetRegressionTest.java new file mode 100644 index 0000000..9b55ef5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/CharsetRegressionTest.java @@ -0,0 +1,121 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.ResultSetInternalMethods; + +import testsuite.BaseStatementInterceptor; +import testsuite.BaseTestCase; + +public class CharsetRegressionTest extends BaseTestCase { + + public CharsetRegressionTest(String name) { + super(name); + } + + /** + * Tests fix for Bug#73663 (19479242), utf8mb4 does not work for connector/j >=5.1.13 + * + * This test is only run when character_set_server=utf8mb4 and collation-server set to one of utf8mb4 collations (it's better to test two configurations: + * with default utf8mb4_general_ci and one of non-default, say utf8mb4_bin) + * + * @throws Exception + */ + public void testBug73663() throws Exception { + + this.rs = this.stmt.executeQuery("show variables like 'collation_server'"); + this.rs.next(); + String collation = this.rs.getString(2); + + if (collation != null && collation.startsWith("utf8mb4") && "utf8mb4".equals(((MySQLConnection) this.conn).getServerVariable("character_set_server"))) { + Properties p = new Properties(); + p.setProperty("characterEncoding", "UTF-8"); + p.setProperty("statementInterceptors", Bug73663StatementInterceptor.class.getName()); + + getConnectionWithProps(p); + // exception will be thrown from the statement interceptor if any "SET NAMES utf8" statement is issued instead of "SET NAMES utf8mb4" + } else { + System.out.println( + "testBug73663 was skipped: This test is only run when character_set_server=utf8mb4 and collation-server set to one of utf8mb4 collations."); + } + } + + /** + * Statement interceptor used to implement preceding test. + */ + public static class Bug73663StatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql.contains("SET NAMES utf8") && !sql.contains("utf8mb4")) { + throw new SQLException("Character set statement issued: " + sql); + } + return null; + } + } + + /** + * Tests fix for Bug#72630 (18758686), NullPointerException during handshake in some situations + * + * @throws Exception + */ + public void testBug72630() throws Exception { + // bug is related to authentication plugins, available only in 5.5.7+ + if (versionMeetsMinimum(5, 5, 7)) { + try { + createUser("'Bug72630User'@'%'", "IDENTIFIED WITH mysql_native_password AS 'pwd'"); + this.stmt.execute("GRANT ALL ON *.* TO 'Bug72630User'@'%'"); + + final Properties props = new Properties(); + props.setProperty("user", "Bug72630User"); + props.setProperty("password", "pwd"); + props.setProperty("characterEncoding", "NonexistentEncoding"); + + assertThrows(SQLException.class, "Unsupported character encoding 'NonexistentEncoding'.", new Callable() { + public Void call() throws Exception { + getConnectionWithProps(props); + return null; + } + }); + + props.remove("characterEncoding"); + props.setProperty("passwordCharacterEncoding", "NonexistentEncoding"); + assertThrows(SQLException.class, "Unsupported character encoding 'NonexistentEncoding' for 'passwordCharacterEncoding' or 'characterEncoding'.", + new Callable() { + public Void call() throws Exception { + getConnectionWithProps(props); + return null; + } + }); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/ConnectionRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/ConnectionRegressionTest.java new file mode 100644 index 0000000..4731056 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/ConnectionRegressionTest.java @@ -0,0 +1,9852 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.management.MBeanServer; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.fabric.jdbc.ErrorReportingExceptionInterceptor; +import com.mysql.jdbc.AuthenticationPlugin; +import com.mysql.jdbc.Buffer; +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.ConnectionGroupManager; +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.Driver; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.LoadBalanceExceptionChecker; +import com.mysql.jdbc.LoadBalancedConnectionProxy; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlDataTruncation; +import com.mysql.jdbc.MysqlErrorNumbers; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.RandomBalanceStrategy; +import com.mysql.jdbc.ReplicationConnection; +import com.mysql.jdbc.ReplicationConnectionGroup; +import com.mysql.jdbc.ReplicationConnectionGroupManager; +import com.mysql.jdbc.ReplicationConnectionProxy; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.SocketMetadata; +import com.mysql.jdbc.StandardSocketFactory; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.TimeUtil; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.authentication.MysqlNativePasswordPlugin; +import com.mysql.jdbc.authentication.Sha256PasswordPlugin; +import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException; +import com.mysql.jdbc.exceptions.MySQLTransientException; +import com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker; +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXid; +import com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection; +import com.mysql.jdbc.jmx.ReplicationGroupManagerMBean; +import com.mysql.jdbc.log.StandardLogger; + +import testsuite.BaseStatementInterceptor; +import testsuite.BaseTestCase; +import testsuite.UnreliableSocketFactory; + +/** + * Regression tests for Connections + */ +public class ConnectionRegressionTest extends BaseTestCase { + /** + * @param name + * the name of the testcase + */ + public ConnectionRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ConnectionRegressionTest.class); + } + + public void testBug1914() throws Exception { + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BIGINT)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BINARY)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BIT)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), CHAR)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DATE)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DECIMAL)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), DOUBLE)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), FLOAT)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), INTEGER)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), LONGVARBINARY)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), LONGVARCHAR)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TIME)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TIMESTAMP)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), TINYINT)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), VARBINARY)}")); + System.out.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), VARCHAR)}")); + } + + /** + * Tests fix for BUG#3554 - Not specifying database in URL causes + * MalformedURL exception. + * + * @throws Exception + * if an error ocurrs. + */ + public void testBug3554() throws Exception { + try { + new NonRegisteringDriver().connect("jdbc:mysql://localhost:3306/?user=root&password=root", new Properties()); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf("Malformed") == -1); + } + } + + public void testBug3790() throws Exception { + String field2OldValue = "foo"; + String field2NewValue = "bar"; + int field1OldValue = 1; + + Connection conn1 = null; + Connection conn2 = null; + Statement stmt1 = null; + Statement stmt2 = null; + ResultSet rs2 = null; + + Properties props = new Properties(); + + try { + createTable("testBug3790", "(field1 INT NOT NULL PRIMARY KEY, field2 VARCHAR(32)) ", "InnoDB"); + this.stmt.executeUpdate("INSERT INTO testBug3790 VALUES (" + field1OldValue + ", '" + field2OldValue + "')"); + + conn1 = getConnectionWithProps(props); // creates a new connection + conn2 = getConnectionWithProps(props); // creates another new + // connection + conn1.setAutoCommit(false); + conn2.setAutoCommit(false); + + stmt1 = conn1.createStatement(); + stmt1.executeUpdate("UPDATE testBug3790 SET field2 = '" + field2NewValue + "' WHERE field1=" + field1OldValue); + conn1.commit(); + + stmt2 = conn2.createStatement(); + + rs2 = stmt2.executeQuery("SELECT field1, field2 FROM testBug3790"); + + assertTrue(rs2.next()); + assertTrue(rs2.getInt(1) == field1OldValue); + assertTrue(rs2.getString(2).equals(field2NewValue)); + } finally { + if (rs2 != null) { + rs2.close(); + } + + if (stmt2 != null) { + stmt2.close(); + } + + if (stmt1 != null) { + stmt1.close(); + } + + if (conn1 != null) { + conn1.close(); + } + + if (conn2 != null) { + conn2.close(); + } + } + } + + /** + * Tests if the driver configures character sets correctly for 4.1.x + * servers. Requires that the 'admin connection' is configured, as this test + * needs to create/drop databases. + * + * @throws Exception + * if an error occurs + */ + public void testCollation41() throws Exception { + if (versionMeetsMinimum(4, 1) && isAdminConnectionConfigured()) { + Map charsetsAndCollations = getCharacterSetsAndCollations(); + charsetsAndCollations.remove("latin7"); // Maps to multiple Java + // charsets + charsetsAndCollations.remove("ucs2"); // can't be used as a + // connection charset + + for (String charsetName : charsetsAndCollations.keySet()) { + Connection charsetConn = null; + Statement charsetStmt = null; + + try { + //String collationName = charsetsAndCollations.get(charsetName); + + Properties props = new Properties(); + props.put("characterEncoding", charsetName); + + System.out.println("Testing character set " + charsetName); + + charsetConn = getAdminConnectionWithProps(props); + + charsetStmt = charsetConn.createStatement(); + + charsetStmt.executeUpdate("DROP DATABASE IF EXISTS testCollation41"); + charsetStmt.executeUpdate("DROP TABLE IF EXISTS testCollation41"); + + charsetStmt.executeUpdate("CREATE DATABASE testCollation41 DEFAULT CHARACTER SET " + charsetName); + charsetConn.setCatalog("testCollation41"); + + // We've switched catalogs, so we need to recreate the + // statement to pick this up... + charsetStmt = charsetConn.createStatement(); + + StringBuilder createTableCommand = new StringBuilder("CREATE TABLE testCollation41(field1 VARCHAR(255), field2 INT)"); + + charsetStmt.executeUpdate(createTableCommand.toString()); + + charsetStmt.executeUpdate("INSERT INTO testCollation41 VALUES ('abc', 0)"); + + int updateCount = charsetStmt.executeUpdate("UPDATE testCollation41 SET field2=1 WHERE field1='abc'"); + assertTrue(updateCount == 1); + } finally { + if (charsetStmt != null) { + charsetStmt.executeUpdate("DROP TABLE IF EXISTS testCollation41"); + charsetStmt.executeUpdate("DROP DATABASE IF EXISTS testCollation41"); + charsetStmt.close(); + } + + if (charsetConn != null) { + charsetConn.close(); + } + } + } + } + } + + /** + * Tests setReadOnly() being reset during failover + * + * @throws Exception + * if an error occurs. + */ + public void testSetReadOnly() throws Exception { + Properties props = new Properties(); + props.put("autoReconnect", "true"); + + String sepChar = "?"; + + if (BaseTestCase.dbUrl.indexOf("?") != -1) { + sepChar = "&"; + } + + Connection reconnectableConn = DriverManager.getConnection(BaseTestCase.dbUrl + sepChar + "autoReconnect=true", props); + + this.rs = reconnectableConn.createStatement().executeQuery("SELECT CONNECTION_ID()"); + this.rs.next(); + + String connectionId = this.rs.getString(1); + + reconnectableConn.setReadOnly(true); + + boolean isReadOnly = reconnectableConn.isReadOnly(); + + Connection killConn = getConnectionWithProps((Properties) null); + + killConn.createStatement().executeUpdate("KILL " + connectionId); + Thread.sleep(2000); + + SQLException caughtException = null; + + int numLoops = 8; + + while (caughtException == null && numLoops > 0) { + numLoops--; + + try { + reconnectableConn.createStatement().executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + caughtException = sqlEx; + } + } + + System.out.println("Executing statement on reconnectable connection..."); + + this.rs = reconnectableConn.createStatement().executeQuery("SELECT CONNECTION_ID()"); + this.rs.next(); + assertTrue("Connection is not a reconnected-connection", !connectionId.equals(this.rs.getString(1))); + + try { + reconnectableConn.createStatement().executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + // ignore + } + + this.rs = reconnectableConn.createStatement().executeQuery("SELECT 1"); + + assertTrue(reconnectableConn.isReadOnly() == isReadOnly); + } + + private Map getCharacterSetsAndCollations() throws Exception { + Map charsetsToLoad = new HashMap(); + + try { + this.rs = this.stmt.executeQuery("SHOW character set"); + + while (this.rs.next()) { + charsetsToLoad.put(this.rs.getString("Charset"), this.rs.getString("Default collation")); + } + + // + // These don't have mappings in Java... + // + charsetsToLoad.remove("swe7"); + charsetsToLoad.remove("hp8"); + charsetsToLoad.remove("dec8"); + charsetsToLoad.remove("koi8u"); + charsetsToLoad.remove("keybcs2"); + charsetsToLoad.remove("geostd8"); + charsetsToLoad.remove("armscii8"); + } finally { + if (this.rs != null) { + this.rs.close(); + } + } + + return charsetsToLoad; + } + + /** + * Tests fix for BUG#4334, port #'s not being picked up for + * failover/autoreconnect. + * + * @throws Exception + * if an error occurs. + */ + public void testBug4334() throws Exception { + if (isAdminConnectionConfigured()) { + Connection adminConnection = null; + + try { + adminConnection = getAdminConnection(); + + int bogusPortNumber = 65534; + + NonRegisteringDriver driver = new NonRegisteringDriver(); + + Properties oldProps = driver.parseURL(BaseTestCase.dbUrl, null); + + String host = driver.host(oldProps); + int port = driver.port(oldProps); + String database = oldProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + String user = oldProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + String password = oldProps.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + + StringBuilder newUrlToTestPortNum = new StringBuilder("jdbc:mysql://"); + + if (host != null) { + newUrlToTestPortNum.append(host); + } + + newUrlToTestPortNum.append(":").append(port); + newUrlToTestPortNum.append(","); + + if (host != null) { + newUrlToTestPortNum.append(host); + } + + newUrlToTestPortNum.append(":").append(bogusPortNumber); + newUrlToTestPortNum.append("/"); + + if (database != null) { + newUrlToTestPortNum.append(database); + } + + if ((user != null) || (password != null)) { + newUrlToTestPortNum.append("?"); + + if (user != null) { + newUrlToTestPortNum.append("user=").append(user); + + if (password != null) { + newUrlToTestPortNum.append("&"); + } + } + + if (password != null) { + newUrlToTestPortNum.append("password=").append(password); + } + } + + Properties autoReconnectProps = new Properties(); + autoReconnectProps.put("autoReconnect", "true"); + + System.out.println(newUrlToTestPortNum); + + // + // First test that port #'s are being correctly picked up + // + // We do this by looking at the error message that is returned + // + Connection portNumConn = DriverManager.getConnection(newUrlToTestPortNum.toString(), autoReconnectProps); + Statement portNumStmt = portNumConn.createStatement(); + this.rs = portNumStmt.executeQuery("SELECT connection_id()"); + this.rs.next(); + + killConnection(adminConnection, this.rs.getString(1)); + + try { + portNumStmt.executeQuery("SELECT connection_id()"); + } catch (SQLException sqlEx) { + // we expect this one + } + + try { + portNumStmt.executeQuery("SELECT connection_id()"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().toLowerCase().indexOf("connection refused") != -1); + } + + // + // Now make sure failover works + // + StringBuilder newUrlToTestFailover = new StringBuilder("jdbc:mysql://"); + + if (host != null) { + newUrlToTestFailover.append(host); + } + + newUrlToTestFailover.append(":").append(port); + newUrlToTestFailover.append(","); + + if (host != null) { + newUrlToTestFailover.append(host); + } + + newUrlToTestFailover.append(":").append(bogusPortNumber); + newUrlToTestFailover.append("/"); + + if (database != null) { + newUrlToTestFailover.append(database); + } + + if ((user != null) || (password != null)) { + newUrlToTestFailover.append("?"); + + if (user != null) { + newUrlToTestFailover.append("user=").append(user); + + if (password != null) { + newUrlToTestFailover.append("&"); + } + } + + if (password != null) { + newUrlToTestFailover.append("password=").append(password); + } + } + + Connection failoverConn = DriverManager.getConnection(newUrlToTestFailover.toString(), autoReconnectProps); + Statement failoverStmt = failoverConn.createStatement(); + this.rs = failoverStmt.executeQuery("SELECT connection_id()"); + this.rs.next(); + + killConnection(adminConnection, this.rs.getString(1)); + + try { + failoverStmt.executeQuery("SELECT connection_id()"); + } catch (SQLException sqlEx) { + // we expect this one + } + + this.rs = failoverStmt.executeQuery("SELECT connection_id()"); + } finally { + if (adminConnection != null) { + adminConnection.close(); + } + } + } + } + + private static void killConnection(Connection adminConn, String threadId) throws SQLException { + adminConn.createStatement().execute("KILL " + threadId); + } + + /** + * Tests fix for BUG#6966, connections starting up failed-over (due to down + * master) never retry master. + * + * @throws Exception + * if the test fails...Note, test is timing-dependent, but + * should work in most cases. + */ + public void testBug6966() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "true"); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); + + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); + + props.setProperty("queriesBeforeRetryMaster", "50"); + props.setProperty("maxReconnects", "1"); + + UnreliableSocketFactory.mapHost("master", host); + UnreliableSocketFactory.mapHost("slave", host); + UnreliableSocketFactory.downHost("master"); + + Connection failoverConnection = null; + + try { + failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); + failoverConnection.setAutoCommit(false); + + String originalConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + + for (int i = 0; i < 50; i++) { + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + } + + UnreliableSocketFactory.dontDownHost("master"); + + failoverConnection.setAutoCommit(true); + + String newConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + + assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); + assertFalse(newConnectionId.equals(originalConnectionId)); + + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + } finally { + UnreliableSocketFactory.flushAllStaticData(); + + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + + /** + * Test fix for BUG#7952 -- Infinite recursion when 'falling back' to master + * in failover configuration. + * + * @throws Exception + * if the tests fails. + */ + public void testBug7952() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "true"); + + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + host = host + ":" + port; + } + + host = host + "," + host; + + props.remove("PORT"); + props.remove("HOST"); + + props.setProperty("queriesBeforeRetryMaster", "10"); + props.setProperty("maxReconnects", "1"); + + Connection failoverConnection = null; + Connection killerConnection = getConnectionWithProps((String) null); + + try { + failoverConnection = getConnectionWithProps("jdbc:mysql://" + host + "/", props); + failoverConnection.setAutoCommit(false); + + String failoverConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + + System.out.println("Connection id: " + failoverConnectionId); + + killConnection(killerConnection, failoverConnectionId); + + Thread.sleep(3000); // This can take some time.... + + try { + failoverConnection.createStatement().executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + assertTrue("08S01".equals(sqlEx.getSQLState())); + } + + ((com.mysql.jdbc.Connection) failoverConnection).setFailedOver(true); + + failoverConnection.setAutoCommit(true); + + String failedConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + System.out.println("Failed over connection id: " + failedConnectionId); + + ((com.mysql.jdbc.Connection) failoverConnection).setFailedOver(true); + + for (int i = 0; i < 30; i++) { + failoverConnection.setAutoCommit(true); + System.out.println(getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()")); + // failoverConnection.createStatement().executeQuery("SELECT + // 1"); + failoverConnection.setAutoCommit(true); + } + + String fallbackConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + System.out.println("fallback connection id: " + fallbackConnectionId); + + /* + * long begin = System.currentTimeMillis(); + * + * failoverConnection.setAutoCommit(true); + * + * long end = System.currentTimeMillis(); + * + * assertTrue("Probably didn't try failing back to the + * master....check test", (end - begin) > 500); + * + * failoverConnection.createStatement().executeQuery("SELECT 1"); + */ + } finally { + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + + /** + * Tests fix for BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as + * aliases for sjis. + * + * @throws Exception + * if the test fails. + */ + public void testBug7607() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Connection ms932Conn = null, cp943Conn = null, shiftJisConn = null, windows31JConn = null; + + try { + Properties props = new Properties(); + props.setProperty("characterEncoding", "MS932"); + + ms932Conn = getConnectionWithProps(props); + + this.rs = ms932Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + String encoding = this.rs.getString(2); + if (!versionMeetsMinimum(5, 0, 3) && !versionMeetsMinimum(4, 1, 11)) { + assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH)); + } else { + assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH)); + } + + this.rs = ms932Conn.createStatement().executeQuery("SELECT 'abc'"); + assertTrue(this.rs.next()); + + String charsetToCheck = "ms932"; + + assertEquals(charsetToCheck, ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toLowerCase(Locale.ENGLISH)); + + try { + ms932Conn.createStatement().executeUpdate("drop table if exists testBug7607"); + ms932Conn.createStatement().executeUpdate("create table testBug7607 (sortCol int, col1 varchar(100) ) character set sjis"); + ms932Conn.createStatement().executeUpdate("insert into testBug7607 values(1, 0x835C)"); // standard + // sjis + ms932Conn.createStatement().executeUpdate("insert into testBug7607 values(2, 0x878A)"); // NEC + // kanji + + this.rs = ms932Conn.createStatement().executeQuery("SELECT col1 FROM testBug7607 ORDER BY sortCol ASC"); + assertTrue(this.rs.next()); + String asString = this.rs.getString(1); + assertTrue("\u30bd".equals(asString)); + + // Can't be fixed unless server is fixed, + // this is fixed in 4.1.7. + + assertTrue(this.rs.next()); + asString = this.rs.getString(1); + assertEquals("\u3231", asString); + } finally { + ms932Conn.createStatement().executeUpdate("drop table if exists testBug7607"); + } + + props = new Properties(); + props.setProperty("characterEncoding", "SHIFT_JIS"); + + shiftJisConn = getConnectionWithProps(props); + + this.rs = shiftJisConn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + encoding = this.rs.getString(2); + assertTrue("sjis".equalsIgnoreCase(encoding)); + + this.rs = shiftJisConn.createStatement().executeQuery("SELECT 'abc'"); + assertTrue(this.rs.next()); + + String charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toUpperCase(Locale.US); + + // assertEquals("SHIFT_JIS", charSetUC); + + props = new Properties(); + props.setProperty("characterEncoding", "WINDOWS-31J"); + + windows31JConn = getConnectionWithProps(props); + + this.rs = windows31JConn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + encoding = this.rs.getString(2); + + if (!versionMeetsMinimum(5, 0, 3) && !versionMeetsMinimum(4, 1, 11)) { + assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH)); + } else { + assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH)); + } + + this.rs = windows31JConn.createStatement().executeQuery("SELECT 'abc'"); + assertTrue(this.rs.next()); + + if (!versionMeetsMinimum(4, 1, 11)) { + assertEquals("sjis".toLowerCase(Locale.ENGLISH), + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toLowerCase(Locale.ENGLISH)); + } else { + assertEquals("windows-31j".toLowerCase(Locale.ENGLISH), + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toLowerCase(Locale.ENGLISH)); + } + + props = new Properties(); + props.setProperty("characterEncoding", "CP943"); + + cp943Conn = getConnectionWithProps(props); + + this.rs = cp943Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + encoding = this.rs.getString(2); + assertTrue("sjis".equalsIgnoreCase(encoding)); + + this.rs = cp943Conn.createStatement().executeQuery("SELECT 'abc'"); + assertTrue(this.rs.next()); + + charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(1).toUpperCase(Locale.US); + + assertEquals("CP943", charSetUC); + + } finally { + if (ms932Conn != null) { + ms932Conn.close(); + } + + if (shiftJisConn != null) { + shiftJisConn.close(); + } + + if (windows31JConn != null) { + windows31JConn.close(); + } + + if (cp943Conn != null) { + cp943Conn.close(); + } + } + } + } + + /** + * In some case Connector/J's round-robin function doesn't work. + * + * I had 2 mysqld, node1 "localhost:3306" and node2 "localhost:3307". + * + * 1. node1 is up, node2 is up + * + * 2. java-program connect to node1 by using properties + * "autoRecconect=true", + * "roundRobinLoadBalance=true","failOverReadOnly=false". + * + * 3. node1 is down, node2 is up + * + * 4. java-program execute a query and fail, but Connector/J's round-robin + * fashion failover work and if java-program retry a query it can succeed + * (connection is change to node2 by Connector/j) + * + * 5. node1 is up, node2 is up + * + * 6. node1 is up, node2 is down + * + * 7. java-program execute a query, but this time Connector/J doesn't work + * althought node1 is up and usable. + * + * + * @throws Exception + */ + + /* + * FIXME: This test is no longer valid with random selection of hosts public + * void testBug8643() throws Exception { if (runMultiHostTests()) { + * Properties defaultProps = getMasterSlaveProps(); + * + * defaultProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + * defaultProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + * + * defaultProps.put("autoReconnect", "true"); + * defaultProps.put("roundRobinLoadBalance", "true"); + * defaultProps.put("failOverReadOnly", "false"); + * + * Connection con = null; try { con = + * DriverManager.getConnection(getMasterSlaveUrl(), defaultProps); Statement + * stmt1 = con.createStatement(); + * + * ResultSet rs1 = stmt1 .executeQuery("show variables like 'port'"); + * rs1.next(); + * + * rs1 = stmt1.executeQuery("select connection_id()"); rs1.next(); String + * originalConnectionId = rs1.getString(1); this.stmt.executeUpdate("kill " + * + originalConnectionId); + * + * int numLoops = 8; + * + * SQLException caughtException = null; + * + * while (caughtException == null && numLoops > 0) { numLoops--; + * + * try { rs1 = stmt1.executeQuery("show variables like 'port'"); } catch + * (SQLException sqlEx) { caughtException = sqlEx; } } + * + * assertNotNull(caughtException); + * + * // failover and retry rs1 = + * stmt1.executeQuery("show variables like 'port'"); + * + * rs1.next(); assertTrue(!((com.mysql.jdbc.Connection) con) + * .isMasterConnection()); + * + * rs1 = stmt1.executeQuery("select connection_id()"); rs1.next(); String + * nextConnectionId = rs1.getString(1); + * assertTrue(!nextConnectionId.equals(originalConnectionId)); + * + * this.stmt.executeUpdate("kill " + nextConnectionId); + * + * numLoops = 8; + * + * caughtException = null; + * + * while (caughtException == null && numLoops > 0) { numLoops--; + * + * try { rs1 = stmt1.executeQuery("show variables like 'port'"); } catch + * (SQLException sqlEx) { caughtException = sqlEx; } } + * + * assertNotNull(caughtException); + * + * // failover and retry rs1 = + * stmt1.executeQuery("show variables like 'port'"); + * + * rs1.next(); assertTrue(((com.mysql.jdbc.Connection) con) + * .isMasterConnection()); + * + * } finally { if (con != null) { try { con.close(); } catch (Exception e) { + * e.printStackTrace(); } } } } } + */ + + /** + * Tests fix for BUG#9206, can not use 'UTF-8' for characterSetResults + * configuration property. + */ + public void testBug9206() throws Exception { + Properties props = new Properties(); + props.setProperty("characterSetResults", "UTF-8"); + getConnectionWithProps(props).close(); + } + + /** + * These two charsets have different names depending on version of MySQL + * server. + * + * @throws Exception + * if the test fails. + */ + public void testNewCharsetsConfiguration() throws Exception { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "EUC_KR"); + getConnectionWithProps(props).close(); + + props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "KOI8_R"); + getConnectionWithProps(props).close(); + } + + /** + * Tests fix for BUG#10144 - Memory leak in ServerPreparedStatement if + * serverPrepare() fails. + */ + + public void testBug10144() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Properties props = new Properties(); + props.setProperty("emulateUnsupportedPstmts", "false"); + props.setProperty("useServerPrepStmts", "true"); + + Connection bareConn = getConnectionWithProps(props); + + int currentOpenStatements = ((com.mysql.jdbc.Connection) bareConn).getActiveStatementCount(); + + try { + bareConn.prepareStatement("Boo!"); + fail("Should not've been able to prepare that one!"); + } catch (SQLException sqlEx) { + assertEquals(currentOpenStatements, ((com.mysql.jdbc.Connection) bareConn).getActiveStatementCount()); + } finally { + bareConn.close(); + } + } + } + + /** + * Tests fix for BUG#10496 - SQLException is thrown when using property + * "characterSetResults" + */ + public void testBug10496() throws Exception { + if (versionMeetsMinimum(5, 0, 3)) { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "WINDOWS-31J"); + props.setProperty("characterSetResults", "WINDOWS-31J"); + getConnectionWithProps(props).close(); + + props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "EUC_JP"); + props.setProperty("characterSetResults", "EUC_JP"); + getConnectionWithProps(props).close(); + } + } + + /** + * Tests fix for BUG#11259, autoReconnect ping causes exception on + * connection startup. + * + * @throws Exception + * if the test fails. + */ + public void testBug11259() throws Exception { + Connection dsConn = null; + try { + Properties props = new Properties(); + props.setProperty("autoReconnect", "true"); + dsConn = getConnectionWithProps(props); + } finally { + if (dsConn != null) { + dsConn.close(); + } + } + } + + /** + * Tests fix for BUG#11879 -- ReplicationConnection won't switch to slave, + * throws "Catalog can't be null" exception. + * + * @throws Exception + * if the test fails + */ + public void testBug11879() throws Exception { + if (runMultiHostTests()) { + Connection replConn = null; + + try { + replConn = getMasterSlaveReplicationConnection(); + replConn.setReadOnly(true); + replConn.setReadOnly(false); + } finally { + if (replConn != null) { + replConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#11976 - maxPerformance.properties mis-spells + * "elideSetAutoCommits". + * + * @throws Exception + * if the test fails. + */ + public void testBug11976() throws Exception { + if (!versionMeetsMinimum(6, 0)) { + return; // server status is broken until MySQL-6.0 + } + + Properties props = new Properties(); + props.setProperty("useConfigs", "maxPerformance"); + + Connection maxPerfConn = getConnectionWithProps(props); + assertEquals(true, ((com.mysql.jdbc.Connection) maxPerfConn).getElideSetAutoCommits()); + } + + /** + * Tests fix for BUG#12218, properties shared between master and slave with + * replication connection. + * + * @throws Exception + * if the test fails. + */ + public void testBug12218() throws Exception { + if (runMultiHostTests()) { + Connection replConn = null; + + try { + replConn = getMasterSlaveReplicationConnection(); + assertTrue(!((MySQLConnection) ((ReplicationConnection) replConn).getMasterConnection()) + .hasSameProperties(((ReplicationConnection) replConn).getSlavesConnection())); + } finally { + if (replConn != null) { + replConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#12229 - explainSlowQueries hangs with server-side + * prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug12229() throws Exception { + createTable("testBug12229", "(`int_field` integer )"); + this.stmt.executeUpdate("insert into testBug12229 values (123456),(1)"); + + Properties props = new Properties(); + props.put("profileSQL", "true"); + props.put("slowQueryThresholdMillis", "0"); + props.put("logSlowQueries", "true"); + props.put("explainSlowQueries", "true"); + props.put("useServerPrepStmts", "true"); + + Connection explainConn = getConnectionWithProps(props); + + this.pstmt = explainConn.prepareStatement("SELECT `int_field` FROM `testBug12229` WHERE `int_field` = ?"); + this.pstmt.setInt(1, 1); + + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + } + + /** + * Tests fix for BUG#12752 - Cp1251 incorrectly mapped to win1251 for + * servers newer than 4.0.x. + * + * @throws Exception + * if the test fails. + */ + public void testBug12752() throws Exception { + Properties props = new Properties(); + props.setProperty("characterEncoding", "Cp1251"); + getConnectionWithProps(props).close(); + } + + /** + * Tests fix for BUG#12753, sessionVariables=....=...., doesn't work as it's + * tokenized incorrectly. + * + * @throws Exception + * if the test fails. + */ + public void testBug12753() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Properties props = new Properties(); + props.setProperty("sessionVariables", "sql_mode=ansi"); + + Connection sessionConn = null; + + try { + sessionConn = getConnectionWithProps(props); + + String sqlMode = getMysqlVariable(sessionConn, "sql_mode"); + assertTrue(sqlMode.indexOf("ANSI") != -1); + } finally { + if (sessionConn != null) { + sessionConn.close(); + sessionConn = null; + } + } + } + } + + /** + * Tests fix for BUG#13048 - maxQuerySizeToLog is not respected. + * + * @throws Exception + * if the test fails + */ + public void testBug13048() throws Exception { + + Connection profileConn = null; + PrintStream oldErr = System.err; + + try { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + System.setErr(new PrintStream(bOut)); + + Properties props = new Properties(); + props.setProperty("profileSQL", "true"); + props.setProperty("maxQuerySizeToLog", "2"); + props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger"); + + profileConn = getConnectionWithProps(props); + + StringBuilder queryBuf = new StringBuilder("SELECT '"); + + for (int i = 0; i < 500; i++) { + queryBuf.append("a"); + } + + queryBuf.append("'"); + + this.rs = profileConn.createStatement().executeQuery(queryBuf.toString()); + this.rs.close(); + + String logString = new String(bOut.toString("ISO8859-1")); + assertTrue(logString.indexOf("... (truncated)") != -1); + + bOut = new ByteArrayOutputStream(); + System.setErr(new PrintStream(bOut)); + + this.rs = profileConn.prepareStatement(queryBuf.toString()).executeQuery(); + logString = new String(bOut.toString("ISO8859-1")); + + assertTrue(logString.indexOf("... (truncated)") != -1); + } finally { + System.setErr(oldErr); + + if (profileConn != null) { + profileConn.close(); + } + + if (this.rs != null) { + ResultSet toClose = this.rs; + this.rs = null; + toClose.close(); + } + } + } + + /** + * Tests fix for BUG#13453 - can't use & or = in URL configuration values + * (we now allow you to use www-form-encoding). + * + * @throws Exception + * if the test fails + */ + public void testBug13453() throws Exception { + StringBuilder urlBuf = new StringBuilder(dbUrl); + + if (dbUrl.indexOf('?') == -1) { + urlBuf.append('?'); + } else { + urlBuf.append('&'); + } + + urlBuf.append("sessionVariables=@testBug13453='%25%26+%3D'"); + + Connection encodedConn = null; + + try { + encodedConn = DriverManager.getConnection(urlBuf.toString(), null); + + this.rs = encodedConn.createStatement().executeQuery("SELECT @testBug13453"); + assertTrue(this.rs.next()); + assertEquals("%& =", this.rs.getString(1)); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + + if (encodedConn != null) { + encodedConn.close(); + } + } + } + + /** + * Tests fix for BUG#15065 - Usage advisor complains about unreferenced + * columns, even though they've been referenced. + * + * @throws Exception + * if the test fails. + */ + public void testBug15065() throws Exception { + createTable("testBug15065", "(field1 int)"); + + this.stmt.executeUpdate("INSERT INTO testBug15065 VALUES (1)"); + + Connection advisorConn = null; + Statement advisorStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger"); + + advisorConn = getConnectionWithProps(props); + advisorStmt = advisorConn.createStatement(); + + Method[] getMethods = ResultSet.class.getMethods(); + + PrintStream oldErr = System.err; + + try { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + System.setErr(new PrintStream(bOut)); + + HashMap methodsToSkipMap = new HashMap(); + + // Needs an actual URL + methodsToSkipMap.put("getURL", null); + + // Java6 JDBC4.0 methods we don't implement + methodsToSkipMap.put("getNCharacterStream", null); + methodsToSkipMap.put("getNClob", null); + methodsToSkipMap.put("getNString", null); + methodsToSkipMap.put("getRowId", null); + methodsToSkipMap.put("getSQLXML", null); + + for (int j = 0; j < 2; j++) { + for (int i = 0; i < getMethods.length; i++) { + String methodName = getMethods[i].getName(); + + if (methodName.startsWith("get") && !methodsToSkipMap.containsKey(methodName)) { + Class[] parameterTypes = getMethods[i].getParameterTypes(); + + if (parameterTypes.length == 1 && parameterTypes[0] == Integer.TYPE) { + if (j == 0) { + this.rs = advisorStmt.executeQuery("SELECT COUNT(*) FROM testBug15065"); + } else { + this.rs = advisorConn.prepareStatement("SELECT COUNT(*) FROM testBug15065").executeQuery(); + } + + this.rs.next(); + + try { + + getMethods[i].invoke(this.rs, new Object[] { new Integer(1) }); + } catch (InvocationTargetException invokeEx) { + // we don't care about bad values, just that + // the + // column gets "touched" + if (!invokeEx.getCause().getClass().isAssignableFrom(java.sql.SQLException.class) + && !invokeEx.getCause().getClass().getName().equals("com.mysql.jdbc.NotImplemented") + && !invokeEx.getCause().getClass().getName().equals("java.sql.SQLFeatureNotSupportedException")) { + throw invokeEx; + } + } + + this.rs.close(); + this.rs = null; + } + } + } + } + + String logOut = bOut.toString("ISO8859-1"); + + if (logOut.indexOf(".Level") != -1) { + return; // we ignore for warnings + } + + assertTrue("Usage advisor complained about columns:\n\n" + logOut, logOut.indexOf("columns") == -1); + } finally { + System.setErr(oldErr); + } + } finally { + if (advisorConn != null) { + advisorConn.close(); + } + } + } + + /** + * Tests fix for BUG#15544, no "dos" character set in MySQL > 4.1.0 + * + * @throws Exception + * if the test fails + */ + public void testBug15544() throws Exception { + Properties props = new Properties(); + props.setProperty("characterEncoding", "Cp437"); + Connection dosConn = null; + + try { + dosConn = getConnectionWithProps(props); + } finally { + if (dosConn != null) { + dosConn.close(); + } + } + } + + public void testCSC5765() throws Exception { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "utf8"); + props.setProperty("characterSetResults", "utf8"); + props.setProperty("connectionCollation", "utf8_bin"); + + Connection utf8Conn = null; + + try { + utf8Conn = getConnectionWithProps(props); + this.rs = utf8Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'character_%'"); + while (this.rs.next()) { + System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); + } + + this.rs = utf8Conn.createStatement().executeQuery("SHOW VARIABLES LIKE 'collation_%'"); + while (this.rs.next()) { + System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); + } + } finally { + if (utf8Conn != null) { + utf8Conn.close(); + } + } + } + + /** + * Tests fix for BUG#15570 - ReplicationConnection incorrectly copies state, + * doesn't transfer connection context correctly when transitioning between + * the same read-only states. + * + * (note, this test will fail if the test user doesn't have permission to + * "USE 'mysql'". + * + * @throws Exception + * if the test fails. + */ + public void testBug15570() throws Exception { + Connection replConn = null; + + try { + replConn = getMasterSlaveReplicationConnection(); + + int masterConnectionId = Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString()); + + replConn.setReadOnly(false); + + assertEquals(masterConnectionId, Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString())); + + String currentCatalog = replConn.getCatalog(); + + replConn.setCatalog(currentCatalog); + assertEquals(currentCatalog, replConn.getCatalog()); + + replConn.setReadOnly(true); + + int slaveConnectionId = Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString()); + + // The following test is okay for now, as the chance of MySQL wrapping the connection id counter during our testsuite is very small. + // As per Bug#21286268 fix a Replication connection first initializes the Slaves sub-connection, then the Masters. + assertTrue("Master id " + masterConnectionId + " is not newer than slave id " + slaveConnectionId, masterConnectionId > slaveConnectionId); + + assertEquals(currentCatalog, replConn.getCatalog()); + + String newCatalog = "mysql"; + + replConn.setCatalog(newCatalog); + assertEquals(newCatalog, replConn.getCatalog()); + + replConn.setReadOnly(true); + assertEquals(newCatalog, replConn.getCatalog()); + + replConn.setReadOnly(false); + assertEquals(masterConnectionId, Integer.parseInt(getSingleIndexedValueWithQuery(replConn, 1, "SELECT CONNECTION_ID()").toString())); + } finally { + if (replConn != null) { + replConn.close(); + } + } + } + + /** + * Tests bug where downed slave caused round robin load balance not to cycle + * back to first host in the list. + * + * @throws Exception + * if the test fails...Note, test is timing-dependent, but + * should work in most cases. + */ + public void testBug23281() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "false"); + props.setProperty("roundRobinLoadBalance", "true"); + props.setProperty("failoverReadOnly", "false"); + props.setProperty("connectTimeout", "5000"); + + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + host = host + ":" + port; + } + + props.remove("PORT"); + props.remove("HOST"); + + StringBuilder newHostBuf = new StringBuilder(); + + newHostBuf.append(host); + + newHostBuf.append(","); + // newHostBuf.append(host); + newHostBuf.append("192.0.2.1"); // non-exsitent machine from RFC3330 + // test network + newHostBuf.append(":65532"); // make sure the slave fails + + props.remove("PORT"); + props.remove("HOST"); + + Connection failoverConnection = null; + + try { + failoverConnection = getConnectionWithProps("jdbc:mysql://" + newHostBuf.toString() + "/", props); + + String originalConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT CONNECTION_ID()").toString(); + + System.out.println(originalConnectionId); + + Connection nextConnection = getConnectionWithProps("jdbc:mysql://" + newHostBuf.toString() + "/", props); + + String nextId = getSingleIndexedValueWithQuery(nextConnection, 1, "SELECT CONNECTION_ID()").toString(); + + System.out.println(nextId); + + } finally { + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + + /** + * Tests to insure proper behavior for BUG#24706. + * + * @throws Exception + * if the test fails. + */ + public void testBug24706() throws Exception { + if (!versionMeetsMinimum(6, 0)) { + return; // server status isn't there to support this feature + } + + Properties props = new Properties(); + props.setProperty("elideSetAutoCommits", "true"); + props.setProperty("logger", "StandardLogger"); + props.setProperty("profileSQL", "true"); + Connection c = null; + + StandardLogger.startLoggingToBuffer(); + + try { + c = getConnectionWithProps(props); + c.setAutoCommit(true); + c.createStatement().execute("SELECT 1"); + c.setAutoCommit(true); + c.setAutoCommit(false); + c.createStatement().execute("SELECT 1"); + c.setAutoCommit(false); + + // We should only see _one_ "set autocommit=" sent to the server + + String log = StandardLogger.getBuffer().toString(); + int searchFrom = 0; + int count = 0; + int found = 0; + + while ((found = log.indexOf("SET autocommit=", searchFrom)) != -1) { + searchFrom = found + 1; + count++; + } + + // The SELECT doesn't actually start a transaction, so being pedantic the driver issues SET autocommit=0 again in this case. + assertEquals(2, count); + } finally { + StandardLogger.dropBuffer(); + + if (c != null) { + c.close(); + } + + } + } + + /** + * Tests fix for BUG#25514 - Timer instance used for + * Statement.setQueryTimeout() created per-connection, rather than per-VM, + * causing memory leak. + * + * @throws Exception + * if the test fails. + */ + public void testBug25514() throws Exception { + + for (int i = 0; i < 10; i++) { + getConnectionWithProps((Properties) null).close(); + } + + ThreadGroup root = Thread.currentThread().getThreadGroup().getParent(); + + while (root.getParent() != null) { + root = root.getParent(); + } + + int numThreadsNamedTimer = findNamedThreadCount(root, "Timer"); + + if (numThreadsNamedTimer == 0) { + numThreadsNamedTimer = findNamedThreadCount(root, "MySQL Statement Cancellation Timer"); + } + + // Notice that this seems impossible to test on JDKs prior to 1.5, as there is no reliable way to find the TimerThread, so we have to rely on new JDKs + // for this test. + assertTrue("More than one timer for cancel was created", numThreadsNamedTimer <= 1); + } + + private int findNamedThreadCount(ThreadGroup group, String nameStart) { + + int count = 0; + + int numThreads = group.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + numThreads = group.enumerate(threads, false); + + for (int i = 0; i < numThreads; i++) { + if (threads[i].getName().startsWith(nameStart)) { + count++; + } + } + + int numGroups = group.activeGroupCount(); + ThreadGroup[] groups = new ThreadGroup[numGroups * 2]; + numGroups = group.enumerate(groups, false); + + for (int i = 0; i < numGroups; i++) { + count += findNamedThreadCount(groups[i], nameStart); + } + + return count; + } + + /** + * Ensures that we don't miss getters/setters for driver properties in + * ConnectionProperties so that names given in documentation work with + * DataSources which will use JavaBean-style names and reflection to set the + * values (and often fail silently! when the method isn't available). + * + * @throws Exception + */ + public void testBug23626() throws Exception { + Class clazz = this.conn.getClass(); + + DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(dbUrl, null); + StringBuilder missingSettersBuf = new StringBuilder(); + StringBuilder missingGettersBuf = new StringBuilder(); + + Class[][] argTypes = { new Class[] { String.class }, new Class[] { Integer.TYPE }, new Class[] { Long.TYPE }, new Class[] { Boolean.TYPE } }; + + for (int i = 0; i < dpi.length; i++) { + + String propertyName = dpi[i].name; + + if (propertyName.equals("HOST") || propertyName.equals("PORT") || propertyName.equals("DBNAME") || propertyName.equals("user") + || propertyName.equals("password")) { + continue; + } + + StringBuilder mutatorName = new StringBuilder("set"); + mutatorName.append(Character.toUpperCase(propertyName.charAt(0))); + mutatorName.append(propertyName.substring(1)); + + StringBuilder accessorName = new StringBuilder("get"); + accessorName.append(Character.toUpperCase(propertyName.charAt(0))); + accessorName.append(propertyName.substring(1)); + + try { + clazz.getMethod(accessorName.toString(), (Class[]) null); + } catch (NoSuchMethodException nsme) { + missingGettersBuf.append(accessorName.toString()); + missingGettersBuf.append("\n"); + } + + boolean foundMethod = false; + + for (int j = 0; j < argTypes.length; j++) { + try { + clazz.getMethod(mutatorName.toString(), argTypes[j]); + foundMethod = true; + break; + } catch (NoSuchMethodException nsme) { + + } + } + + if (!foundMethod) { + missingSettersBuf.append(mutatorName); + missingSettersBuf.append("\n"); + } + } + + assertEquals("Missing setters for listed configuration properties.", "", missingSettersBuf.toString()); + assertEquals("Missing getters for listed configuration properties.", "", missingSettersBuf.toString()); + } + + /** + * Tests fix for BUG#25545 - Client flags not sent correctly during + * handshake when using SSL. + * + * Requires test certificates from testsuite/ssl-test-certs to be installed + * on the server being tested. + * + * @throws Exception + * if the test fails. + */ + public void testBug25545() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createProcedure("testBug25545", "() BEGIN SELECT 1; END"); + + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + + Connection sslConn = null; + + try { + Properties props = new Properties(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + + sslConn = getConnectionWithProps(props); + sslConn.prepareCall("{ call testBug25545()}").execute(); + } finally { + if (sslConn != null) { + sslConn.close(); + } + } + } + + /** + * Tests fix for BUG#36948 - Trying to use trustCertificateKeyStoreUrl + * causes an IllegalStateException. + * + * Requires test certificates from testsuite/ssl-test-certs to be installed + * on the server being tested. + * + * @throws Exception + * if the test fails. + */ + public void testBug36948() throws Exception { + Connection _conn = null; + + try { + Properties props = getPropertiesFromTestsuiteUrl(); + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + String db = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, "test"); + + String hostSpec = host; + + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + hostSpec = host + ":" + port; + } + + props = getHostFreePropertiesFromTestsuiteUrl(); + props.remove("useSSL"); + props.remove("requireSSL"); + props.remove("verifyServerCertificate"); + props.remove("trustCertificateKeyStoreUrl"); + props.remove("trustCertificateKeyStoreType"); + props.remove("trustCertificateKeyStorePassword"); + + final String url = "jdbc:mysql://" + hostSpec + "/" + db + "?useSSL=true&requireSSL=true&verifyServerCertificate=true" + + "&trustCertificateKeyStoreUrl=file:src/testsuite/ssl-test-certs/test-cert-store&trustCertificateKeyStoreType=JKS" + + "&trustCertificateKeyStorePassword=password"; + + _conn = DriverManager.getConnection(url, props); + } finally { + if (_conn != null) { + _conn.close(); + } + } + + } + + /** + * Tests fix for BUG#27655 - getTransactionIsolation() uses + * "SHOW VARIABLES LIKE" which is very inefficient on MySQL-5.0+ + * + * @throws Exception + */ + public void testBug27655() throws Exception { + Properties props = new Properties(); + props.setProperty("profileSQL", "true"); + props.setProperty("logger", "StandardLogger"); + StandardLogger.startLoggingToBuffer(); + + Connection loggedConn = null; + + try { + loggedConn = getConnectionWithProps(props); + loggedConn.getTransactionIsolation(); + + if (versionMeetsMinimum(4, 0, 3)) { + assertEquals(-1, StandardLogger.getBuffer().toString().indexOf("SHOW VARIABLES LIKE 'tx_isolation'")); + } + } finally { + StandardLogger.dropBuffer(); + if (loggedConn != null) { + loggedConn.close(); + } + } + } + + /** + * Tests fix for issue where a failed-over connection would let an + * application call setReadOnly(false), when that call should be ignored + * until the connection is reconnected to a writable master. + * + * @throws Exception + * if the test fails. + */ + public void testFailoverReadOnly() throws Exception { + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("autoReconnect", "true"); + props.setProperty("queriesBeforeRetryMaster", "0"); + props.setProperty("secondsBeforeRetryMaster", "0"); // +^ enable fall back to primary as soon as possible + + Connection failoverConn = null; + + Statement failoverStmt = null; + + try { + failoverConn = getConnectionWithProps(getMasterSlaveUrl(), props); + + failoverStmt = failoverConn.createStatement(); + + String masterConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); + + this.stmt.execute("KILL " + masterConnectionId); + + // die trying, so we get the next host + for (int i = 0; i < 100; i++) { + try { + failoverStmt.executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + break; + } + } + + String slaveConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); + + assertTrue("Didn't get a new physical connection", !masterConnectionId.equals(slaveConnectionId)); + + failoverConn.setReadOnly(false); // this should be ignored + + assertTrue(failoverConn.isReadOnly()); + + this.stmt.execute("KILL " + slaveConnectionId); // we can't issue this on our own connection :p + + // die trying, so we get the next host + for (int i = 0; i < 100; i++) { + try { + failoverStmt.executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + break; + } + } + + String newMasterId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString(); + + assertTrue("Didn't get a new physical connection", !slaveConnectionId.equals(newMasterId)); + + failoverConn.setReadOnly(false); + + assertFalse(failoverConn.isReadOnly()); + } finally { + if (failoverStmt != null) { + failoverStmt.close(); + } + + if (failoverConn != null) { + failoverConn.close(); + } + } + } + + public void testPropertiesDescriptionsKeys() throws Exception { + DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(dbUrl, null); + + for (int i = 0; i < dpi.length; i++) { + String description = dpi[i].description; + String propertyName = dpi[i].name; + + if (description.indexOf("Missing error message for key '") != -1 || description.startsWith("!")) { + fail("Missing message for configuration property " + propertyName); + } + + if (description.length() < 10) { + fail("Suspiciously short description for configuration property " + propertyName); + } + } + } + + public void testBug29106() throws Exception { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class checkerClass = cl.loadClass("com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker"); + ((MysqlValidConnectionChecker) checkerClass.newInstance()).isValidConnection(this.conn); + } + + public void testBug29852() throws Exception { + Connection lbConn = getLoadBalancedConnection(); + assertTrue(!lbConn.getClass().getName().startsWith("com.mysql.jdbc")); + lbConn.close(); + } + + /** + * Test of a new feature to fix BUG 22643, specifying a "validation query" + * in your connection pool that starts with "slash-star ping slash-star" + * _exactly_ will cause the driver to " + instead send a ping to the server + * (much lighter weight), and when using a ReplicationConnection or a + * LoadBalancedConnection, will send the ping across all active connections. + * + * @throws Exception + */ + public void testBug22643() throws Exception { + checkPingQuery(this.conn); + + Connection replConnection = getMasterSlaveReplicationConnection(); + + try { + checkPingQuery(replConnection); + } finally { + if (replConnection != null) { + replConnection.close(); + } + } + + Connection lbConn = getLoadBalancedConnection(); + + try { + checkPingQuery(lbConn); + } finally { + if (lbConn != null) { + lbConn.close(); + } + } + } + + private void checkPingQuery(Connection c) throws SQLException { + // Yes, I know we're sending 2, and looking for 1 that's part of the test, since we don't _really_ send the query to the server! + String aPingQuery = "/* ping */ SELECT 2"; + Statement pingStmt = c.createStatement(); + PreparedStatement pingPStmt = null; + + this.rs = pingStmt.executeQuery(aPingQuery); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 1); + + assertTrue(pingStmt.execute(aPingQuery)); + this.rs = pingStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 1); + + pingPStmt = c.prepareStatement(aPingQuery); + + assertTrue(pingPStmt.execute()); + this.rs = pingPStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 1); + + this.rs = pingPStmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 1); + + } + + public void testBug31053() throws Exception { + Properties props = new Properties(); + props.setProperty("connectTimeout", "2000"); + props.setProperty("loadBalanceStrategy", "random"); + + Connection lbConn = getLoadBalancedConnection(2, "localhost:23", props); + + lbConn.setAutoCommit(false); + + for (int i = 0; i < 10; i++) { + lbConn.commit(); + } + } + + public void testBug32877() throws Exception { + Properties props = new Properties(); + props.setProperty("connectTimeout", "2000"); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + + Connection lbConn = getLoadBalancedConnection(1, "localhost:23", props); + + lbConn.setAutoCommit(false); + + long begin = System.currentTimeMillis(); + + for (int i = 0; i < 4; i++) { + lbConn.commit(); + } + + assertTrue(System.currentTimeMillis() - begin < 10000); + } + + /** + * Tests fix for BUG#33734 - NullPointerException when using client-side + * prepared statements and enabling caching of prepared statements (only + * present in nightly builds of 5.1). + * + * @throws Exception + */ + public void testBug33734() throws Exception { + Connection testConn = getConnectionWithProps("cachePrepStmts=true,useServerPrepStmts=false"); + try { + testConn.prepareStatement("SELECT 1"); + } finally { + testConn.close(); + } + } + + /** 34703 [NEW]: isValild() aborts Connection on timeout */ + + public void testBug34703() throws Exception { + if (!com.mysql.jdbc.Util.isJdbc4()) { + return; + } + + Method isValid = java.sql.Connection.class.getMethod("isValid", new Class[] { Integer.TYPE }); + + Connection newConn = getConnectionWithProps((Properties) null); + isValid.invoke(newConn, new Object[] { new Integer(1) }); + Thread.sleep(2000); + assertTrue(((Boolean) isValid.invoke(newConn, new Object[] { new Integer(0) })).booleanValue()); + } + + public void testBug34937() throws Exception { + com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource(); + StringBuilder urlBuf = new StringBuilder(); + urlBuf.append(getMasterSlaveUrl()); + urlBuf.append("?"); + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + String key = null; + + Enumeration keyEnum = props.keys(); + + while (keyEnum.hasMoreElements()) { + key = (String) keyEnum.nextElement(); + urlBuf.append(key); + urlBuf.append("="); + urlBuf.append(props.get(key)); + urlBuf.append("&"); + } + + String url = urlBuf.toString(); + url = "jdbc:mysql:replication:" + url.substring(url.indexOf("jdbc:mysql:") + "jdbc:mysql:".length()); + ds.setURL(url); + Connection replConn = ds.getPooledConnection().getConnection(); + + boolean readOnly = false; + + for (int i = 0; i < 10; i++) { + this.rs = replConn.createStatement().executeQuery("SELECT 1"); + assertTrue(this.rs.next()); + this.rs = replConn.prepareStatement("SELECT 1").executeQuery(); + assertTrue(this.rs.next()); + readOnly = !readOnly; + replConn.setReadOnly(readOnly); + } + } + + public void testBug35660() throws Exception { + + Connection lbConn = getLoadBalancedConnection(null); + Connection lbConn2 = getLoadBalancedConnection(null); + + try { + assertEquals(this.conn, this.conn); + assertEquals(lbConn, lbConn); + assertFalse(lbConn.equals(this.conn)); + assertFalse(lbConn.equals(lbConn2)); + } finally { + lbConn.close(); + lbConn2.close(); + } + } + + public void testBug37570() throws Exception { + Properties props = new Properties(); + props.setProperty("characterEncoding", "utf-8"); + props.setProperty("passwordCharacterEncoding", "utf-8"); + + // TODO enable for usual connection? + Connection adminConn = getAdminConnectionWithProps(props); + + if (adminConn != null) { + + String unicodePassword = "\u0430\u0431\u0432"; // Cyrillic string + String user = "bug37570"; + Statement adminStmt = adminConn.createStatement(); + + adminStmt.executeUpdate("create user '" + user + "'@'127.0.0.1' identified by 'foo'"); + adminStmt.executeUpdate("grant usage on *.* to '" + user + "'@'127.0.0.1'"); + adminStmt.executeUpdate("update mysql.user set password=PASSWORD('" + unicodePassword + "') where user = '" + user + "'"); + adminStmt.executeUpdate("flush privileges"); + + try { + ((MySQLConnection) adminConn).changeUser(user, unicodePassword); + } catch (SQLException sqle) { + assertTrue("Connection with non-latin1 password failed", false); + } + + } + } + + public void testUnreliableSocketFactory() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + assertNotNull("Connection should not be null", this.conn); + + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("first"); + UnreliableSocketFactory.downHost("second"); + try { + conn2.createStatement().execute("SELECT 1"); + fail("Should hang here."); + } catch (SQLException sqlEx) { + assertEquals("08S01", sqlEx.getSQLState()); + } + } + + public void testReplicationConnectionGroupHostManagement() throws Exception { + String replicationGroup1 = "rg1"; + + Properties props = new Properties(); + props.setProperty("replicationConnectionGroup", replicationGroup1); + props.setProperty("retriesAllDown", "3"); + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props); + assertNotNull("Connection should not be null", this.conn); + conn2.setAutoCommit(false); + String port = getPort(props, new NonRegisteringDriver()); + String firstHost = "first:" + port; + String secondHost = "second:" + port; + String thirdHost = "third:" + port; + + // "first" should be master, "second" and "third" should be slaves. + assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, firstHost)); + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, firstHost)); + + // remove "third" from slave pool: + conn2.removeSlave(thirdHost); + + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, thirdHost)); + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, thirdHost)); + + // add "third" back into slave pool: + conn2.addSlaveHost(thirdHost); + + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, thirdHost)); + assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, thirdHost)); + + conn2.setReadOnly(false); + + assertEquals(0, ReplicationConnectionGroupManager.getNumberOfMasterPromotion(replicationGroup1)); + + // failover to "second" as master + ReplicationConnectionGroupManager.promoteSlaveToMaster(replicationGroup1, secondHost); + assertEquals(1, ReplicationConnectionGroupManager.getNumberOfMasterPromotion(replicationGroup1)); + + // "first" is still a master: + assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, firstHost)); + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, firstHost)); + assertEquals(1, ReplicationConnectionGroupManager.getConnectionCountWithHostAsMaster(replicationGroup1, secondHost)); + assertEquals(0, ReplicationConnectionGroupManager.getConnectionCountWithHostAsSlave(replicationGroup1, secondHost)); + + ReplicationConnectionGroupManager.removeMasterHost(replicationGroup1, firstHost); + + conn2.createStatement().execute("SELECT 1"); + assertFalse(conn2.isClosed()); + + conn2.commit(); + + // validate that queries are successful: + conn2.createStatement().execute("SELECT 1"); + assertTrue(conn2.isHostMaster(secondHost)); + + // master is now offline + UnreliableSocketFactory.downHost("second"); + try { + Statement lstmt = conn2.createStatement(); + lstmt.execute("SELECT 1"); + fail("Should fail here due to closed connection"); + } catch (SQLException sqlEx) { + assertEquals("08S01", sqlEx.getSQLState()); + } + + } + + public void testReplicationConnectionHostManagement() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "3"); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props); + conn2.setAutoCommit(false); + String port = getPort(props, new NonRegisteringDriver()); + String firstHost = "first:" + port; + String secondHost = "second:" + port; + String thirdHost = "third:" + port; + + // "first" should be master, "second" and "third" should be slaves. + assertTrue(conn2.isHostMaster(firstHost)); + assertTrue(conn2.isHostSlave(secondHost)); + assertTrue(conn2.isHostSlave(thirdHost)); + assertFalse(conn2.isHostSlave(firstHost)); + assertFalse(conn2.isHostMaster(secondHost)); + assertFalse(conn2.isHostMaster(thirdHost)); + + // remove "third" from slave pool: + conn2.removeSlave(thirdHost); + assertFalse(conn2.isHostSlave(thirdHost)); + assertFalse(conn2.isHostMaster(thirdHost)); + + // add "third" back into slave pool: + conn2.addSlaveHost(thirdHost); + assertTrue(conn2.isHostSlave(thirdHost)); + assertFalse(conn2.isHostMaster(thirdHost)); + conn2.setReadOnly(false); + + // failover to "second" as master, "first" + // can still be used: + conn2.promoteSlaveToMaster(secondHost); + assertTrue(conn2.isHostMaster(firstHost)); + assertFalse(conn2.isHostSlave(firstHost)); + assertFalse(conn2.isHostSlave(secondHost)); + assertTrue(conn2.isHostMaster(secondHost)); + assertTrue(conn2.isHostSlave(thirdHost)); + assertFalse(conn2.isHostMaster(thirdHost)); + + conn2.removeMasterHost(firstHost); + + // "first" should no longer be used: + conn2.promoteSlaveToMaster(secondHost); + assertFalse(conn2.isHostMaster(firstHost)); + assertFalse(conn2.isHostSlave(firstHost)); + assertFalse(conn2.isHostSlave(secondHost)); + assertTrue(conn2.isHostMaster(secondHost)); + assertTrue(conn2.isHostSlave(thirdHost)); + assertFalse(conn2.isHostMaster(thirdHost)); + + conn2.createStatement().execute("SELECT 1"); + assertFalse(conn2.isClosed()); + + // check that we're waiting until transaction boundary to fail over. + // assertTrue(conn2.hasPendingNewMaster()); + assertFalse(conn2.isClosed()); + conn2.commit(); + assertFalse(conn2.isClosed()); + assertTrue(conn2.isHostMaster(secondHost)); + assertFalse(conn2.isClosed()); + assertTrue(conn2.isMasterConnection()); + assertFalse(conn2.isClosed()); + + // validate that queries are successful: + conn2.createStatement().execute("SELECT 1"); + assertTrue(conn2.isHostMaster(secondHost)); + + // master is now offline + UnreliableSocketFactory.downHost("second"); + try { + Statement lstmt = conn2.createStatement(); + lstmt.execute("SELECT 1"); + fail("Should fail here due to closed connection"); + } catch (SQLException sqlEx) { + assertEquals("08S01", sqlEx.getSQLState()); + } + + UnreliableSocketFactory.dontDownHost("second"); + try { + // won't work now even though master is back up connection has already been implicitly closed when a new master host cannot be found: + conn2.createStatement().execute("SELECT 1"); + fail("Will fail because inability to find new master host implicitly closes connection."); + } catch (SQLException e) { + assertEquals("08003", e.getSQLState()); + } + + } + + public void testReplicationConnectWithNoMaster() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "3"); + props.setProperty("allowMasterDownConnections", "true"); + + Set downedHosts = new HashSet(); + downedHosts.add("first"); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "first", "second", "third" }, props, downedHosts); + assertTrue(conn2.isReadOnly()); + assertFalse(conn2.isMasterConnection()); + try { + conn2.createStatement().execute("SELECT 1"); + } catch (SQLException e) { + fail("Should not fail to execute SELECT statements!"); + } + UnreliableSocketFactory.flushAllStaticData(); + conn2.setReadOnly(false); + assertFalse(conn2.isReadOnly()); + assertTrue(conn2.isMasterConnection()); + try { + conn2.createStatement().execute("DROP TABLE IF EXISTS testRepTable"); + conn2.createStatement().execute("CREATE TABLE testRepTable (a INT)"); + conn2.createStatement().execute("INSERT INTO testRepTable VALUES (1)"); + conn2.createStatement().execute("DROP TABLE IF EXISTS testRepTable"); + + } catch (SQLException e) { + fail("Should not fail to execute CREATE/INSERT/DROP statements."); + } + } + + public void testReplicationConnectWithMultipleMasters() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "3"); + + Set configs = new HashSet(); + MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); + MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); + MockConnectionConfiguration third = new MockConnectionConfiguration("third", "master", null, false); + + configs.add(first); + configs.add(second); + configs.add(third); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); + assertFalse(conn2.isReadOnly()); + assertTrue(conn2.isMasterConnection()); + assertTrue(conn2.isHostSlave(first.getAddress())); + assertTrue(conn2.isHostMaster(second.getAddress())); + assertTrue(conn2.isHostMaster(third.getAddress())); + + } + + public void testReplicationConnectionMemory() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "3"); + String replicationGroup = "memoryGroup"; + props.setProperty("replicationConnectionGroup", replicationGroup); + + Set configs = new HashSet(); + MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); + MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); + MockConnectionConfiguration third = new MockConnectionConfiguration("third", "slave", null, false); + + configs.add(first); + configs.add(second); + configs.add(third); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replicationGroup, first.getAddress()); + ReplicationConnectionGroupManager.removeMasterHost(replicationGroup, second.getAddress()); + ReplicationConnectionGroupManager.addSlaveHost(replicationGroup, second.getAddress()); + + conn2.setReadOnly(false); + + assertFalse(conn2.isReadOnly()); + assertTrue(conn2.isMasterConnection()); + assertTrue(conn2.isHostMaster(first.getAddress())); + assertTrue(conn2.isHostSlave(second.getAddress())); + assertTrue(conn2.isHostSlave(third.getAddress())); + + // make sure state changes made are reflected in new connections: + + ReplicationConnection conn3 = this.getUnreliableReplicationConnection(configs, props); + + conn3.setReadOnly(false); + + assertFalse(conn3.isReadOnly()); + assertTrue(conn3.isMasterConnection()); + assertTrue(conn3.isHostMaster(first.getAddress())); + assertTrue(conn3.isHostSlave(second.getAddress())); + assertTrue(conn3.isHostSlave(third.getAddress())); + + } + + public void testReplicationJMXInterfaces() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "3"); + String replicationGroup = "testReplicationJMXInterfaces"; + props.setProperty("replicationConnectionGroup", replicationGroup); + props.setProperty("replicationEnableJMX", "true"); + + Set configs = new HashSet(); + MockConnectionConfiguration first = new MockConnectionConfiguration("first", "slave", null, false); + MockConnectionConfiguration second = new MockConnectionConfiguration("second", "master", null, false); + MockConnectionConfiguration third = new MockConnectionConfiguration("third", "slave", null, false); + + configs.add(first); + configs.add(second); + configs.add(third); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(configs, props); + + ReplicationGroupManagerMBean bean = getReplicationMBean(); + + assertEquals(1, bean.getActiveLogicalConnectionCount(replicationGroup)); + assertEquals(1, bean.getTotalLogicalConnectionCount(replicationGroup)); + assertEquals(0, bean.getSlavePromotionCount(replicationGroup)); + assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); + assertEquals(2, bean.getActiveSlaveHostCount(replicationGroup)); + bean.removeSlaveHost(replicationGroup, first.getAddress()); + assertFalse(bean.getSlaveHostsList(replicationGroup).contains(first.getAddress())); + assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); + conn2.close(); + assertEquals(0, bean.getActiveLogicalConnectionCount(replicationGroup)); + conn2 = this.getUnreliableReplicationConnection(configs, props); + assertEquals(1, bean.getActiveLogicalConnectionCount(replicationGroup)); + assertEquals(2, bean.getTotalLogicalConnectionCount(replicationGroup)); + assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); + assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); + bean.promoteSlaveToMaster(replicationGroup, third.getAddress()); + assertEquals(2, bean.getActiveMasterHostCount(replicationGroup)); + assertEquals(0, bean.getActiveSlaveHostCount(replicationGroup)); + // confirm this works when no group filter is specified: + bean.addSlaveHost(null, first.getAddress()); + assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); + assertEquals(2, bean.getActiveMasterHostCount(replicationGroup)); + bean.removeMasterHost(replicationGroup, second.getAddress()); + assertEquals(1, bean.getActiveSlaveHostCount(replicationGroup)); + assertEquals(1, bean.getActiveMasterHostCount(replicationGroup)); + + ReplicationConnection conn3 = this.getUnreliableReplicationConnection(configs, props); + + assertEquals(2, bean.getActiveLogicalConnectionCount(replicationGroup)); + assertEquals(3, bean.getTotalLogicalConnectionCount(replicationGroup)); + + assertTrue(bean.getMasterHostsList(replicationGroup).contains(third.getAddress())); + assertFalse(bean.getMasterHostsList(replicationGroup).contains(first.getAddress())); + assertFalse(bean.getMasterHostsList(replicationGroup).contains(second.getAddress())); + + assertFalse(bean.getSlaveHostsList(replicationGroup).contains(third.getAddress())); + assertTrue(bean.getSlaveHostsList(replicationGroup).contains(first.getAddress())); + assertFalse(bean.getSlaveHostsList(replicationGroup).contains(second.getAddress())); + + assertTrue(bean.getMasterHostsList(replicationGroup).contains(conn3.getMasterConnection().getHost())); + assertTrue(bean.getSlaveHostsList(replicationGroup).contains(conn3.getSlavesConnection().getHost())); + + assertTrue(bean.getRegisteredConnectionGroups().contains(replicationGroup)); + + } + + private ReplicationGroupManagerMBean getReplicationMBean() throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + ObjectName mbeanName = new ObjectName("com.mysql.jdbc.jmx:type=ReplicationGroupManager"); + return (ReplicationGroupManagerMBean) MBeanServerInvocationHandler.newProxyInstance(mbs, mbeanName, ReplicationGroupManagerMBean.class, false); + + } + + public void testBug43421() throws Exception { + + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("second"); + UnreliableSocketFactory.downHost("first"); + try { + conn2.createStatement().execute("/* ping */"); + fail("Pings will not succeed when one host is down and using loadbalance w/o global blacklist."); + } catch (SQLException sqlEx) { + } + + UnreliableSocketFactory.flushAllStaticData(); + props = new Properties(); + props.setProperty("globalBlacklistTimeout", "200"); + props.setProperty("loadBalanceStrategy", "bestResponseTime"); + + conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + + assertNotNull("Connection should not be null", this.conn); + + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + // both connections are live now + UnreliableSocketFactory.downHost("second"); + try { + conn2.createStatement().execute("/* ping */"); + } catch (SQLException sqlEx) { + fail("Pings should succeed even though host is down."); + } + } + + public void testBug48442() throws Exception { + + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "random"); + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + + assertNotNull("Connection should not be null", conn2); + conn2.setAutoCommit(false); + UnreliableSocketFactory.downHost("second"); + int hc = 0; + try { + conn2.createStatement().execute("SELECT 1"); + } catch (SQLException e) { + conn2.createStatement().execute("SELECT 1"); + } + hc = conn2.hashCode(); + conn2.commit(); + UnreliableSocketFactory.dontDownHost("second"); + UnreliableSocketFactory.downHost("first"); + try { + conn2.commit(); + } catch (SQLException e) { + } + assertTrue(hc == conn2.hashCode()); + + } + + public void testBug45171() throws Exception { + List statementsToTest = new LinkedList(); + statementsToTest.add(this.conn.createStatement()); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1")); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS)); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", new int[0])); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", new String[0])); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1")); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS)); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", new int[0])); + statementsToTest.add(((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 1", new String[0])); + + for (Statement toTest : statementsToTest) { + assertEquals(toTest.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY); + assertEquals(toTest.getResultSetConcurrency(), ResultSet.CONCUR_READ_ONLY); + } + + } + + /** + * Tests fix for BUG#44587, provide last packet sent/received timing in all + * connection failure errors. + */ + public void testBug44587() throws Exception { + Exception e = null; + String msg = SQLError.createLinkFailureMessageBasedOnHeuristics((MySQLConnection) this.conn, System.currentTimeMillis() - 1000, + System.currentTimeMillis() - 2000, e); + assertTrue(containsMessage(msg, "CommunicationsException.ServerPacketTimingInfo")); + } + + /** + * Tests fix for BUG#45419, ensure that time is not converted to seconds + * before being reported as milliseconds. + */ + public void testBug45419() throws Exception { + Exception e = null; + String msg = SQLError.createLinkFailureMessageBasedOnHeuristics((MySQLConnection) this.conn, System.currentTimeMillis() - 1000, + System.currentTimeMillis() - 2000, e); + Matcher m = Pattern.compile("([\\d\\,\\.]+)", Pattern.MULTILINE).matcher(msg); + assertTrue(m.find()); + assertTrue(Long.parseLong(m.group(0).replaceAll("[,.]", "")) >= 2000); + assertTrue(Long.parseLong(m.group(1).replaceAll("[,.]", "")) >= 1000); + } + + public static boolean containsMessage(String msg, String key) { + String[] expectedFragments = Messages.getString(key).split("\\{\\d\\}"); + for (int i = 0; i < expectedFragments.length; i++) { + if (msg.indexOf(expectedFragments[i]) < 0) { + return false; + } + } + return true; + } + + public void testBug46637() throws Exception { + String hostname = getPortFreeHostname(null, new NonRegisteringDriver()); + UnreliableSocketFactory.flushAllStaticData(); + UnreliableSocketFactory.downHost(hostname); + + try { + Connection noConn = getConnectionWithProps("socketFactory=testsuite.UnreliableSocketFactory"); + noConn.close(); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf("has not received") != -1); + } finally { + UnreliableSocketFactory.flushAllStaticData(); + } + } + + public void testBug32216() throws Exception { + checkBug32216("www.mysql.com", "12345", "my_database"); + checkBug32216("www.mysql.com", null, "my_database"); + } + + private void checkBug32216(String host, String port, String dbname) throws SQLException { + NonRegisteringDriver driver = new NonRegisteringDriver(); + + StringBuilder url = new StringBuilder("jdbc:mysql://"); + url.append(host); + + if (port != null) { + url.append(':'); + url.append(port); + } + + url.append('/'); + url.append(dbname); + + Properties result = driver.parseURL(url.toString(), new Properties()); + + assertEquals("hostname not equal", host, result.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY)); + if (port != null) { + assertEquals("port not equal", port, result.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY)); + } else { + assertEquals("port default incorrect", "3306", result.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY)); + } + + assertEquals("dbname not equal", dbname, result.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY)); + } + + public void testBug44324() throws Exception { + createTable("bug44324", "(Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, SomeVChar VARCHAR(10)) ENGINE=MyISAM;"); + + try { + this.stmt.executeUpdate("INSERT INTO bug44324 values (null, 'Some text much longer than 10 characters')"); + } catch (MysqlDataTruncation sqlEx) { + assertTrue(0 != sqlEx.getErrorCode()); + } + + } + + public void testBug46925() throws Exception { + MysqlXADataSource xads1 = new MysqlXADataSource(); + MysqlXADataSource xads2 = new MysqlXADataSource(); + + Xid txid = new MysqlXid(new byte[] { 0x1 }, new byte[] { 0xf }, 3306); + + xads1.setPinGlobalTxToPhysicalConnection(true); + xads1.setUrl(dbUrl); + + xads2.setPinGlobalTxToPhysicalConnection(true); + xads2.setUrl(dbUrl); + + XAConnection c1 = xads1.getXAConnection(); + assertTrue(c1 instanceof SuspendableXAConnection); + // start a transaction on one connection + c1.getXAResource().start(txid, XAResource.TMNOFLAGS); + c1.getXAResource().end(txid, XAResource.TMSUCCESS); + + XAConnection c2 = xads2.getXAConnection(); + assertTrue(c2 instanceof SuspendableXAConnection); + // prepare on another one. Since we are using a "pinned" connection we should have the same "currentXAConnection" for both SuspendableXAConnection + c2.getXAResource().prepare(txid); // this will fail without the fix. + c2.getXAResource().commit(txid, false); + } + + public void testBug47494() throws Exception { + try { + getConnectionWithProps("jdbc:mysql://localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getCause() instanceof IOException); + } + + try { + getConnectionWithProps("jdbc:mysql://:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getCause() instanceof IOException); + } + + try { + getConnectionWithProps("jdbc:mysql://:9999,:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getCause() instanceof IOException); + } + + try { + getConnectionWithProps( + "jdbc:mysql://localhost:9999,localhost:9999/test?socketFactory=testsuite.regression.ConnectionRegressionTest$PortNumberSocketFactory"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getCause() instanceof IOException); + } + } + + public static class PortNumberSocketFactory extends StandardSocketFactory { + + public PortNumberSocketFactory() { + + } + + @Override + public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { + assertEquals(9999, portNumber); + + throw new IOException(); + } + + } + + public void testBug48486() throws Exception { + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + String hostSpec = host; + + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + hostSpec = host + ":" + port; + } + + String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + removeHostRelatedProps(props); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + StringBuilder configs = new StringBuilder(); + for (@SuppressWarnings("rawtypes") + Map.Entry entry : props.entrySet()) { + configs.append(entry.getKey()); + configs.append("="); + configs.append(entry.getValue()); + configs.append("&"); + } + + String newUrl = String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, configs.toString()); + + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + ds.setUrl(newUrl); + + Connection c = ds.getPooledConnection().getConnection(); + this.rs = c.createStatement().executeQuery("SELECT 1"); + this.rs = c.prepareStatement("SELECT 1").executeQuery(); + } + + public void testBug48605() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "random"); + props.setProperty("selfDestructOnPingMaxOperations", "5"); + final Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + + assertNotNull("Connection should not be null", conn2); + conn2.setAutoCommit(false); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.commit(); + // after commit we may be using a different connection, make sure the number of executions on this also reaches the defined limit. + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 1"); + + assertThrows(SQLException.class, "Ping or validation failed because configured connection lifetime exceeded\\.", new Callable() { + public Void call() throws Exception { + conn2.createStatement().execute("/* ping */ SELECT 1"); + return null; + } + }); + + assertTrue(conn2.isClosed()); + + assertThrows(SQLException.class, "No operations allowed after connection closed.*", new Callable() { + public Void call() throws Exception { + conn2.createStatement().execute("SELECT 1"); + return null; + } + }); + } + + public void testBug49700() throws Exception { + Connection c = getConnectionWithProps("sessionVariables=@foo='bar'"); + assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo")); + ((com.mysql.jdbc.Connection) c).resetServerState(); + assertEquals("bar", getSingleIndexedValueWithQuery(c, 1, "SELECT @foo")); + } + + public void testBug51266() throws Exception { + Properties props = new Properties(); + props.setProperty("roundRobinLoadBalance", "true"); // shouldn't be + // needed, but used + // in reported bug, + // it's removed by + // the driver + Set downedHosts = new HashSet(); + downedHosts.add("first"); + + // this loop will hang on the first unreliable host if the bug isn't + // fixed. + for (int i = 0; i < 20; i++) { + getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props, downedHosts).close(); + } + } + + // Tests fix for Bug#51643 - connection chosen by load balancer "sticks" to statements that live past commit()/rollback(). + + public void testBug51643() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", "com.mysql.jdbc.SequentialBalanceStrategy"); + + Connection lbConn = getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + try { + PreparedStatement cPstmt = lbConn.prepareStatement("SELECT connection_id()"); + PreparedStatement serverPstmt = lbConn.prepareStatement("SELECT connection_id()"); + Statement plainStmt = lbConn.createStatement(); + + lbConn.setAutoCommit(false); + this.rs = cPstmt.executeQuery(); + this.rs.next(); + String cPstmtConnId = this.rs.getString(1); + + this.rs = serverPstmt.executeQuery(); + this.rs.next(); + String serverPstmtConnId = this.rs.getString(1); + + this.rs = plainStmt.executeQuery("SELECT connection_id()"); + this.rs.next(); + String plainStmtConnId = this.rs.getString(1); + lbConn.commit(); + lbConn.setAutoCommit(false); + + this.rs = cPstmt.executeQuery(); + this.rs.next(); + String cPstmtConnId2 = this.rs.getString(1); + assertFalse(cPstmtConnId2.equals(cPstmtConnId)); + + this.rs = serverPstmt.executeQuery(); + this.rs.next(); + String serverPstmtConnId2 = this.rs.getString(1); + assertFalse(serverPstmtConnId2.equals(serverPstmtConnId)); + + this.rs = plainStmt.executeQuery("SELECT connection_id()"); + this.rs.next(); + String plainStmtConnId2 = this.rs.getString(1); + assertFalse(plainStmtConnId2.equals(plainStmtConnId)); + } finally { + lbConn.close(); + } + } + + public void testBug51783() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); + props.setProperty("loadBalanceBlacklistTimeout", "5000"); + props.setProperty("loadBalancePingTimeout", "100"); + props.setProperty("loadBalanceValidateConnectionOnSwapServer", "true"); + + String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + if (portNumber == null) { + portNumber = "3306"; + } + + ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(false); + conn2.createStatement().execute("SELECT 1"); + ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, -1); + UnreliableSocketFactory.downHost("second"); + try { + conn2.commit(); // will be on second after this + assertTrue("Connection should be closed", conn2.isClosed()); + } catch (SQLException e) { + fail("Should not error because failure to get another server."); + } + conn2.close(); + + props = new Properties(); + props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); + props.setProperty("loadBalanceBlacklistTimeout", "5000"); + props.setProperty("loadBalancePingTimeout", "100"); + props.setProperty("loadBalanceValidateConnectionOnSwapServer", "false"); + ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); + conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(false); + conn2.createStatement().execute("SELECT 1"); + ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, 1); + UnreliableSocketFactory.downHost("second"); + try { + conn2.commit(); // will be on second after this + assertFalse("Connection should not be closed, should be able to connect to first", conn2.isClosed()); + } catch (SQLException e) { + fail("Should not error because failure to get another server."); + } + } + + public static class ForcedLoadBalanceStrategy extends RandomBalanceStrategy { + + private static String forcedFutureServer = null; + private static int forceFutureServerTimes = 0; + + public static void forceFutureServer(String host, int times) { + forcedFutureServer = host; + forceFutureServerTimes = times; + } + + public static void dontForceFutureServer() { + forcedFutureServer = null; + forceFutureServerTimes = 0; + } + + @Override + public com.mysql.jdbc.ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, + Map liveConnections, long[] responseTimes, int numRetries) throws SQLException { + if (forcedFutureServer == null || forceFutureServerTimes == 0 || !configuredHosts.contains(forcedFutureServer)) { + return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); + } + if (forceFutureServerTimes > 0) { + forceFutureServerTimes--; + } + ConnectionImpl conn = liveConnections.get(forcedFutureServer); + + if (conn == null) { + conn = proxy.createConnectionForHost(forcedFutureServer); + + } + return conn; + } + + @Override + public void destroy() { + super.destroy(); + + } + + @Override + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + super.init(conn, props); + + } + + } + + public void testAutoCommitLB() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", CountingReBalanceStrategy.class.getName()); + props.setProperty("loadBalanceAutoCommitStatementThreshold", "3"); + + String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + if (portNumber == null) { + portNumber = "3306"; + } + + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(true); + CountingReBalanceStrategy.resetTimesRebalanced(); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 2"); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.createStatement().execute("SELECT 3"); + assertEquals(1, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.setAutoCommit(false); + CountingReBalanceStrategy.resetTimesRebalanced(); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 2"); + conn2.createStatement().execute("SELECT 3"); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.close(); + + props.remove("loadBalanceAutoCommitStatementThreshold"); + conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(true); + CountingReBalanceStrategy.resetTimesRebalanced(); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 2"); + conn2.createStatement().execute("SELECT 3"); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.setAutoCommit(false); + CountingReBalanceStrategy.resetTimesRebalanced(); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 2"); + conn2.createStatement().execute("SELECT 3"); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.close(); + + props.setProperty("loadBalanceAutoCommitStatementThreshold", "3"); + props.setProperty("loadBalanceAutoCommitStatementRegex", ".*2.*"); + conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(true); + CountingReBalanceStrategy.resetTimesRebalanced(); + conn2.createStatement().execute("SELECT 1"); + conn2.createStatement().execute("SELECT 2"); + conn2.createStatement().execute("SELECT 3"); + conn2.createStatement().execute("SELECT 2"); + assertEquals(0, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.createStatement().execute("SELECT 2"); + assertEquals(1, CountingReBalanceStrategy.getTimesRebalanced()); + conn2.close(); + + } + + public static class CountingReBalanceStrategy extends RandomBalanceStrategy { + + private static int rebalancedTimes = 0; + + public static int getTimesRebalanced() { + return rebalancedTimes; + } + + public static void resetTimesRebalanced() { + rebalancedTimes = 0; + } + + @Override + public com.mysql.jdbc.ConnectionImpl pickConnection(LoadBalancedConnectionProxy proxy, List configuredHosts, + Map liveConnections, long[] responseTimes, int numRetries) throws SQLException { + rebalancedTimes++; + return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); + + } + + @Override + public void destroy() { + super.destroy(); + } + + @Override + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + super.init(conn, props); + } + + } + + public void testBug56429() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "true"); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); + + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); + + props.setProperty("queriesBeforeRetryMaster", "50"); + props.setProperty("maxReconnects", "1"); + + UnreliableSocketFactory.mapHost("master", host); + UnreliableSocketFactory.mapHost("slave", host); + + Connection failoverConnection = null; + + try { + failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); + + String userHost = getSingleIndexedValueWithQuery(1, "SELECT USER()").toString(); + String[] userParts = userHost.split("@"); + + this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); + + int startConnCount = 0; + + while (this.rs.next()) { + if (this.rs.getString("User").equals(userParts[0]) && this.rs.getString("Host").equals(userParts[1])) { + startConnCount++; + } + } + + assert (startConnCount > 0); + + failoverConnection.setAutoCommit(false); // this will fail if state + // not copied over + + for (int i = 0; i < 20; i++) { + + failoverConnection.commit(); + } + + this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); + + int endConnCount = 0; + + while (this.rs.next()) { + if (this.rs.getString("User").equals(userParts[0]) && this.rs.getString("Host").equals(userParts[1])) { + endConnCount++; + } + } + + assert (endConnCount > 0); + + if (endConnCount - startConnCount >= 20) { // this may be bogus if run on a real system, we should probably look to see they're coming from this + // testsuite? + fail("We're leaking connections even when not failed over"); + } + } finally { + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + + public void testBug56955() throws Exception { + assertEquals("JKS", ((com.mysql.jdbc.Connection) this.conn).getTrustCertificateKeyStoreType()); + assertEquals("JKS", ((com.mysql.jdbc.Connection) this.conn).getClientCertificateKeyStoreType()); + } + + public void testBug57262() throws Exception { + Properties props = new Properties(); + props.setProperty("characterEncoding", "utf-8"); + props.setProperty("useUnicode", "true"); + props.setProperty("useOldUTF8Behavior", "true"); + + Connection c = getConnectionWithProps(props); + ResultSet r = c.createStatement().executeQuery("SHOW SESSION VARIABLES LIKE 'character_set_connection'"); + r.next(); + assertEquals("latin1", r.getString(2)); + } + + public void testBug58706() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "true"); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); + + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); + + props.setProperty("queriesBeforeRetryMaster", "0"); + props.setProperty("secondsBeforeRetryMaster", "1"); + props.setProperty("failOverReadOnly", "false"); + + UnreliableSocketFactory.mapHost("master", host); + UnreliableSocketFactory.mapHost("slave", host); + + Connection failoverConnection = null; + + try { + failoverConnection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); + failoverConnection.setAutoCommit(false); + + assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); + + for (int i = 0; i < 50; i++) { + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + } + + UnreliableSocketFactory.downHost("master"); + + try { + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); // this should fail and trigger failover + fail("Expected exception"); + } catch (SQLException sqlEx) { + assertEquals("08S01", sqlEx.getSQLState()); + } + + failoverConnection.setAutoCommit(true); + assertEquals("/slave", UnreliableSocketFactory.getHostFromLastConnection()); + assertTrue(!failoverConnection.isReadOnly()); + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + UnreliableSocketFactory.dontDownHost("master"); + Thread.sleep(2000); + failoverConnection.setAutoCommit(true); + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + assertEquals("/master", UnreliableSocketFactory.getHostFromLastConnection()); + this.rs = failoverConnection.createStatement().executeQuery("SELECT 1"); + } finally { + UnreliableSocketFactory.flushAllStaticData(); + + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + + public void testStatementComment() throws Exception { + Connection c = getConnectionWithProps("autoGenerateTestcaseScript=true,logger=StandardLogger"); + PrintStream oldErr = System.err; + + try { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(bOut); + System.setErr(printStream); + + ((com.mysql.jdbc.Connection) c).setStatementComment("Hi there"); + c.setAutoCommit(false); + + c.createStatement().execute("SELECT 1"); + c.commit(); + c.rollback(); + Pattern pattern = Pattern.compile("Hi"); + String loggedData = new String(bOut.toByteArray()); + Matcher matcher = pattern.matcher(loggedData); + int count = 0; + while (matcher.find()) { + count++; + } + + assertEquals(4, count); + } finally { + System.setErr(oldErr); + } + } + + public void testReconnectWithCachedConfig() throws Exception { + Connection rConn = getConnectionWithProps("autoReconnect=true,initialTimeout=2,maxReconnects=3,cacheServerConfiguration=true,elideSetAutoCommits=true"); + String threadId = getSingleIndexedValueWithQuery(rConn, 1, "select connection_id()").toString(); + killConnection(this.conn, threadId); + boolean detectedDeadConn = false; + + for (int i = 0; i < 100; i++) { + try { + rConn.createStatement().executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + detectedDeadConn = true; + break; + } + } + + assertTrue(detectedDeadConn); + this.rs = rConn.prepareStatement("SELECT 1").executeQuery(); + + Connection rConn2 = getConnectionWithProps( + "autoReconnect=true,initialTimeout=2,maxReconnects=3,cacheServerConfiguration=true,elideSetAutoCommits=true"); + this.rs = rConn2.prepareStatement("SELECT 1").executeQuery(); + + } + + public void testBug61201() throws Exception { + Properties props = new Properties(); + props.setProperty("sessionVariables", "FOREIGN_KEY_CHECKS=0"); + props.setProperty("characterEncoding", "latin1"); + props.setProperty("profileSQL", "true"); + + Connection varConn = getConnectionWithProps(props); + varConn.close(); + } + + public void testChangeUser() throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + + Connection testConn = getConnectionWithProps(props); + Statement testStmt = testConn.createStatement(); + + for (int i = 0; i < 500; i++) { + ((com.mysql.jdbc.Connection) testConn).changeUser(props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY), + props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); + + if (i % 10 == 0) { + try { + ((com.mysql.jdbc.Connection) testConn).changeUser("bubba", props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); + } catch (SQLException sqlEx) { + if (versionMeetsMinimum(5, 6, 13)) { + assertTrue(testConn.isClosed()); + testConn = getConnectionWithProps(props); + testStmt = testConn.createStatement(); + } + } + } + + this.rs = testStmt.executeQuery("SELECT 1"); + } + testConn.close(); + } + + public void testChangeUserClosedConn() throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + Connection newConn = getConnectionWithProps((Properties) null); + + try { + newConn.close(); + ((com.mysql.jdbc.Connection) newConn).changeUser(props.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY), + props.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY)); + fail("Expected SQL Exception"); + } catch (SQLException ex) { + // expected + if (!ex.getClass().getName().endsWith("MySQLNonTransientConnectionException")) { + throw ex; + } + } finally { + newConn.close(); + } + } + + public void testBug63284() throws Exception { + Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null); + props.setProperty("autoReconnect", "true"); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); + + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + props.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); + props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); + + props.setProperty("queriesBeforeRetryMaster", "50"); + props.setProperty("maxReconnects", "1"); + + UnreliableSocketFactory.mapHost("master", host); + UnreliableSocketFactory.mapHost("slave", host); + + Connection failoverConnection1 = null; + Connection failoverConnection2 = null; + + try { + failoverConnection1 = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); + + failoverConnection2 = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", props); + + assertTrue(((com.mysql.jdbc.Connection) failoverConnection1).isMasterConnection()); + + // Two different Connection objects should not equal each other: + assertFalse(failoverConnection1.equals(failoverConnection2)); + + int hc = failoverConnection1.hashCode(); + + UnreliableSocketFactory.downHost("master"); + + for (int i = 0; i < 3; i++) { + try { + failoverConnection1.createStatement().execute("SELECT 1"); + } catch (SQLException e) { + // do nothing, expect SQLException when failing over initially goal here is to ensure valid connection against a slave + } + } + // ensure we're now connected to the slave + assertFalse(((com.mysql.jdbc.Connection) failoverConnection1).isMasterConnection()); + + // ensure that hashCode() result is persistent across failover events when proxy state changes + assertEquals(hc, failoverConnection1.hashCode()); + } finally { + if (failoverConnection1 != null) { + failoverConnection1.close(); + } + if (failoverConnection2 != null) { + failoverConnection2.close(); + } + } + } + + public void testDefaultPlugin() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + Connection testConn = null; + Properties props = new Properties(); + + props.setProperty("defaultAuthenticationPlugin", ""); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to incorrect defaultAuthenticationPlugin value", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("defaultAuthenticationPlugin", "mysql_native_password"); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to incorrect defaultAuthenticationPlugin value (mechanism name instead of class name)", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to defaultAuthenticationPlugin value is not listed", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + try { + testConn = getConnectionWithProps(props); + assertTrue(true); + } catch (SQLException sqlEx) { + assertTrue("Exception is not expected due to defaultAuthenticationPlugin value is correctly listed", false); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + } + + public void testDisabledPlugins() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + Connection testConn = null; + Properties props = new Properties(); + + props.setProperty("disabledAuthenticationPlugins", "mysql_native_password"); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("disabledAuthenticationPlugins", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + props.setProperty("defaultAuthenticationPlugin", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + props.setProperty("disabledAuthenticationPlugins", "auth_test_plugin"); + try { + testConn = getConnectionWithProps(props); + assertTrue("Exception is expected due to disabled defaultAuthenticationPlugin", false); + } catch (SQLException sqlEx) { + assertTrue(true); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + props.setProperty("disabledAuthenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + try { + testConn = getConnectionWithProps(props); + assertTrue(true); + } catch (SQLException sqlEx) { + assertTrue("Exception is not expected due to disabled plugin is not default", false); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + } + + public void testAuthTestPlugin() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + boolean install_plugin_in_runtime = false; + try { + + // install plugin if required + this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE`" + + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server'"); + if (this.rs.next()) { + if (!this.rs.getBoolean(1)) { + install_plugin_in_runtime = true; + } + } else { + install_plugin_in_runtime = true; + } + + if (install_plugin_in_runtime) { + String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; + this.stmt.executeUpdate("INSTALL PLUGIN test_plugin_server SONAME 'auth_test_plugin" + ext + "'"); + } + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + // create proxy users + createUser("'wl5851user'@'%'", "identified WITH test_plugin_server AS 'plug_dest'"); + createUser("'plug_dest'@'%'", "IDENTIFIED BY 'foo'"); + this.stmt.executeUpdate("GRANT PROXY ON 'plug_dest'@'%' TO 'wl5851user'@'%'"); + this.stmt.executeUpdate("delete from mysql.db where user='plug_dest'"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + + dbname + "', 'plug_dest', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'plug_dest', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("flush privileges"); + + props = new Properties(); + props.setProperty("user", "wl5851user"); + props.setProperty("password", "plug_dest"); + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$AuthTestPlugin"); + + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + try { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("wl5851user", testRs.getString(1).split("@")[0]); + assertEquals("plug_dest", testRs.getString(2).split("@")[0]); + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + } finally { + if (install_plugin_in_runtime) { + this.stmt.executeUpdate("UNINSTALL PLUGIN test_plugin_server"); + } + } + } + } + + public void testTwoQuestionsPlugin() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + boolean install_plugin_in_runtime = false; + try { + + // install plugin if required + this.rs = this.stmt.executeQuery( + "select (PLUGIN_LIBRARY LIKE 'two_questions%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='two_questions'"); + if (this.rs.next()) { + if (!this.rs.getBoolean(1)) { + install_plugin_in_runtime = true; + } + } else { + install_plugin_in_runtime = true; + } + + if (install_plugin_in_runtime) { + String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; + this.stmt.executeUpdate("INSTALL PLUGIN two_questions SONAME 'auth" + ext + "'"); + } + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + createUser("'wl5851user2'@'%'", "identified WITH two_questions AS 'two_questions_password'"); + this.stmt.executeUpdate("delete from mysql.db where user='wl5851user2'"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + + dbname + "', 'wl5851user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'wl5851user2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("flush privileges"); + + props = new Properties(); + props.setProperty("user", "wl5851user2"); + props.setProperty("password", "two_questions_password"); + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$TwoQuestionsPlugin"); + + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + try { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("wl5851user2", testRs.getString(1).split("@")[0]); + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + } finally { + if (install_plugin_in_runtime) { + this.stmt.executeUpdate("UNINSTALL PLUGIN two_questions"); + } + } + } + } + + public void testThreeAttemptsPlugin() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + boolean install_plugin_in_runtime = false; + try { + + // install plugin if required + this.rs = this.stmt.executeQuery( + "select (PLUGIN_LIBRARY LIKE 'three_attempts%') as `TRUE`" + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='three_attempts'"); + if (this.rs.next()) { + if (!this.rs.getBoolean(1)) { + install_plugin_in_runtime = true; + } + } else { + install_plugin_in_runtime = true; + } + + if (install_plugin_in_runtime) { + String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; + this.stmt.executeUpdate("INSTALL PLUGIN three_attempts SONAME 'auth" + ext + "'"); + } + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + createUser("'wl5851user3'@'%'", "identified WITH three_attempts AS 'three_attempts_password'"); + this.stmt.executeUpdate("delete from mysql.db where user='wl5851user3'"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + + dbname + "', 'wl5851user3', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate( + "insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv,Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', 'information\\_schema', 'wl5851user3', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("flush privileges"); + + props = new Properties(); + props.setProperty("user", "wl5851user3"); + props.setProperty("password", "three_attempts_password"); + props.setProperty("authenticationPlugins", "testsuite.regression.ConnectionRegressionTest$ThreeAttemptsPlugin"); + + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + try { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("wl5851user3", testRs.getString(1).split("@")[0]); + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + } finally { + if (install_plugin_in_runtime) { + this.stmt.executeUpdate("UNINSTALL PLUGIN three_attempts"); + } + } + } + } + + public static class AuthTestPlugin implements AuthenticationPlugin { + + private String password = null; + + public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { + } + + public void destroy() { + this.password = null; + } + + public String getProtocolPluginName() { + return "auth_test_plugin"; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + Buffer bresp = new Buffer(StringUtils.getBytes(this.password)); + toServer.add(bresp); + return true; + } + + } + + public static class TwoQuestionsPlugin implements AuthenticationPlugin { + + private String password = null; + + public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { + } + + public void destroy() { + this.password = null; + } + + public String getProtocolPluginName() { + return "dialog"; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + if ((fromServer.getByteBuffer()[0] & 0xff) == 4) { + Buffer bresp = new Buffer(StringUtils.getBytes(this.password)); + toServer.add(bresp); + } else { + Buffer bresp = new Buffer(StringUtils.getBytes("yes, of course")); + toServer.add(bresp); + } + return true; + } + + } + + public static class ThreeAttemptsPlugin implements AuthenticationPlugin { + + private String password = null; + private int counter = 0; + + public void init(com.mysql.jdbc.Connection conn1, Properties props) throws SQLException { + this.counter = 0; + } + + public void destroy() { + this.password = null; + this.counter = 0; + } + + public String getProtocolPluginName() { + return "dialog"; + } + + public boolean requiresConfidentiality() { + return false; + } + + public boolean isReusable() { + return true; + } + + public void setAuthenticationParameters(String user, String password) { + this.password = password; + } + + public boolean nextAuthenticationStep(Buffer fromServer, List toServer) throws SQLException { + toServer.clear(); + this.counter++; + if ((fromServer.getByteBuffer()[0] & 0xff) == 4) { + Buffer bresp = new Buffer(StringUtils.getBytes(this.counter > 2 ? this.password : "wrongpassword" + this.counter)); + toServer.add(bresp); + } else { + Buffer bresp = new Buffer(fromServer.getByteBuffer()); + toServer.add(bresp); + } + return true; + } + + } + + public void testOldPasswordPlugin() throws Exception { + + if (!versionMeetsMinimum(5, 5, 7) || versionMeetsMinimum(5, 7, 5)) { + // As of 5.7.5, support for mysql_old_password is removed. + System.out.println("testOldPasswordPlugin was skipped: This test is only run for 5.5.7 - 5.7.4 server versions."); + return; + } + + Connection testConn = null; + + try { + this.stmt.executeUpdate("SET @current_secure_auth = @@global.secure_auth"); + this.stmt.executeUpdate("SET GLOBAL secure_auth= off"); + + createUser("'bug64983user1'@'%'", "IDENTIFIED WITH mysql_old_password"); + this.stmt.executeUpdate("set password for 'bug64983user1'@'%' = OLD_PASSWORD('pwd')"); + this.stmt.executeUpdate("grant all on *.* to 'bug64983user1'@'%'"); + + createUser("'bug64983user2'@'%'", "IDENTIFIED WITH mysql_old_password"); + this.stmt.executeUpdate("set password for 'bug64983user2'@'%' = OLD_PASSWORD('')"); + this.stmt.executeUpdate("grant all on *.* to 'bug64983user2'@'%'"); + + createUser("'bug64983user3'@'%'", "IDENTIFIED WITH mysql_old_password"); + this.stmt.executeUpdate("grant all on *.* to 'bug64983user3'@'%'"); + + this.stmt.executeUpdate("flush privileges"); + + Properties props = new Properties(); + + // connect with default plugin + props.setProperty("user", "bug64983user1"); + props.setProperty("password", "pwd"); + testConn = getConnectionWithProps(props); + ResultSet testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); + testConn.close(); + + props.setProperty("user", "bug64983user2"); + props.setProperty("password", ""); + testConn = getConnectionWithProps(props); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); + testConn.close(); + + props.setProperty("user", "bug64983user3"); + props.setProperty("password", ""); + testConn = getConnectionWithProps(props); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); + testConn.close(); + + // connect with MysqlOldPasswordPlugin plugin + props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlOldPasswordPlugin"); + + props.setProperty("user", "bug64983user1"); + props.setProperty("password", "pwd"); + testConn = getConnectionWithProps(props); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); + testConn.close(); + + props.setProperty("user", "bug64983user2"); + props.setProperty("password", ""); + testConn = getConnectionWithProps(props); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); + testConn.close(); + + props.setProperty("user", "bug64983user3"); + props.setProperty("password", ""); + testConn = getConnectionWithProps(props); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); + + // changeUser + ((MySQLConnection) testConn).changeUser("bug64983user1", "pwd"); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user1", testRs.getString(1).split("@")[0]); + + ((MySQLConnection) testConn).changeUser("bug64983user2", ""); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user2", testRs.getString(1).split("@")[0]); + + ((MySQLConnection) testConn).changeUser("bug64983user3", ""); + testRs = testConn.createStatement().executeQuery("select USER()"); + testRs.next(); + assertEquals("bug64983user3", testRs.getString(1).split("@")[0]); + + } finally { + try { + this.stmt.executeUpdate("SET GLOBAL secure_auth = @current_secure_auth"); + + if (testConn != null) { + testConn.close(); + } + } catch (Exception ex) { + System.err.println("Exception during cleanup:"); + ex.printStackTrace(); + } + } + + } + + public void testAuthCleartextPlugin() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + + boolean install_plugin_in_runtime = false; + try { + + // install plugin if required + this.rs = this.stmt.executeQuery("select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE`" + + " FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='cleartext_plugin_server'"); + if (this.rs.next()) { + if (!this.rs.getBoolean(1)) { + install_plugin_in_runtime = true; + } + } else { + install_plugin_in_runtime = true; + } + + if (install_plugin_in_runtime) { + String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; + this.stmt.executeUpdate("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin" + ext + "'"); + } + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + // create proxy users + createUser("'wl5735user'@'%'", "identified WITH cleartext_plugin_server AS ''"); + this.stmt.executeUpdate("delete from mysql.db where user='wl5735user'"); + this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + + "', 'wl5735user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES " + + "('%', 'information\\_schema', 'wl5735user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', " + + "'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("flush privileges"); + + props = new Properties(); + props.setProperty("user", "wl5735user"); + props.setProperty("password", ""); + props.setProperty("useSSL", "false"); + + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + try { + testConn = getConnectionWithProps(props); + fail("SQLException expected due to SSL connection is required"); + } catch (Exception e) { + assertEquals("SSL connection required for plugin 'mysql_clear_password'. Check if \"useSSL\" is set to \"true\".", e.getMessage()); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + try { + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + props.setProperty("useSSL", "true"); + testConn = getConnectionWithProps(props); + + assertTrue("SSL connection isn't actually established!", ((MySQLConnection) testConn).getIO().isSSLEstablished()); + + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + + assertEquals("wl5735user", testRs.getString(1).split("@")[0]); + assertEquals("wl5735user", testRs.getString(2).split("@")[0]); + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + } finally { + if (install_plugin_in_runtime) { + this.stmt.executeUpdate("UNINSTALL PLUGIN cleartext_plugin_server"); + } + } + } + } + + /** + * This test requires two server instances: + * 1) main test server pointed by com.mysql.jdbc.testsuite.url variable + * configured without RSA encryption support + * 2) additional server instance pointed by com.mysql.jdbc.testsuite.url.sha256default + * variable configured with default-authentication-plugin=sha256_password + * and RSA encryption enabled. + * + * To run this test please add this variable to ant call: + * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd + * + * @throws Exception + */ + public void testSha256PasswordPlugin() throws Exception { + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + + /* + * test against server without RSA support + */ + if (versionMeetsMinimum(5, 6, 5)) { + + if (!pluginIsActive(this.stmt, "sha256_password")) { + fail("sha256_password required to run this test"); + } + if (allowsRsa(this.stmt)) { + fail("RSA encryption must be disabled on " + System.getProperty("com.mysql.jdbc.testsuite.url") + " to run this test"); + } + + try { + this.stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); + createUser("'wl5602user'@'%'", "identified WITH sha256_password"); + this.stmt.executeUpdate("grant all on *.* to 'wl5602user'@'%'"); + createUser("'wl5602nopassword'@'%'", "identified WITH sha256_password"); + this.stmt.executeUpdate("grant all on *.* to 'wl5602nopassword'@'%'"); + this.stmt.executeUpdate("SET GLOBAL old_passwords= 2"); + this.stmt.executeUpdate("SET SESSION old_passwords= 2"); + this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'wl5602user'@'%' IDENTIFIED BY 'pwd'" + : "set password for 'wl5602user'@'%' = PASSWORD('pwd')"); + this.stmt.executeUpdate("flush privileges"); + + final Properties propsNoRetrieval = new Properties(); + propsNoRetrieval.setProperty("user", "wl5602user"); + propsNoRetrieval.setProperty("password", "pwd"); + propsNoRetrieval.setProperty("useSSL", "false"); + + final Properties propsNoRetrievalNoPassword = new Properties(); + propsNoRetrievalNoPassword.setProperty("user", "wl5602nopassword"); + propsNoRetrievalNoPassword.setProperty("password", ""); + propsNoRetrievalNoPassword.setProperty("useSSL", "false"); + + final Properties propsAllowRetrieval = new Properties(); + propsAllowRetrieval.setProperty("user", "wl5602user"); + propsAllowRetrieval.setProperty("password", "pwd"); + propsAllowRetrieval.setProperty("allowPublicKeyRetrieval", "true"); + propsAllowRetrieval.setProperty("useSSL", "false"); + + final Properties propsAllowRetrievalNoPassword = new Properties(); + propsAllowRetrievalNoPassword.setProperty("user", "wl5602nopassword"); + propsAllowRetrievalNoPassword.setProperty("password", ""); + propsAllowRetrievalNoPassword.setProperty("allowPublicKeyRetrieval", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); + + // 1. without SSL + // SQLException expected due to server doesn't recognize Public Key Retrieval packet + assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable() { + public Void call() throws Exception { + try { + getConnectionWithProps(propsNoRetrieval); + return null; + } catch (Exception ex) { + ex.printStackTrace(); + throw ex; + } + } + }); + assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable() { + public Void call() throws Exception { + getConnectionWithProps(propsAllowRetrieval); + return null; + } + }); + + assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 2. with serverRSAPublicKeyFile specified + // SQLException expected due to server doesn't recognize RSA encrypted payload + propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + + assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable() { + public Void call() throws Exception { + getConnectionWithProps(propsNoRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Access denied for user 'wl5602user'.*", new Callable() { + public Void call() throws Exception { + getConnectionWithProps(propsAllowRetrieval); + return null; + } + }); + + assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 3. over SSL + propsNoRetrieval.setProperty("useSSL", "true"); + propsNoRetrievalNoPassword.setProperty("useSSL", "true"); + propsAllowRetrieval.setProperty("useSSL", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); + + assertCurrentUser(null, propsNoRetrieval, "wl5602user", true); + assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(null, propsAllowRetrieval, "wl5602user", true); + assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // over SSL with client-default Sha256PasswordPlugin + propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsNoRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsAllowRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + + assertCurrentUser(null, propsNoRetrieval, "wl5602user", true); + assertCurrentUser(null, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(null, propsAllowRetrieval, "wl5602user", true); + assertCurrentUser(null, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + } finally { + this.stmt.executeUpdate("flush privileges"); + this.stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); + } + } + + /* + * test against server with RSA support + */ + if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 6, 5)) { + + if (!pluginIsActive(this.sha256Stmt, "sha256_password")) { + fail("sha256_password required to run this test"); + } + if (!allowsRsa(this.sha256Stmt)) { + fail("RSA encryption must be enabled on " + sha256Url + " to run this test"); + } + + try { + // create user with long password and sha256_password auth + this.sha256Stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); + createUser(this.sha256Stmt, "'wl5602user'@'%'", "identified WITH sha256_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'wl5602user'@'%'"); + createUser(this.sha256Stmt, "'wl5602nopassword'@'%'", "identified WITH sha256_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'wl5602nopassword'@'%'"); + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords= 2"); + this.sha256Stmt.executeUpdate("SET SESSION old_passwords= 2"); + this.sha256Stmt.executeUpdate(((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6) + ? "ALTER USER 'wl5602user'@'%' IDENTIFIED BY 'pwd'" : "set password for 'wl5602user'@'%' = PASSWORD('pwd')"); + this.sha256Stmt.executeUpdate("flush privileges"); + + final Properties propsNoRetrieval = new Properties(); + propsNoRetrieval.setProperty("user", "wl5602user"); + propsNoRetrieval.setProperty("password", "pwd"); + + final Properties propsNoRetrievalNoPassword = new Properties(); + propsNoRetrievalNoPassword.setProperty("user", "wl5602nopassword"); + propsNoRetrievalNoPassword.setProperty("password", ""); + + final Properties propsAllowRetrieval = new Properties(); + propsAllowRetrieval.setProperty("user", "wl5602user"); + propsAllowRetrieval.setProperty("password", "pwd"); + propsAllowRetrieval.setProperty("allowPublicKeyRetrieval", "true"); + + final Properties propsAllowRetrievalNoPassword = new Properties(); + propsAllowRetrievalNoPassword.setProperty("user", "wl5602nopassword"); + propsAllowRetrievalNoPassword.setProperty("password", ""); + propsAllowRetrievalNoPassword.setProperty("allowPublicKeyRetrieval", "true"); + + // 1. with client-default MysqlNativePasswordPlugin + propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); + propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); + + // 1.1. RSA + propsNoRetrieval.setProperty("useSSL", "false"); + propsAllowRetrieval.setProperty("useSSL", "false"); + + assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", false); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 1.2. over SSL + propsNoRetrieval.setProperty("useSSL", "true"); + propsNoRetrievalNoPassword.setProperty("useSSL", "true"); + propsAllowRetrieval.setProperty("useSSL", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); + + assertCurrentUser(sha256Url, propsNoRetrieval, "wl5602user", true); + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", true); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 2. with client-default Sha256PasswordPlugin + propsNoRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsNoRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsAllowRetrieval.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + propsAllowRetrievalNoPassword.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + + // 2.1. RSA + propsNoRetrieval.setProperty("useSSL", "false"); + propsNoRetrievalNoPassword.setProperty("useSSL", "false"); + propsAllowRetrieval.setProperty("useSSL", "false"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); + + assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", false); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 2.2. over SSL + propsNoRetrieval.setProperty("useSSL", "true"); + propsNoRetrievalNoPassword.setProperty("useSSL", "true"); + propsAllowRetrieval.setProperty("useSSL", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); + + assertCurrentUser(sha256Url, propsNoRetrieval, "wl5602user", true); + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", false); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 3. with serverRSAPublicKeyFile specified + propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + + // 3.1. RSA + propsNoRetrieval.setProperty("useSSL", "false"); + propsNoRetrievalNoPassword.setProperty("useSSL", "false"); + propsAllowRetrieval.setProperty("useSSL", "false"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); + + assertCurrentUser(sha256Url, propsNoRetrieval, "wl5602user", false); + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", false); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 3.2. Runtime setServerRSAPublicKeyFile must be denied + final Connection c2 = getConnectionWithProps(sha256Url, propsNoRetrieval); + assertThrows(SQLException.class, "Dynamic change of ''serverRSAPublicKeyFile'' is not allowed.", new Callable() { + public Void call() throws Exception { + ((ConnectionProperties) c2).setServerRSAPublicKeyFile("src/testsuite/ssl-test-certs/mykey.pub"); + return null; + } + }); + c2.close(); + + // 3.3. Runtime setAllowPublicKeyRetrieval must be denied + final Connection c3 = getConnectionWithProps(sha256Url, propsNoRetrieval); + assertThrows(SQLException.class, "Dynamic change of ''allowPublicKeyRetrieval'' is not allowed.", new Callable() { + public Void call() throws Exception { + ((ConnectionProperties) c3).setAllowPublicKeyRetrieval(true); + return null; + } + }); + c3.close(); + + // 3.4. over SSL + propsNoRetrieval.setProperty("useSSL", "true"); + propsNoRetrievalNoPassword.setProperty("useSSL", "true"); + propsAllowRetrieval.setProperty("useSSL", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); + + assertCurrentUser(sha256Url, propsNoRetrieval, "wl5602user", true); + assertCurrentUser(sha256Url, propsNoRetrievalNoPassword, "wl5602nopassword", false); + assertCurrentUser(sha256Url, propsAllowRetrieval, "wl5602user", true); + assertCurrentUser(sha256Url, propsAllowRetrievalNoPassword, "wl5602nopassword", false); + + // 4. with wrong serverRSAPublicKeyFile specified + propsNoRetrieval.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); + propsNoRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); + propsAllowRetrieval.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); + propsAllowRetrievalNoPassword.setProperty("serverRSAPublicKeyFile", "unexistant/dummy.pub"); + + // 4.1. RSA + propsNoRetrieval.setProperty("useSSL", "false"); + propsNoRetrievalNoPassword.setProperty("useSSL", "false"); + propsAllowRetrieval.setProperty("useSSL", "false"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "false"); + + propsNoRetrieval.setProperty("paranoid", "false"); + propsNoRetrievalNoPassword.setProperty("paranoid", "false"); + propsAllowRetrieval.setProperty("paranoid", "false"); + propsAllowRetrievalNoPassword.setProperty("paranoid", "false"); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrievalNoPassword); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrievalNoPassword); + return null; + } + }); + + propsNoRetrieval.setProperty("paranoid", "true"); + propsNoRetrievalNoPassword.setProperty("paranoid", "true"); + propsAllowRetrieval.setProperty("paranoid", "true"); + propsAllowRetrievalNoPassword.setProperty("paranoid", "true"); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrievalNoPassword); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrievalNoPassword); + return null; + } + }); + + // 4.2. over SSL + propsNoRetrieval.setProperty("useSSL", "true"); + propsNoRetrievalNoPassword.setProperty("useSSL", "true"); + propsAllowRetrieval.setProperty("useSSL", "true"); + propsAllowRetrievalNoPassword.setProperty("useSSL", "true"); + + propsNoRetrieval.setProperty("paranoid", "false"); + propsNoRetrievalNoPassword.setProperty("paranoid", "false"); + propsAllowRetrieval.setProperty("paranoid", "false"); + propsAllowRetrievalNoPassword.setProperty("paranoid", "false"); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrievalNoPassword); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key 'unexistant/dummy.pub'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrievalNoPassword); + return null; + } + }); + + propsNoRetrieval.setProperty("paranoid", "true"); + propsNoRetrievalNoPassword.setProperty("paranoid", "true"); + propsAllowRetrieval.setProperty("paranoid", "true"); + propsAllowRetrievalNoPassword.setProperty("paranoid", "true"); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsNoRetrievalNoPassword); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrieval); + return null; + } + }); + assertThrows(SQLException.class, "Unable to read public key ", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, propsAllowRetrievalNoPassword); + return null; + } + }); + + } finally { + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); + } + + } + } + + private void assertCurrentUser(String url, Properties props, String expectedUser, boolean sslRequired) throws SQLException { + Connection connection = url == null ? getConnectionWithProps(props) : getConnectionWithProps(url, props); + if (sslRequired) { + assertTrue("SSL connection isn't actually established!", ((MySQLConnection) connection).getIO().isSSLEstablished()); + } + Statement st = connection.createStatement(); + ResultSet rset = st.executeQuery("select USER(),CURRENT_USER()"); + rset.next(); + assertEquals(expectedUser, rset.getString(1).split("@")[0]); + assertEquals(expectedUser, rset.getString(2).split("@")[0]); + connection.close(); + } + + private boolean pluginIsActive(Statement st, String plugin) throws SQLException { + ResultSet rset = st.executeQuery("select (PLUGIN_STATUS='ACTIVE') as `TRUE` from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME='" + plugin + "'"); + boolean pluginIsActive = false; + if (rset.next()) { + pluginIsActive = rset.getBoolean(1); + } + return pluginIsActive; + } + + private boolean allowsRsa(Statement st) throws SQLException { + boolean allowsRSA = false; + ResultSet rset = st.executeQuery("SHOW STATUS LIKE 'Rsa_public_key'"); + if (rset.next()) { + String key = rset.getString(2); + if (key != null) { + String value = rset.getString(2); + allowsRSA = (value != null && value.length() > 0); + } + } + return allowsRSA; + } + + public void testBug36662() throws Exception { + + try { + String tz1 = TimeUtil.getCanonicalTimezone("MEST", null); + assertNotNull(tz1); + } catch (Exception e1) { + String mes1 = e1.getMessage(); + mes1 = mes1.substring(mes1.lastIndexOf("The timezones that 'MEST' maps to are:") + 39); + try { + String tz2 = TimeUtil.getCanonicalTimezone("CEST", null); + assertEquals(mes1, tz2); + } catch (Exception e2) { + String mes2 = e2.getMessage(); + mes2 = mes2.substring(mes2.lastIndexOf("The timezones that 'CEST' maps to are:") + 39); + assertEquals(mes1, mes2); + } + } + } + + public void testBug37931() throws Exception { + + Connection _conn = null; + Properties props = new Properties(); + props.setProperty("characterSetResults", "ISO88591"); + + try { + _conn = getConnectionWithProps(props); + assertTrue("This point should not be reached.", false); + } catch (Exception e) { + assertEquals("Can't map ISO88591 given for characterSetResults to a supported MySQL encoding.", e.getMessage()); + } finally { + if (_conn != null) { + _conn.close(); + } + } + + props.setProperty("characterSetResults", "null"); + + try { + _conn = getConnectionWithProps(props); + + Statement _stmt = _conn.createStatement(); + ResultSet _rs = _stmt.executeQuery("show variables where variable_name='character_set_results'"); + if (_rs.next()) { + String res = _rs.getString(2); + if (res == null || "NULL".equalsIgnoreCase(res) || res.length() == 0) { + assertTrue(true); + } else { + assertTrue(false); + } + } + } finally { + if (_conn != null) { + _conn.close(); + } + } + } + + public void testBug64205() throws Exception { + if (versionMeetsMinimum(5, 5, 0)) { + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + props = new Properties(); + props.setProperty("characterEncoding", "EUC_JP"); + + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + try { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); + } catch (SQLException e1) { + if (e1.getClass().getName().endsWith("MySQLSyntaxErrorException")) { + assertEquals("Table '" + dbname + ".\u307B\u3052\u307b\u3052' doesn't exist", e1.getMessage()); + } else if (e1.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { + // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists + assertTrue(e1.getMessage().contains("Can't find file")); + } else { + throw e1; + } + + try { + props.setProperty("characterSetResults", "SJIS"); + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testSt.execute("SET lc_messages = 'ru_RU'"); + testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); + } catch (SQLException e2) { + if (e2.getClass().getName().endsWith("MySQLSyntaxErrorException")) { + assertEquals( + "\u0422\u0430\u0431\u043b\u0438\u0446\u0430 '" + dbname + + ".\u307b\u3052\u307b\u3052' \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442", + e2.getMessage()); + } else if (e2.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { + // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists + assertTrue("File not found error message should be russian but is this one: " + e2.getMessage(), + e2.getMessage().indexOf("\u0444\u0430\u0439\u043b") > -1); + } else { + throw e2; + } + } + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + // also test with explicit characterSetResults and cacheServerConfiguration + try { + props.setProperty("characterSetResults", "EUC_JP"); + props.setProperty("cacheServerConfiguration", "true"); + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SELECT * FROM `" + dbname + "`.`\u307b\u3052\u307b\u3052`"); + fail("Exception should be thrown for attemping to query non-existing table"); + } catch (SQLException e1) { + if (e1.getClass().getName().endsWith("MySQLSyntaxErrorException")) { + assertEquals("Table '" + dbname + ".\u307B\u3052\u307b\u3052' doesn't exist", e1.getMessage()); + } else if (e1.getErrorCode() == MysqlErrorNumbers.ER_FILE_NOT_FOUND) { + // this could happen on Windows with 5.5 and 5.6 servers where BUG#14642248 exists + assertTrue(e1.getMessage().contains("Can't find file")); + } else { + throw e1; + } + } finally { + testConn.close(); + } + + // Error messages may also be received after the handshake but before connection initialization is complete. This tests the interpretation of + // errors thrown during this time window using an invalid session variable + try { + props.setProperty("characterSetResults", "EUC_JP"); + props.setProperty("sessionVariables", "lc_messages=ru_RU,invalidVar=1"); + testConn = getConnectionWithProps(props); + fail("Exception should be thrown for attempting to set an unknown system variable"); + } catch (SQLException e1) { + // The Russian version of this error message is 45 characters long. A mis-interpretation, e.g. decoding as latin1, would return a length of 75 + assertEquals(45, e1.getMessage().length()); + } finally { + testConn.close(); + } + } + } + + public void testIsLocal() throws Exception { + boolean normalState = ((ConnectionImpl) this.conn).isServerLocal(); + + if (normalState) { + boolean isNotLocal = ((ConnectionImpl) getConnectionWithProps( + SocketMetadata.Helper.IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME + "=www.oracle.com:3306")).isServerLocal(); + + assertFalse(isNotLocal == normalState); + } + } + + /** + * Tests fix for BUG#57662, Incorrect Query Duration When useNanosForElapsedTime Enabled + * + * @throws Exception + * if the test fails. + */ + public void testBug57662() throws Exception { + + createTable("testBug57662", "(x VARCHAR(10) NOT NULL DEFAULT '')"); + Connection conn_is = null; + try { + Properties props = new Properties(); + props.setProperty("profileSQL", "true"); + props.setProperty("useNanosForElapsedTime", "true"); + props.setProperty("logger", "testsuite.simple.TestBug57662Logger"); + conn_is = getConnectionWithProps(props); + this.rs = conn_is.getMetaData().getColumns(null, null, "testBug57662", "%"); + + assertFalse(((testsuite.simple.TestBug57662Logger) ((ConnectionImpl) conn_is).getLog()).hasNegativeDurations); + + } finally { + if (conn_is != null) { + conn_is.close(); + } + } + + } + + public void testBug14563127() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); + props.setProperty("loadBalanceBlacklistTimeout", "5000"); + props.setProperty("loadBalancePingTimeout", "100"); + props.setProperty("loadBalanceValidateConnectionOnSwapServer", "true"); + + String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + if (portNumber == null) { + portNumber = "3306"; + } + + ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + conn2.setAutoCommit(false); + conn2.createStatement().execute("SELECT 1"); + + // make sure second is added to active connections cache: + ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, -1); + conn2.commit(); + + // switch back to first: + ForcedLoadBalanceStrategy.forceFutureServer("first:" + portNumber, -1); + conn2.commit(); + + // kill second while still in cache: + UnreliableSocketFactory.downHost("second"); + + // force second host to be selected next time: + ForcedLoadBalanceStrategy.forceFutureServer("second:" + portNumber, 1); + + try { + conn2.commit(); // will be on second after this + assertTrue("Connection should not be closed", !conn2.isClosed()); + } catch (SQLException e) { + fail("Should not error because failure to select another server."); + } + conn2.close(); + + } + + /** + * Tests fix for BUG#11237 useCompression=true and LOAD DATA LOCAL INFILE SQL Command + * + * @throws Exception + * if any errors occur + */ + public void testBug11237() throws Exception { + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'"); + this.rs.next(); + if (this.rs.getInt(2) < 4 + 1024 * 1024 * 16 - 1) { + fail("You need to increase max_allowed_packet to at least " + (4 + 1024 * 1024 * 16 - 1) + " before running this test!"); + } + + int requiredSize = 1024 * 1024 * 300; + int fieldLength = 1023; + int loops = requiredSize / 2 / (fieldLength + 1); + + File testFile = File.createTempFile("cj-testloaddata", ".dat"); + testFile.deleteOnExit(); + + // TODO: following cleanup doesn't work correctly during concurrent execution of testsuite + // cleanupTempFiles(testFile, "cj-testloaddata"); + + BufferedOutputStream bOut = new BufferedOutputStream(new FileOutputStream(testFile)); + + for (int i = 0; i < loops; i++) { + for (int j = 0; j < fieldLength; j++) { + bOut.write("a".getBytes()[0]); + } + bOut.write("\t".getBytes()[0]); + for (int j = 0; j < fieldLength; j++) { + bOut.write("b".getBytes()[0]); + } + bOut.write("\n".getBytes()[0]); + } + + bOut.flush(); + bOut.close(); + + createTable("testBug11237", "(field1 VARCHAR(1024), field2 VARCHAR(1024))"); + + StringBuilder fileNameBuf = null; + + if (File.separatorChar == '\\') { + fileNameBuf = new StringBuilder(); + + String fileName = testFile.getAbsolutePath(); + int fileNameLength = fileName.length(); + + for (int i = 0; i < fileNameLength; i++) { + char c = fileName.charAt(i); + + if (c == '\\') { + fileNameBuf.append("/"); + } else { + fileNameBuf.append(c); + } + } + } else { + fileNameBuf = new StringBuilder(testFile.getAbsolutePath()); + } + + Properties props = new Properties(); + props.put("useCompression", "true"); + Connection conn1 = getConnectionWithProps(props); + Statement stmt1 = conn1.createStatement(); + + int updateCount = stmt1.executeUpdate("LOAD DATA LOCAL INFILE '" + fileNameBuf.toString() + "' INTO TABLE testBug11237 CHARACTER SET " + + CharsetMapping.getMysqlCharsetForJavaEncoding(((MySQLConnection) this.conn).getEncoding(), (com.mysql.jdbc.Connection) conn1)); + + assertTrue(updateCount == loops); + + } + + public void testStackOverflowOnMissingInterceptor() throws Exception { + try { + Properties props = new Properties(); + props.setProperty("statementInterceptors", "fooBarBaz"); + + getConnectionWithProps(props).close(); + } catch (Exception e) { + } + } + + public void testExpiredPassword() throws Exception { + if (versionMeetsMinimum(5, 6, 10)) { + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + + Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = urlProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + try { + + createUser("'must_change1'@'%'", "IDENTIFIED BY 'aha'"); + this.stmt.executeUpdate("grant all on `" + dbname + "`.* to 'must_change1'@'%'"); + createUser("'must_change2'@'%'", "IDENTIFIED BY 'aha'"); + this.stmt.executeUpdate("grant all on `" + dbname + "`.* to 'must_change2'@'%' IDENTIFIED BY 'aha'"); + + // TODO workaround for Bug#77732, should be fixed in 5.7.9 + if (versionMeetsMinimum(5, 7, 6)) { + this.stmt.executeUpdate("GRANT SELECT ON `performance_schema`.`session_variables` TO 'must_change1'@'%' IDENTIFIED BY 'aha'"); + this.stmt.executeUpdate("GRANT SELECT ON `performance_schema`.`session_variables` TO 'must_change2'@'%' IDENTIFIED BY 'aha'"); + } + + this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'must_change1'@'%', 'must_change2'@'%' PASSWORD EXPIRE" + : "ALTER USER 'must_change1'@'%' PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); + + Properties props = new Properties(); + + // ALTER USER can be prepared as of 5.6.8 (BUG#14646014) + if (versionMeetsMinimum(5, 6, 8)) { + props.setProperty("useServerPrepStmts", "true"); + testConn = getConnectionWithProps(props); + + this.pstmt = testConn.prepareStatement(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'must_change1'@'%', 'must_change2'@'%' PASSWORD EXPIRE" + : "ALTER USER 'must_change1'@'%' PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); + this.pstmt.executeUpdate(); + this.pstmt.close(); + + this.pstmt = testConn.prepareStatement(versionMeetsMinimum(5, 7, 6) ? "ALTER USER ?, 'must_change2'@'%' PASSWORD EXPIRE" + : "ALTER USER ? PASSWORD EXPIRE, 'must_change2'@'%' PASSWORD EXPIRE"); + this.pstmt.setString(1, "must_change1"); + this.pstmt.executeUpdate(); + this.pstmt.close(); + + testConn.close(); + } + + props.setProperty("user", "must_change1"); + props.setProperty("password", "aha"); + + try { + testConn = getConnectionWithProps(props); + fail("SQLException expected due to password expired"); + } catch (SQLException e1) { + + if (e1.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD + || e1.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { + + props.setProperty("disconnectOnExpiredPasswords", "false"); + try { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); + fail("SQLException expected due to password expired"); + + } catch (SQLException e3) { + if (e3.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + } + testSt.executeUpdate( + versionMeetsMinimum(5, 7, 6) ? "ALTER USER USER() IDENTIFIED BY 'newpwd'" : "SET PASSWORD = PASSWORD('newpwd')"); + testConn.close(); + + props.setProperty("user", "must_change1"); + props.setProperty("password", "newpwd"); + props.setProperty("disconnectOnExpiredPasswords", "true"); + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); + assertTrue(testRs.next()); + + // change user + try { + ((MySQLConnection) testConn).changeUser("must_change2", "aha"); + fail("SQLException expected due to password expired"); + + } catch (SQLException e4) { + if (e4.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD + || e4.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { + props.setProperty("disconnectOnExpiredPasswords", "false"); + testConn = getConnectionWithProps(props); + + try { + ((MySQLConnection) testConn).changeUser("must_change2", "aha"); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); + fail("SQLException expected due to password expired"); + + } catch (SQLException e5) { + if (e5.getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) { + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + } + testSt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER USER() IDENTIFIED BY 'newpwd'" + : "SET PASSWORD = PASSWORD('newpwd')"); + testConn.close(); + + props.setProperty("user", "must_change2"); + props.setProperty("password", "newpwd"); + props.setProperty("disconnectOnExpiredPasswords", "true"); + testConn = getConnectionWithProps(props); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("SHOW VARIABLES LIKE 'disconnect_on_expired_password'"); + assertTrue(testRs.next()); + + } + + } else { + throw e4; + } + } + + } + + } else { + throw e1; + } + + } + + } finally { + if (testRs != null) { + testRs.close(); + } + if (testSt != null) { + testSt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + + } + + } + + public void testBug68011() throws Exception { + + Connection c = null; + try { + Properties props = new Properties(); + props.setProperty("noDatetimeStringSync", "true"); + props.setProperty("useTimezone", "true"); + c = getConnectionWithProps(props); + } catch (SQLException e) { + assertTrue(e.getMessage().contains("noDatetimeStringSync")); + } finally { + if (c != null) { + c.close(); + } + } + } + + /** + * Tests connection attributes + * + * @throws Exception + */ + public void testConnectionAttributes() throws Exception { + if (!versionMeetsMinimum(5, 6)) { + return; + } + Properties props = new Properties(); + props.setProperty("connectionAttributes", "first:one,again:two"); + props.setProperty("user", getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.USER_PROPERTY_KEY)); + Connection attConn = super.getConnectionWithProps(props); + ResultSet rslt = attConn.createStatement() + .executeQuery("SELECT * FROM performance_schema.session_connect_attrs WHERE processlist_id = CONNECTION_ID()"); + Map matchedCounts = new HashMap(); + + // disabling until standard values are defined and implemented + // matchedCounts.put("_os", 0); + // matchedCounts.put("_platform", 0); + matchedCounts.put("_runtime_version", 0); + matchedCounts.put("_runtime_vendor", 0); + matchedCounts.put("_client_version", 0); + matchedCounts.put("_client_license", 0); + matchedCounts.put("_client_name", 0); + matchedCounts.put("first", 0); + matchedCounts.put("again", 0); + + while (rslt.next()) { + String key = rslt.getString(2); + String val = rslt.getString(3); + if (!matchedCounts.containsKey(key)) { + fail("Unexpected connection attribute key: " + key); + } + matchedCounts.put(key, matchedCounts.get(key) + 1); + if (key.equals("_runtime_version")) { + assertEquals(System.getProperty("java.version"), val); + } else if (key.equals("_os")) { + assertEquals(NonRegisteringDriver.OS, val); + } else if (key.equals("_platform")) { + assertEquals(NonRegisteringDriver.PLATFORM, val); + } else if (key.equals("_runtime_vendor")) { + assertEquals(System.getProperty("java.vendor"), val); + } else if (key.equals("_client_version")) { + assertEquals(NonRegisteringDriver.VERSION, val); + } else if (key.equals("_client_license")) { + assertEquals(NonRegisteringDriver.LICENSE, val); + } else if (key.equals("_client_name")) { + assertEquals(NonRegisteringDriver.NAME, val); + } else if (key.equals("first")) { + assertEquals("one", val); + } else if (key.equals("again")) { + assertEquals("two", val); + } + } + + rslt.close(); + attConn.close(); + + for (String key : matchedCounts.keySet()) { + if (matchedCounts.get(key) != 1) { + fail("Incorrect number of entries for key \"" + key + "\": " + matchedCounts.get(key)); + } + } + + props.setProperty("connectionAttributes", "none"); + attConn = super.getConnectionWithProps(props); + rslt = attConn.createStatement().executeQuery("SELECT * FROM performance_schema.session_connect_attrs WHERE processlist_id = CONNECTION_ID()"); + if (rslt.next()) { + fail("Expected no connection attributes."); + } + + } + + /** + * Tests fix for BUG#16224249 - Deadlock on concurrently used LoadBalancedMySQLConnection + * + * @throws Exception + */ + public void testBug16224249() throws Exception { + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + String hostSpec = host; + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + hostSpec = host + ":" + port; + } + + String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + removeHostRelatedProps(props); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + StringBuilder configs = new StringBuilder(); + for (@SuppressWarnings("rawtypes") + Map.Entry entry : props.entrySet()) { + configs.append(entry.getKey()); + configs.append("="); + configs.append(entry.getValue()); + configs.append("&"); + } + + String loadbalanceUrl = String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, configs.toString()); + String failoverUrl = String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, "127.0.0.1:" + port, database, configs.toString()); + + Connection[] loadbalancedconnection = new Connection[] { new NonRegisteringDriver().connect(loadbalanceUrl, null), + new NonRegisteringDriver().connect(loadbalanceUrl, null), new NonRegisteringDriver().connect(loadbalanceUrl, null) }; + + Connection[] failoverconnection = new Connection[] { new NonRegisteringDriver().connect(failoverUrl, null), + new NonRegisteringDriver().connect(failoverUrl, null), new NonRegisteringDriver().connect(failoverUrl, null) }; + + // WebLogic-style test + Class mysqlCls = null; + Class jcls = failoverconnection[0].getClass(); // the driver-level connection, a Proxy in this case... + ClassLoader jcl = jcls.getClassLoader(); + if (jcl != null) { + mysqlCls = jcl.loadClass("com.mysql.jdbc.Connection"); + } else { + mysqlCls = Class.forName("com.mysql.jdbc.Connection", true, null); + } + + if ((mysqlCls != null) && (mysqlCls.isAssignableFrom(jcls))) { + Method abort = mysqlCls.getMethod("abortInternal", new Class[] {}); + boolean hasAbortMethod = abort != null; + assertTrue("abortInternal() method should be found for connection class " + jcls, hasAbortMethod); + } else { + fail("com.mysql.jdbc.Connection interface IS NOT ASSIGNABE from connection class " + jcls); + } + //------------- + + // Concurrent test + System.out.println("Warming up"); + for (int i = 0; i < failoverconnection.length; i++) { + this.stmt = failoverconnection[i].createStatement(); + this.pstmt = failoverconnection[i].prepareStatement("SELECT 1 FROM DUAL"); + for (int j = 0; j < 10000; j++) { + this.rs = this.pstmt.executeQuery(); + this.rs = this.stmt.executeQuery("SELECT 1 FROM DUAL"); + } + } + + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(12); + + ScheduledFuture f1 = scheduler.schedule(new PollTask(failoverconnection[0], 1), 500, TimeUnit.MILLISECONDS); + ScheduledFuture f2 = scheduler.schedule(new PollTask(failoverconnection[1], 2), 500, TimeUnit.MILLISECONDS); + ScheduledFuture f3 = scheduler.schedule(new PollTask(failoverconnection[2], 3), 500, TimeUnit.MILLISECONDS); + ScheduledFuture f4 = scheduler.schedule(new PollTask(loadbalancedconnection[0], 4), 500, TimeUnit.MILLISECONDS); + ScheduledFuture f5 = scheduler.schedule(new PollTask(loadbalancedconnection[1], 5), 500, TimeUnit.MILLISECONDS); + ScheduledFuture f6 = scheduler.schedule(new PollTask(loadbalancedconnection[2], 6), 500, TimeUnit.MILLISECONDS); + + ScheduledFuture f7 = scheduler.schedule(new CancelTask(failoverconnection[0], 7), 600, TimeUnit.MILLISECONDS); + ScheduledFuture f8 = scheduler.schedule(new CancelTask(failoverconnection[1], 8), 600, TimeUnit.MILLISECONDS); + ScheduledFuture f9 = scheduler.schedule(new CancelTask(failoverconnection[2], 9), 600, TimeUnit.MILLISECONDS); + ScheduledFuture f10 = scheduler.schedule(new CancelTask(loadbalancedconnection[0], 10), 600, TimeUnit.MILLISECONDS); + ScheduledFuture f11 = scheduler.schedule(new CancelTask(loadbalancedconnection[1], 11), 600, TimeUnit.MILLISECONDS); + ScheduledFuture f12 = scheduler.schedule(new CancelTask(loadbalancedconnection[2], 12), 600, TimeUnit.MILLISECONDS); + + try { + while (f1.get(5, TimeUnit.SECONDS) != null || f2.get(5, TimeUnit.SECONDS) != null || f3.get(5, TimeUnit.SECONDS) != null + || f4.get(5, TimeUnit.SECONDS) != null || f5.get(5, TimeUnit.SECONDS) != null || f6.get(5, TimeUnit.SECONDS) != null + || f7.get(5, TimeUnit.SECONDS) != null || f8.get(5, TimeUnit.SECONDS) != null || f9.get(5, TimeUnit.SECONDS) != null + || f10.get(5, TimeUnit.SECONDS) != null || f11.get(5, TimeUnit.SECONDS) != null || f12.get(5, TimeUnit.SECONDS) != null) { + System.out.println("waiting"); + } + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + if (this.testServerPrepStmtDeadlockCounter < 12) { + Map tr = Thread.getAllStackTraces(); + for (StackTraceElement[] el : tr.values()) { + System.out.println(); + for (StackTraceElement stackTraceElement : el) { + System.out.println(stackTraceElement); + } + } + } + + for (int i = 0; i < failoverconnection.length; i++) { + try { + this.rs = failoverconnection[i].createStatement().executeQuery("SELECT 1"); + } catch (Exception e1) { + try { + this.rs = failoverconnection[i].createStatement().executeQuery("SELECT 1"); + fail("Connection should be explicitly closed."); + } catch (Exception e2) { + assertTrue(true); + } + } + } + + scheduler.shutdown(); + + } + + /** + * Tests fix for BUG#68763, ReplicationConnection.isMasterConnection() returns false always + * + * @throws Exception + * if the test fails. + */ + public void testBug68763() throws Exception { + + ReplicationConnection replConn = null; + + replConn = (ReplicationConnection) getMasterSlaveReplicationConnection(); + replConn.setReadOnly(true); + assertFalse("isMasterConnection() should be false for slave connection", replConn.isMasterConnection()); + replConn.setReadOnly(false); + assertTrue("isMasterConnection() should be true for master connection", replConn.isMasterConnection()); + + } + + /** + * Tests fix for BUG#68733, ReplicationConnection does not ping all underlying + * active physical connections to slaves. + * + * @throws Exception + * if the test fails. + */ + public void testBug68733() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); + props.setProperty("loadBalancePingTimeout", "100"); + props.setProperty("autoReconnect", "true"); + props.setProperty("retriesAllDown", "1"); + + String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + if (portNumber == null) { + portNumber = "3306"; + } + + ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); + // throw Exception if slave2 gets ping + UnreliableSocketFactory.downHost("slave2"); + + ReplicationConnection conn2 = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, props); + assertTrue("Is not actually on master!", conn2.isMasterConnection()); + + conn2.setAutoCommit(false); + + conn2.commit(); + // go to slaves: + conn2.setReadOnly(true); + + // should succeed, as slave2 has not yet been activated: + conn2.createStatement().execute("/* ping */ SELECT 1"); + // allow connections to slave2: + UnreliableSocketFactory.dontDownHost("slave2"); + // force next re-balance to slave2: + ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); + // re-balance: + conn2.commit(); + // down slave1 (active but not selected slave connection): + UnreliableSocketFactory.downHost("slave1"); + // should succeed, as slave2 is currently selected: + conn2.createStatement().execute("/* ping */ SELECT 1"); + + // make all hosts available + UnreliableSocketFactory.flushAllStaticData(); + + // peg connection to slave2: + ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); + conn2.commit(); + + this.rs = conn2.createStatement().executeQuery("SELECT CONNECTION_ID()"); + this.rs.next(); + int slave2id = this.rs.getInt(1); + + // peg connection to slave1 now: + ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); + conn2.commit(); + + // this is a really hacky way to confirm ping was processed + // by an inactive load-balanced connection, but we lack COM_PING + // counters on the server side, and need to create infrastructure + // to capture what's being sent by the driver separately. + + Thread.sleep(2000); + conn2.createStatement().execute("/* ping */ SELECT 1"); + this.rs = conn2.createStatement().executeQuery("SELECT time FROM information_schema.processlist WHERE id = " + slave2id); + this.rs.next(); + assertTrue("Processlist should be less than 2 seconds due to ping", this.rs.getInt(1) < 2); + + // peg connection to slave2: + ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); + conn2.commit(); + // leaving connection tied to slave2, bring slave2 down and slave1 up: + UnreliableSocketFactory.downHost("slave2"); + + try { + conn2.createStatement().execute("/* ping */ SELECT 1"); + fail("Expected failure because current slave connection is down."); + } catch (SQLException e) { + } + + conn2.close(); + + ForcedLoadBalanceStrategy.forceFutureServer("slave1:" + portNumber, -1); + UnreliableSocketFactory.flushAllStaticData(); + conn2 = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, props); + conn2.setAutoCommit(false); + // go to slaves: + conn2.setReadOnly(true); + + // on slave1 now: + conn2.commit(); + + ForcedLoadBalanceStrategy.forceFutureServer("slave2:" + portNumber, -1); + // on slave2 now: + conn2.commit(); + + // disable master: + UnreliableSocketFactory.downHost("master"); + + // ping should succeed, because we're still attached to slaves: + conn2.createStatement().execute("/* ping */ SELECT 1"); + + // bring master back up: + UnreliableSocketFactory.dontDownHost("master"); + + // get back to master, confirm it's recovered: + conn2.commit(); + conn2.createStatement().execute("/* ping */ SELECT 1"); + try { + conn2.setReadOnly(false); + } catch (SQLException e) { + } + + conn2.commit(); + + // take down both slaves: + UnreliableSocketFactory.downHost("slave1"); + UnreliableSocketFactory.downHost("slave2"); + + assertTrue(conn2.isMasterConnection()); + // should succeed, as we're still on master: + conn2.createStatement().execute("/* ping */ SELECT 1"); + + UnreliableSocketFactory.dontDownHost("slave1"); + UnreliableSocketFactory.dontDownHost("slave2"); + UnreliableSocketFactory.downHost("master"); + + try { + conn2.createStatement().execute("/* ping */ SELECT 1"); + fail("should have failed because master is offline"); + } catch (SQLException e) { + + } + + UnreliableSocketFactory.dontDownHost("master"); + conn2.createStatement().execute("SELECT 1"); + // continue on slave2: + conn2.setReadOnly(true); + + // should succeed, as slave2 is up: + conn2.createStatement().execute("/* ping */ SELECT 1"); + + UnreliableSocketFactory.downHost("slave2"); + + try { + conn2.createStatement().execute("/* ping */ SELECT 1"); + fail("should have failed because slave2 is offline and the active chosen connection."); + } catch (SQLException e) { + } + + conn2.close(); + } + + protected int testServerPrepStmtDeadlockCounter = 0; + + class PollTask implements Runnable { + + private Connection c; + private int num = 0; + + private Statement st1 = null; + private PreparedStatement pst1 = null; + + PollTask(Connection cn, int n) throws SQLException { + this.c = cn; + this.num = n; + + this.st1 = this.c.createStatement(); + this.pst1 = this.c.prepareStatement("SELECT 1 FROM DUAL"); + } + + public void run() { + System.out.println(this.num + ". Start polling at " + new Date().getTime()); + boolean connectionClosed = false; + + for (int i = 0; i < 20000; i++) { + try { + this.st1.executeQuery("SELECT 1 FROM DUAL").close(); + this.pst1.executeQuery().close(); + } catch (Exception ex1) { + if (!connectionClosed) { + System.out.println(this.num + "." + i + " " + ex1.getMessage()); + connectionClosed = true; + } else { + break; + } + } + } + + ConnectionRegressionTest.this.testServerPrepStmtDeadlockCounter++; + System.out.println(this.num + ". Done!"); + } + + } + + class CancelTask implements Runnable { + + private Connection c; + private int num = 0; + + CancelTask(Connection cn, int n) throws SQLException { + this.c = cn; + this.num = n; + } + + public void run() { + System.out.println(this.num + ". Start cancelling at " + new Date().getTime()); + + if (Proxy.isProxyClass(this.c.getClass())) { + try { + if (this.num == 7 || this.num == 10) { + Proxy.getInvocationHandler(this.c).invoke(this.c, Connection.class.getMethod("close", new Class[] {}), null); + } else if (this.num == 8 || this.num == 11) { + Proxy.getInvocationHandler(this.c).invoke(this.c, MySQLConnection.class.getMethod("abortInternal", new Class[] {}), null); + } else if (this.num == 9 || this.num == 12) { + Proxy.getInvocationHandler(this.c).invoke(this.c, com.mysql.jdbc.Connection.class.getMethod("abort", new Class[] { Executor.class }), + new Object[] { new ThreadPerTaskExecutor() }); + } + + ConnectionRegressionTest.this.testServerPrepStmtDeadlockCounter++; + System.out.println(this.num + ". Done!"); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + } + + class ThreadPerTaskExecutor implements Executor { + public void execute(Runnable r) { + new Thread(r).start(); + } + } + + /** + * Tests fix for BUG#68400 useCompression=true and connect to server, zip native method cause out of memory + * + * @throws Exception + * if any errors occur + */ + public void testBug68400() throws Exception { + + Field f = com.mysql.jdbc.NonRegisteringDriver.class.getDeclaredField("connectionPhantomRefs"); + f.setAccessible(true); + Map connectionTrackingMap = (Map) f.get(com.mysql.jdbc.NonRegisteringDriver.class); + + Field referentField = java.lang.ref.Reference.class.getDeclaredField("referent"); + referentField.setAccessible(true); + + createTable("testBug68400", "(x VARCHAR(255) NOT NULL DEFAULT '')"); + String s1 = "a very very very very very very very very very very very very very very very very very very very very very very very very large string to ensure compression enabled"; + this.stmt.executeUpdate("insert into testBug68400 values ('" + s1 + "')"); + + Properties props = new Properties(); + props.setProperty("useCompression", "true"); + props.setProperty("connectionAttributes", "testBug68400:true"); + + testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 0, s1, "testBug68400:true"); + testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 1, s1, "testBug68400:true"); + testMemLeakBatch(props, connectionTrackingMap, referentField, 0, 2, s1, "testBug68400:true"); + + System.out.println("Done."); + + } + + /** + * @param props + * @param connectionType + * 0-ConnectionImpl, 1-LoadBalancedConnection, 2-FailoverConnection, 3-ReplicationConnection + * @param finType + * 0 - none, 1 - close(), 2 - abortInternal() + * @throws Exception + */ + private void testMemLeakBatch(Properties props, Map connectionTrackingMap, Field referentField, int connectionType, int finType, String s1, + String attributeValue) throws Exception { + + Connection connection = null; + Statement statement = null; + ResultSet resultSet = null; + int connectionNumber = 0; + + String[] typeNames = new String[] { "ConnectionImpl", "LoadBalancedConnection", "FailoverConnection", "ReplicationConnection" }; + + System.out.println("\n" + typeNames[connectionType] + ", " + (finType == 0 ? "nullification" : (finType == 1 ? "close()" : "abortInternal()"))); + + // 1. Create 100 connections with "testBug68400:true" attribute + for (int j = 0; j < 20; j++) { + switch (connectionType) { + case 1: + //load-balanced connection + connection = getLoadBalancedConnection(props); + break; + case 2: + //failover connection + Properties baseprops = new Driver().parseURL(BaseTestCase.dbUrl, null); + baseprops.setProperty("autoReconnect", "true"); + baseprops.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + Properties urlProps = new NonRegisteringDriver().parseURL(BaseTestCase.dbUrl, null); + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + baseprops.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + baseprops.remove(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY); + baseprops.remove(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1"); + baseprops.remove(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1"); + + baseprops.setProperty("queriesBeforeRetryMaster", "50"); + baseprops.setProperty("maxReconnects", "1"); + + UnreliableSocketFactory.mapHost("master", host); + UnreliableSocketFactory.mapHost("slave", host); + + baseprops.putAll(props); + + connection = getConnectionWithProps("jdbc:mysql://master:" + port + ",slave:" + port + "/", baseprops); + break; + case 3: + //ReplicationConnection; + Properties replProps = new Properties(); + replProps.putAll(props); + replProps.setProperty("loadBalanceStrategy", ForcedLoadBalanceStrategy.class.getName()); + replProps.setProperty("loadBalancePingTimeout", "100"); + replProps.setProperty("autoReconnect", "true"); + + connection = this.getUnreliableReplicationConnection(new String[] { "master", "slave1", "slave2" }, replProps); + + break; + default: + connection = getConnectionWithProps(props); + break; + } + + statement = connection.createStatement(); + resultSet = statement.executeQuery("select /* a very very very very very very very very very very very very very very very very very very very " + + "very very very very very large string to ensure compression enabled */ x from testBug68400"); + if (resultSet.next()) { + String s2 = resultSet.getString(1); + assertEquals(s1, s2); + } + if (resultSet != null) { + resultSet.close(); + } + + statement.close(); + + if (finType == 1) { + connection.close(); + } else if (finType == 2) { + ((com.mysql.jdbc.Connection) connection).abortInternal(); + } + connection = null; + } + + // 2. Count connections before GC + System.out.println("MAP: " + connectionTrackingMap.size()); + + connectionNumber = countTestConnections(connectionTrackingMap, referentField, false, attributeValue); + System.out.println("Test related connections in MAP before GC: " + connectionNumber); + + // 3. Run GC + Runtime.getRuntime().gc(); + + // 4. Sleep to ensure abandoned connection clean up occurred + Thread.sleep(2000); + + // 5. Count connections before GC + connectionNumber = countTestConnections(connectionTrackingMap, referentField, true, attributeValue); + System.out.println("Test related connections in MAP after GC: " + connectionNumber); + System.out.println("MAP: " + connectionTrackingMap.size()); + + assertEquals( + "No connection with \"" + attributeValue + "\" connection attribute should exist in NonRegisteringDriver.connectionPhantomRefs map after GC", 0, + connectionNumber); + } + + private int countTestConnections(Map connectionTrackingMap, Field referentField, boolean show, String attributValue) throws Exception { + int connectionNumber = 0; + for (Object o1 : connectionTrackingMap.keySet()) { + com.mysql.jdbc.Connection ctmp = (com.mysql.jdbc.Connection) referentField.get(o1); + try { + if (ctmp != null && ctmp.getConnectionAttributes() != null && ctmp.getConnectionAttributes().equals(attributValue)) { + connectionNumber++; + if (show) { + System.out.println(ctmp.toString()); + } + } + } catch (NullPointerException e) { + System.out.println("NullPointerException: \n" + ctmp + "\n" + ctmp.getConnectionAttributes()); + } catch (MySQLNonTransientConnectionException e) { + System.out.println("MySQLNonTransientConnectionException (expected for explicitly closed load-balanced connection)"); + } + } + return connectionNumber; + } + + /** + * Tests fix for BUG#17251955, ARRAYINDEXOUTOFBOUNDSEXCEPTION ON LONG MULTI-BYTE DB/USER NAMES + * + * @throws Exception + */ + public void testBug17251955() throws Exception { + Connection c1 = null; + Statement st1 = null; + Connection c2 = null; + Properties props = new Properties(); + Properties props1 = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = props1.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String url = "jdbc:mysql://" + host; + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + String port = props1.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + url = url + ":" + port; + } + + try { + props.setProperty("characterEncoding", "UTF-8"); + c1 = getConnectionWithProps(props); + st1 = c1.createStatement(); + createDatabase(st1, "`\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`"); + createUser(st1, "'\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8'@'%'", "identified by 'msandbox'"); + st1.execute("grant all on `\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`.* to '\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8'@'%'"); + + props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("user", "\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8"); + props.setProperty("password", "msandbox"); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + c2 = DriverManager.getConnection(url + "/\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8", props); + this.rs = c2.createStatement().executeQuery("select 1"); + c2.close(); + + } catch (SQLException e) { + assertFalse("e.getCause() instanceof java.lang.ArrayIndexOutOfBoundsException", e.getCause() instanceof java.lang.ArrayIndexOutOfBoundsException); + + props.setProperty("user", "\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8"); + c2 = DriverManager.getConnection(url + "/\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8", props); + this.rs = c2.createStatement().executeQuery("select 1"); + c2.close(); + } finally { + if (c2 != null) { + c2.close(); + } + if (st1 != null) { + dropUser(st1, "'\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8'@'%'"); + dropDatabase(st1, "`\u30C6\u30B9\u30C8\u30C6\u30B9\u30C8`"); + st1.close(); + } + if (c1 != null) { + c1.close(); + } + } + } + + /** + * Tests fix for BUG#69506 - XAER_DUPID error code is not returned when a duplicate XID is offered in Java. + * + * @throws Exception + * if the test fails. + */ + public void testBug69506() throws Exception { + MysqlXADataSource dataSource = new MysqlXADataSource(); + + dataSource.setUrl(dbUrl); + + XAConnection testXAConn1 = dataSource.getXAConnection(); + XAConnection testXAConn2 = dataSource.getXAConnection(); + + Xid duplicateXID = new MysqlXid("1".getBytes(), "1".getBytes(), 1); + + testXAConn1.getXAResource().start(duplicateXID, 0); + + try { + testXAConn2.getXAResource().start(duplicateXID, 0); + fail("XAException was expected."); + } catch (XAException e) { + assertEquals("Wrong error code retured for duplicated XID.", XAException.XAER_DUPID, e.errorCode); + } + } + + /** + * Tests fix for BUG#69746, ResultSet closed after Statement.close() when dontTrackOpenResources=true + * active physical connections to slaves. + * + * @throws Exception + * if the test fails. + */ + public void testBug69746() throws Exception { + Connection testConnection; + Statement testStatement; + ResultSet testResultSet; + + /* + * Test explicit closes + */ + testConnection = getConnectionWithProps("dontTrackOpenResources=true"); + testStatement = testConnection.createStatement(); + testResultSet = testStatement.executeQuery("SELECT 1"); + + assertFalse("Connection should not be closed.", testConnection.isClosed()); + assertFalse("Statement should not be closed.", isStatementClosedForTestBug69746(testStatement)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + testConnection.close(); + + assertTrue("Connection should be closed.", testConnection.isClosed()); + assertFalse("Statement should not be closed.", isStatementClosedForTestBug69746(testStatement)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + testStatement.close(); + + assertTrue("Connection should be closed.", testConnection.isClosed()); + assertTrue("Statement should be closed.", isStatementClosedForTestBug69746(testStatement)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + testResultSet.close(); + + assertTrue("Connection should be closed.", testConnection.isClosed()); + assertTrue("Statement should be closed.", isStatementClosedForTestBug69746(testStatement)); + assertTrue("ResultSet should be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + /* + * Test implicit closes + */ + // Prepare test objects + createProcedure("testBug69746_proc", "() BEGIN SELECT 1; SELECT 2; SELECT 3; END"); + createTable("testBug69746_tbl", "(fld1 INT NOT NULL AUTO_INCREMENT, fld2 INT, PRIMARY KEY(fld1))"); + + testConnection = getConnectionWithProps("dontTrackOpenResources=true"); + testStatement = testConnection.createStatement(); + testResultSet = testStatement.executeQuery("SELECT 1"); + + // 1. Statement.execute() & Statement.getMoreResults() + this.rs = testStatement.executeQuery("CALL testBug69746_proc"); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + ResultSet testResultSet2 = testStatement.getResultSet(); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); + + testStatement.getMoreResults(); + ResultSet testResultSet3 = testStatement.getResultSet(); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); + + testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT); + ResultSet testResultSet4 = testStatement.getResultSet(); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet4)); + + testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet2)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet3)); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet4)); + + // 2. Statement.executeBatch() + testStatement.addBatch("INSERT INTO testBug69746_tbl (fld2) VALUES (1)"); + testStatement.addBatch("INSERT INTO testBug69746_tbl (fld2) VALUES (2)"); + testStatement.executeBatch(); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + // 3. Statement.executeQuery() + this.rs = testStatement.executeQuery("SELECT 2"); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + // 4. Statement.executeUpdate() + testStatement.executeUpdate("INSERT INTO testBug69746_tbl (fld2) VALUES (3)"); + assertFalse("ResultSet should not be closed.", isResultSetClosedForTestBug69746(testResultSet)); + + testResultSet.close(); + testResultSet2.close(); + testResultSet3.close(); + testResultSet4.close(); + testStatement.close(); + testConnection.close(); + } + + private boolean isStatementClosedForTestBug69746(Statement statement) { + try { + statement.getResultSet(); + } catch (SQLException ex) { + return ex.getMessage().equalsIgnoreCase(Messages.getString("Statement.49")); + } + return false; + } + + private boolean isResultSetClosedForTestBug69746(ResultSet resultSet) { + try { + resultSet.first(); + } catch (SQLException ex) { + return ex.getMessage().equalsIgnoreCase(Messages.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144")); + } + return false; + } + + /** + * This test requires additional server instance configured with + * default-authentication-plugin=sha256_password and RSA encryption enabled. + * + * To run this test please add this variable to ant call: + * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd + * + * @throws Exception + */ + public void testLongAuthResponsePayload() throws Exception { + + if (this.sha256Conn != null && versionMeetsMinimum(5, 6, 6)) { + + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + + // check that sha256_password plugin is available + if (!pluginIsActive(this.sha256Stmt, "sha256_password")) { + fail("sha256_password required to run this test"); + } + + try { + // create user with long password and sha256_password auth + this.sha256Stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); + createUser(this.sha256Stmt, "'wl6134user'@'%'", "identified WITH sha256_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'wl6134user'@'%'"); + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords= 2"); + this.sha256Stmt.executeUpdate("SET SESSION old_passwords= 2"); + this.sha256Stmt.executeUpdate(((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6) + ? "ALTER USER 'wl6134user'@'%' IDENTIFIED BY 'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee'" + : "set password for 'wl6134user'@'%' = PASSWORD('aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee')"); + this.sha256Stmt.executeUpdate("flush privileges"); + + props.setProperty("user", "wl6134user"); + props.setProperty("password", + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeaaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee"); + props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + props.setProperty("useSSL", "false"); + + Connection testConn = null; + try { + testConn = DriverManager.getConnection(sha256Url, props); + fail("SQLException expected due to password is too long for RSA encryption"); + } catch (Exception e) { + assertTrue(e.getMessage().startsWith("Data must not be longer than")); + } finally { + if (testConn != null) { + testConn.close(); + } + } + + try { + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + + props.setProperty("useSSL", "true"); + assertCurrentUser(sha256Url, props, "wl6134user", true); + + } catch (Exception e) { + throw e; + } finally { + if (testConn != null) { + testConn.close(); + } + } + } finally { + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); + } + } + } + + /** + * Tests fix for Bug#69452 - Memory size connection property doesn't support large values well + * + * @throws Exception + * if the test fails. + */ + public void testBug69452() throws Exception { + String[][] testMemUnits = new String[][] { { "k", "kb", "kB", "K", "Kb", "KB" }, { "m", "mb", "mB", "M", "Mb", "MB" }, + { "g", "gb", "gB", "G", "Gb", "GB" } }; + com.mysql.jdbc.Connection connWithMemProps; + long[] memMultiplier = new long[] { 1024, 1024 * 1024, 1024 * 1024 * 1024 }; + + // reflection is needed to access protected info from ConnectionPropertiesImpl.largeRowSizeThreshold + Field propField = com.mysql.jdbc.ConnectionPropertiesImpl.class.getDeclaredField("largeRowSizeThreshold"); + propField.setAccessible(true); + Class propClass = null; + for (Class nestedClass : com.mysql.jdbc.ConnectionPropertiesImpl.class.getDeclaredClasses()) { + if (nestedClass.getName().equals("com.mysql.jdbc.ConnectionPropertiesImpl$IntegerConnectionProperty")) { + propClass = nestedClass; + break; + } + } + + if (propClass != null) { + Method propMethod = propClass.getDeclaredMethod("getValueAsInt"); + propMethod.setAccessible(true); + + for (int i = 0; i < testMemUnits.length; i++) { + for (int j = 0; j < testMemUnits[i].length; j++) { + // testing with memory values under 2GB because higher values aren't supported. + connWithMemProps = (com.mysql.jdbc.Connection) getConnectionWithProps( + String.format("blobSendChunkSize=1.2%1$s,largeRowSizeThreshold=1.4%1$s,locatorFetchBufferSize=1.6%1$s", testMemUnits[i][j])); + + // test values of property 'blobSendChunkSize' + assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'blobSendChunkSize'", (int) (memMultiplier[i] * 1.2), + connWithMemProps.getBlobSendChunkSize()); + + // test values of property 'largeRowSizeThreshold' + assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'largeRowSizeThreshold'", "1.4" + testMemUnits[i][j], + connWithMemProps.getLargeRowSizeThreshold()); + assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'largeRowSizeThreshold'", (int) (memMultiplier[i] * 1.4), + ((Integer) propMethod.invoke(propField.get(connWithMemProps))).intValue()); + + // test values of property 'locatorFetchBufferSize' + assertEquals("Memory unit '" + testMemUnits[i][j] + "'; property 'locatorFetchBufferSize'", (int) (memMultiplier[i] * 1.6), + connWithMemProps.getLocatorFetchBufferSize()); + + connWithMemProps.close(); + } + } + } + } + + /** + * Tests fix for Bug#69777 - Setting maxAllowedPacket below 8203 makes blobSendChunkSize negative. + * + * @throws Exception + * if any errors occur + */ + public void testBug69777() throws Exception { + final int maxPacketSizeThreshold = 8203; // ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE + 11 + + // test maxAllowedPacket below threshold and useServerPrepStmts=true + assertThrows(SQLException.class, "Connection setting too low for 'maxAllowedPacket'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + (maxPacketSizeThreshold - 1)).close(); + return null; + } + }); + + assertThrows(SQLException.class, "Connection setting too low for 'maxAllowedPacket'.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + maxPacketSizeThreshold).close(); + return null; + } + }); + + // the following instructions should execute without any problem + + // test maxAllowedPacket above threshold and useServerPrepStmts=true + getConnectionWithProps("useServerPrepStmts=true,maxAllowedPacket=" + (maxPacketSizeThreshold + 1)).close(); + + // test maxAllowedPacket below threshold and useServerPrepStmts=false + getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + (maxPacketSizeThreshold - 1)).close(); + + // test maxAllowedPacket on threshold and useServerPrepStmts=false + getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + maxPacketSizeThreshold).close(); + + // test maxAllowedPacket above threshold and useServerPrepStmts=false + getConnectionWithProps("useServerPrepStmts=false,maxAllowedPacket=" + (maxPacketSizeThreshold + 1)).close(); + } + + /** + * Tests fix for BUG#69579 - DriverManager.setLoginTimeout not honored. + * + * @throws Exception + * if the test fails. + */ + public void testBug69579() throws Exception { + // Mock Server that accepts network connections and does nothing with them, for connection timeout testing. + class MockServer implements Runnable { + private ServerSocket serverSocket = null; + + int initialize() throws IOException { + this.serverSocket = new ServerSocket(0); + return this.serverSocket.getLocalPort(); + } + + void releaseResources() { + System.out.println("Start releasing mock server resources."); + if (this.serverSocket != null) { + try { + this.serverSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void run() { + if (this.serverSocket == null) { + throw new Error("Mock server not initialized."); + } + Socket clientSocket = null; + try { + while ((clientSocket = this.serverSocket.accept()) != null) { + System.out.println("Client socket accepted: [" + clientSocket.toString() + "]"); + } + } catch (IOException e) { + System.out.println("Shutting down mock server."); + } finally { + if (clientSocket != null) { + try { + clientSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + + ExecutorService executor = Executors.newCachedThreadPool(); + MockServer mockServer = new MockServer(); + int serverPort = 0; + try { + serverPort = mockServer.initialize(); + } catch (IOException e1) { + fail("Failed to initialize a mock server."); + } + final String testURL = "jdbc:mysql://localhost:" + serverPort; + Connection testConn = null; + final int oldLoginTimeout = DriverManager.getLoginTimeout(); + final int loginTimeout = 3; + final int testTimeout = loginTimeout * 2; + long timestamp = System.currentTimeMillis(); + + try { + DriverManager.setLoginTimeout(loginTimeout); + + executor.execute(mockServer); + + Future future = executor.submit(new Callable() { + @SuppressWarnings("synthetic-access") + public Connection call() throws Exception { + return getConnectionWithProps(testURL, ""); + } + }); + + testConn = future.get(testTimeout, TimeUnit.SECONDS); + testConn.close(); + + fail("The connection attempt should have timed out."); + + } catch (InterruptedException e) { + e.printStackTrace(); + fail("Failed to establish a connection with mock server."); + + } catch (ExecutionException e) { + if (e.getCause() instanceof SQLException) { + e.printStackTrace(); + assertTrue(e.getCause().getMessage().startsWith("Communications link failure") + || e.getCause().getMessage().equals(Messages.getString("Connection.LoginTimeout"))); + + assertEquals("Login timeout should have occured in (secs.):", loginTimeout, (System.currentTimeMillis() - timestamp) / 1000); + } else { + fail("Failed to establish a connection with mock server."); + } + + } catch (TimeoutException e) { + fail("Time expired for connection attempt."); + + } finally { + DriverManager.setLoginTimeout(oldLoginTimeout); + mockServer.releaseResources(); + executor.shutdownNow(); + } + } + + /** + * Tests fix for Bug#71038, Add an option for custom collations detection + * + * @throws Exception + */ + public void testBug71038() throws Exception { + Properties p = new Properties(); + p.setProperty("useSSL", "false"); + p.setProperty("detectCustomCollations", "false"); + p.setProperty("statementInterceptors", Bug71038StatementInterceptor.class.getName()); + + MySQLConnection c = (MySQLConnection) getConnectionWithProps(p); + Bug71038StatementInterceptor si = (Bug71038StatementInterceptor) c.getStatementInterceptorsInstances().get(0); + assertTrue("SHOW COLLATION was issued when detectCustomCollations=false", si.cnt == 0); + c.close(); + + p.setProperty("detectCustomCollations", "true"); + p.setProperty("statementInterceptors", Bug71038StatementInterceptor.class.getName()); + + c = (MySQLConnection) getConnectionWithProps(p); + si = (Bug71038StatementInterceptor) c.getStatementInterceptorsInstances().get(0); + assertTrue("SHOW COLLATION wasn't issued when detectCustomCollations=true", si.cnt > 0); + c.close(); + } + + /** + * Counts the number of issued "SHOW COLLATION" statements. + */ + public static class Bug71038StatementInterceptor extends BaseStatementInterceptor { + int cnt = 0; + + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql.contains("SHOW COLLATION")) { + this.cnt++; + } + return null; + } + } + + /** + * Internal method for tests to get a replcation connection with a + * single master host to the test URL. + */ + private ReplicationConnection getTestReplicationConnectionNoSlaves(String masterHost) throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + List masterHosts = new ArrayList(); + masterHosts.add(masterHost); + List slaveHosts = new ArrayList(); // empty + ReplicationConnection replConn = ReplicationConnectionProxy.createProxyInstance(masterHosts, props, slaveHosts, props); + return replConn; + } + + /** + * Test that we remain on the master when: + * - the connection is not in read-only mode + * - no slaves are configured + * - a new slave is added + */ + public void testReplicationConnectionNoSlavesRemainOnMaster() throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + String masterHost = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY) + ":" + props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + ReplicationConnection replConn = getTestReplicationConnectionNoSlaves(masterHost); + Statement s = replConn.createStatement(); + ResultSet rs1 = s.executeQuery("select CONNECTION_ID()"); + assertTrue(rs1.next()); + int masterConnectionId = rs1.getInt(1); + rs1.close(); + s.close(); + + // add a slave and make sure we are NOT on a new connection + replConn.addSlaveHost(masterHost); + s = replConn.createStatement(); + rs1 = s.executeQuery("select CONNECTION_ID()"); + assertTrue(rs1.next()); + assertEquals(masterConnectionId, rs1.getInt(1)); + assertFalse(replConn.isReadOnly()); + rs1.close(); + s.close(); + } + + public void testReplicationConnectionNoSlavesBasics() throws Exception { + // create a replication connection with only a master, get the + // connection id for later use + Properties props = getPropertiesFromTestsuiteUrl(); + String masterHost = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY) + ":" + props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + ReplicationConnection replConn = getTestReplicationConnectionNoSlaves(masterHost); + replConn.setAutoCommit(false); + Statement s = replConn.createStatement(); + ResultSet rs1 = s.executeQuery("select CONNECTION_ID()"); + assertTrue(rs1.next()); + int masterConnectionId = rs1.getInt(1); + assertFalse(replConn.isReadOnly()); + rs1.close(); + s.close(); + + // make sure we are still on the same connection after going + // to read-only mode. There are no slaves, so no other + // connections are possible + replConn.setReadOnly(true); + assertTrue(replConn.isReadOnly()); + assertTrue(replConn.getCurrentConnection().isReadOnly()); + s = replConn.createStatement(); + try { + s.executeUpdate("truncate non_existing_table"); + fail("executeUpdate should not be allowed in read-only mode"); + } catch (SQLException ex) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex.getSQLState()); + } + try { + s.execute("truncate non_existing_table"); + fail("executeUpdate should not be allowed in read-only mode"); + } catch (SQLException ex) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, ex.getSQLState()); + } + rs1 = s.executeQuery("select CONNECTION_ID()"); + assertTrue(rs1.next()); + assertEquals(masterConnectionId, rs1.getInt(1)); + rs1.close(); + s.close(); + + // add a slave and make sure we are on a new connection + replConn.addSlaveHost(masterHost); + s = replConn.createStatement(); + rs1 = s.executeQuery("select CONNECTION_ID()"); + assertTrue(rs1.next()); + assertTrue(rs1.getInt(1) != masterConnectionId); + rs1.close(); + s.close(); + + // switch back to master + replConn.setReadOnly(false); + s = replConn.createStatement(); + rs1 = s.executeQuery("select CONNECTION_ID()"); + assertFalse(replConn.isReadOnly()); + assertFalse(replConn.getCurrentConnection().isReadOnly()); + assertTrue(rs1.next()); + assertEquals(masterConnectionId, rs1.getInt(1)); + rs1.close(); + s.close(); + + // removing the slave should switch back to the master + replConn.setReadOnly(true); + replConn.removeSlave(masterHost); + replConn.commit(); + s = replConn.createStatement(); + rs1 = s.executeQuery("select CONNECTION_ID()"); + // should be maintained even though we're back on the master + assertTrue(replConn.isReadOnly()); + assertTrue(replConn.getCurrentConnection().isReadOnly()); + assertTrue(rs1.next()); + assertEquals(masterConnectionId, rs1.getInt(1)); + rs1.close(); + s.close(); + } + + /** + * Tests fix for Bug#71850 - init() is called twice on exception interceptors + * + * @throws Exception + * if the test fails. + */ + public void testBug71850() throws Exception { + assertThrows(Exception.class, "ExceptionInterceptor.init\\(\\) called 1 time\\(s\\)", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps( + "exceptionInterceptors=testsuite.regression.ConnectionRegressionTest$TestBug71850ExceptionInterceptor," + "user=unexistent_user"); + return null; + } + }); + } + + public static class TestBug71850ExceptionInterceptor implements ExceptionInterceptor { + + private int counter = 0; + + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + this.counter++; + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { + + return new SQLException("ExceptionInterceptor.init() called " + this.counter + " time(s)"); + } + + } + + /** + * Tests fix for BUG#67803 - XA commands sent twice to MySQL server + * + * @throws Exception + * if the test fails. + */ + public void testBug67803() throws Exception { + MysqlXADataSource dataSource = new MysqlXADataSource(); + dataSource.setUrl(dbUrl); + dataSource.setUseCursorFetch(true); + dataSource.setDefaultFetchSize(50); + dataSource.setUseServerPrepStmts(true); + dataSource.setExceptionInterceptors("testsuite.regression.ConnectionRegressionTest$TestBug67803ExceptionInterceptor"); + + XAConnection testXAConn1 = dataSource.getXAConnection(); + testXAConn1.getXAResource().start(new MysqlXid("2".getBytes(), "2".getBytes(), 1), 0); + } + + public static class TestBug67803ExceptionInterceptor implements ExceptionInterceptor { + + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { + if (sqlEx.getErrorCode() == 1295 || sqlEx.getMessage().contains("This command is not supported in the prepared statement protocol yet")) { + // SQLException will not be re-thrown if emulateUnsupportedPstmts=true, thus throw RuntimeException to fail the test + throw new RuntimeException(sqlEx); + } + return sqlEx; + } + + } + + /** + * Test for Bug#72712 - SET NAMES issued unnecessarily. + * + * Using a statement interceptor, ensure that SET NAMES is not + * called if the encoding requested by the client application + * matches that of character_set_server. + * + * Also test that character_set_results is not set unnecessarily. + */ + public void testBug72712() throws Exception { + // this test is only run when character_set_server=latin1 + if (!((MySQLConnection) this.conn).getServerVariable("character_set_server").equals("latin1")) { + return; + } + + Properties p = new Properties(); + p.setProperty("characterEncoding", "cp1252"); + p.setProperty("characterSetResults", "cp1252"); + p.setProperty("statementInterceptors", Bug72712StatementInterceptor.class.getName()); + + getConnectionWithProps(p); + // exception will be thrown from the statement interceptor if any SET statements are issued + } + + /** + * Statement interceptor used to implement preceding test. + */ + public static class Bug72712StatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql.contains("SET NAMES") || sql.contains("character_set_results") && !(sql.contains("SHOW VARIABLES") || sql.contains("SELECT @@"))) { + throw new SQLException("Wrongt statement issued: " + sql); + } + return null; + } + } + + /** + * Test for Bug#62577 - XA connection fails with ClassCastException + */ + public void testBug62577() throws Exception { + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + String hostSpec = host; + + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + hostSpec = host + ":" + port; + } + + String database = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + removeHostRelatedProps(props); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + StringBuilder configs = new StringBuilder(); + for (@SuppressWarnings("rawtypes") + Map.Entry entry : props.entrySet()) { + configs.append(entry.getKey()); + configs.append("="); + configs.append(entry.getValue()); + configs.append("&"); + } + String cfg1 = configs.toString(); + + configs.append("pinGlobalTxToPhysicalConnection"); + configs.append("="); + configs.append("true"); + String cfg2 = configs.toString(); + + // load-balance + testBug62577TestUrl(String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg1)); + testBug62577TestUrl(String.format("jdbc:mysql:loadbalance://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg2)); + // failover + testBug62577TestUrl(String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg1)); + testBug62577TestUrl(String.format("jdbc:mysql://%s,%s/%s?%s", hostSpec, hostSpec, database, cfg2)); + } + + private void testBug62577TestUrl(String url) throws Exception { + MysqlXADataSource dataSource = new MysqlXADataSource(); + dataSource.setUrl(url); + XAConnection xaConn = dataSource.getXAConnection(); + Statement st = xaConn.getConnection().createStatement(); + this.rs = st.executeQuery("SELECT 1;"); + xaConn.close(); + } + + /** + * Test fix for Bug#18869381 - CHANGEUSER() FOR SHA USER RESULTS IN NULLPOINTEREXCEPTION + * + * This test requires additional server instance configured with + * default-authentication-plugin=sha256_password and RSA encryption enabled. + * + * To run this test please add this variable to ant call: + * -Dcom.mysql.jdbc.testsuite.url.sha256default=jdbc:mysql://localhost:3307/test?user=root&password=pwd + * + * @throws Exception + */ + public void testBug18869381() throws Exception { + + if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 6, 6)) { + + if (!pluginIsActive(this.sha256Stmt, "sha256_password")) { + fail("sha256_password required to run this test"); + } + + try { + this.sha256Stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); + createUser(this.sha256Stmt, "'bug18869381user1'@'%'", "identified WITH sha256_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'bug18869381user1'@'%'"); + createUser(this.sha256Stmt, "'bug18869381user2'@'%'", "identified WITH sha256_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'bug18869381user2'@'%'"); + createUser(this.sha256Stmt, "'bug18869381user3'@'%'", "identified WITH mysql_native_password"); + this.sha256Stmt.executeUpdate("grant all on *.* to 'bug18869381user3'@'%'"); + this.sha256Stmt.executeUpdate(((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6) + ? "ALTER USER 'bug18869381user3'@'%' IDENTIFIED BY 'pwd3'" : "set password for 'bug18869381user3'@'%' = PASSWORD('pwd3')"); + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords= 2"); + this.sha256Stmt.executeUpdate("SET SESSION old_passwords= 2"); + this.sha256Stmt.executeUpdate(((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6) + ? "ALTER USER 'bug18869381user1'@'%' IDENTIFIED BY 'LongLongLongLongLongLongLongLongLongLongLongLongPwd1'" + : "set password for 'bug18869381user1'@'%' = PASSWORD('LongLongLongLongLongLongLongLongLongLongLongLongPwd1')"); + this.sha256Stmt.executeUpdate(((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6) + ? "ALTER USER 'bug18869381user2'@'%' IDENTIFIED BY 'pwd2'" : "set password for 'bug18869381user2'@'%' = PASSWORD('pwd2')"); + this.sha256Stmt.executeUpdate("flush privileges"); + + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + + props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.MysqlNativePasswordPlugin"); + props.setProperty("useCompression", "false"); + testBug18869381WithProperties(props); + props.setProperty("useCompression", "true"); + testBug18869381WithProperties(props); + + props.setProperty("defaultAuthenticationPlugin", "com.mysql.jdbc.authentication.Sha256PasswordPlugin"); + props.setProperty("useCompression", "false"); + testBug18869381WithProperties(props); + props.setProperty("useCompression", "true"); + testBug18869381WithProperties(props); + + props.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + props.setProperty("useCompression", "false"); + testBug18869381WithProperties(props); + props.setProperty("useCompression", "true"); + testBug18869381WithProperties(props); + + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + props.setProperty("useSSL", "true"); + props.setProperty("useCompression", "false"); + testBug18869381WithProperties(props); + props.setProperty("useCompression", "true"); + testBug18869381WithProperties(props); + + } finally { + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); + } + } + } + + private void testBug18869381WithProperties(Properties props) throws Exception { + Connection testConn = null; + Statement testSt = null; + ResultSet testRs = null; + + try { + testConn = getConnectionWithProps(sha256Url, props); + + ((MySQLConnection) testConn).changeUser("bug18869381user1", "LongLongLongLongLongLongLongLongLongLongLongLongPwd1"); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("bug18869381user1", testRs.getString(1).split("@")[0]); + assertEquals("bug18869381user1", testRs.getString(2).split("@")[0]); + testSt.close(); + + ((MySQLConnection) testConn).changeUser("bug18869381user2", "pwd2"); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("bug18869381user2", testRs.getString(1).split("@")[0]); + assertEquals("bug18869381user2", testRs.getString(2).split("@")[0]); + testSt.close(); + + ((MySQLConnection) testConn).changeUser("bug18869381user3", "pwd3"); + testSt = testConn.createStatement(); + testRs = testSt.executeQuery("select USER(),CURRENT_USER()"); + testRs.next(); + assertEquals("bug18869381user3", testRs.getString(1).split("@")[0]); + assertEquals("bug18869381user3", testRs.getString(2).split("@")[0]); + + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + + /** + * Tests fix for BUG#73053 - Endless loop in MysqlIO.clearInputStream due to Linux kernel bug. + * + * @throws Exception + * if the test fails. + */ + public void testBug73053() throws Exception { + /* + * Test reported issue using a Socket implementation that simulates the buggy behavior. + */ + try { + Connection testConn = getConnectionWithProps("socketFactory=testsuite.regression.ConnectionRegressionTest$TestBug73053SocketFactory"); + Statement testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT 1"); + testStmt.close(); + testConn.close(); + } catch (SQLException e) { + e.printStackTrace(); + fail("No SQLException should be thrown."); + } + + /* + * Test the re-implementation of the method that was reported to fail - MysqlIO.clearInputStream() in a normal situation were there actually are bytes + * to clear out. When running multi-queries with streaming results, if not all results are consumed then the socket has to be cleared out when closing + * the statement, thus calling MysqlIO.clearInputStream() and effectively discard unread data. + */ + try { + Connection testConn = getConnectionWithProps("allowMultiQueries=true"); + + Statement testStmt = testConn.createStatement(); + testStmt.setFetchSize(Integer.MIN_VALUE); // set for streaming results + + ResultSet testRS = testStmt.executeQuery("SELECT 1; SELECT 2; SELECT 3; SELECT 4"); + + assertTrue(testRS.next()); + assertEquals(1, testRS.getInt(1)); + + assertTrue(testStmt.getMoreResults()); + testStmt.getResultSet(); + + testStmt.close(); + testConn.close(); + } catch (SQLException e) { + fail("No SQLException should be thrown."); + } + + /* + * Test another scenario that may be able to reproduce the bug, as reported by some (never effectively verified though). + */ + try { + final int timeout = 10000; + final String query = "SELECT SLEEP(15)"; + + // 1. run a very slow query in a different thread + Executors.newSingleThreadExecutor().execute(new Runnable() { + public void run() { + try { + // set socketTimeout so this thread doesn't hang if no exception is thrown after killing the connection at server side + @SuppressWarnings("synthetic-access") + Connection testConn = getConnectionWithProps("socketTimeout=" + timeout); + Statement testStmt = testConn.createStatement(); + try { + testStmt.execute(query); + } catch (SQLException e) { + assertEquals("Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.", + e.getCause().getMessage()); + } + testStmt.close(); + testConn.close(); + } catch (SQLException e) { + fail("No SQLException should be thrown."); + } + } + }); + + // 2. kill the connection running the slow query, at server side, to make sure the driver doesn't hang after its killed + final long timestamp = System.currentTimeMillis(); + long elapsedTime = 0; + + boolean run = true; + while (run) { + this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); + while (this.rs.next()) { + if (query.equals(this.rs.getString(8))) { + this.stmt.execute("KILL CONNECTION " + this.rs.getInt(1)); + run = false; + break; + } + } + if (run) { + Thread.sleep(250); + } + elapsedTime = System.currentTimeMillis() - timestamp; + + // allow it 10% more time to reach the socketTimeout threshold + if (elapsedTime > timeout * 1.1) { + fail("Failed to kill the connection at server side."); + } + } + } catch (SQLException e) { + fail("No SQLException should be thrown."); + } + } + + public static class TestBug73053SocketFactory extends StandardSocketFactory { + Socket underlyingSocket; + + @Override + public Socket connect(String hostname, int portNumber, Properties props) throws SocketException, IOException { + return this.underlyingSocket = new ConnectionRegressionTest.TestBug73053SocketWrapper(super.connect(hostname, portNumber, props)); + } + + @Override + public Socket beforeHandshake() throws SocketException, IOException { + super.beforeHandshake(); + return this.underlyingSocket; + } + + @Override + public Socket afterHandshake() throws SocketException, IOException { + super.afterHandshake(); + return this.underlyingSocket; + } + } + + private static class TestBug73053SocketWrapper extends Socket { + final Socket underlyingSocket; + + public TestBug73053SocketWrapper(Socket underlyingSocket) { + this.underlyingSocket = underlyingSocket; + try { + this.underlyingSocket.setSoTimeout(100); + } catch (SocketException e) { + fail("Failed preparing custom Socket"); + } + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + this.underlyingSocket.connect(endpoint); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + this.underlyingSocket.connect(endpoint, timeout); + } + + @Override + public void bind(SocketAddress bindpoint) throws IOException { + this.underlyingSocket.bind(bindpoint); + } + + @Override + public InetAddress getInetAddress() { + return this.underlyingSocket.getInetAddress(); + } + + @Override + public InetAddress getLocalAddress() { + return this.underlyingSocket.getLocalAddress(); + } + + @Override + public int getPort() { + return this.underlyingSocket.getPort(); + } + + @Override + public int getLocalPort() { + return this.underlyingSocket.getLocalPort(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return this.underlyingSocket.getRemoteSocketAddress(); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return this.underlyingSocket.getLocalSocketAddress(); + } + + @Override + public SocketChannel getChannel() { + return this.underlyingSocket.getChannel(); + } + + @Override + public InputStream getInputStream() throws IOException { + return new ConnectionRegressionTest.TestBug73053InputStreamWrapper(this.underlyingSocket.getInputStream()); + } + + @Override + public OutputStream getOutputStream() throws IOException { + return this.underlyingSocket.getOutputStream(); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + this.underlyingSocket.setTcpNoDelay(on); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return this.underlyingSocket.getTcpNoDelay(); + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + this.underlyingSocket.setSoLinger(on, linger); + } + + @Override + public int getSoLinger() throws SocketException { + return this.underlyingSocket.getSoLinger(); + } + + @Override + public void sendUrgentData(int data) throws IOException { + this.underlyingSocket.sendUrgentData(data); + } + + @Override + public void setOOBInline(boolean on) throws SocketException { + this.underlyingSocket.setOOBInline(on); + } + + @Override + public boolean getOOBInline() throws SocketException { + return this.underlyingSocket.getOOBInline(); + } + + @Override + public synchronized void setSoTimeout(int timeout) throws SocketException { + this.underlyingSocket.setSoTimeout(timeout); + } + + @Override + public synchronized int getSoTimeout() throws SocketException { + return this.underlyingSocket.getSoTimeout(); + } + + @Override + public synchronized void setSendBufferSize(int size) throws SocketException { + this.underlyingSocket.setSendBufferSize(size); + } + + @Override + public synchronized int getSendBufferSize() throws SocketException { + return this.underlyingSocket.getSendBufferSize(); + } + + @Override + public synchronized void setReceiveBufferSize(int size) throws SocketException { + this.underlyingSocket.setReceiveBufferSize(size); + } + + @Override + public synchronized int getReceiveBufferSize() throws SocketException { + return this.underlyingSocket.getReceiveBufferSize(); + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + this.underlyingSocket.setKeepAlive(on); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return this.underlyingSocket.getKeepAlive(); + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + this.underlyingSocket.setTrafficClass(tc); + } + + @Override + public int getTrafficClass() throws SocketException { + return this.underlyingSocket.getTrafficClass(); + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + this.underlyingSocket.setReuseAddress(on); + } + + @Override + public boolean getReuseAddress() throws SocketException { + return this.underlyingSocket.getReuseAddress(); + } + + @Override + public synchronized void close() throws IOException { + this.underlyingSocket.close(); + } + + @Override + public void shutdownInput() throws IOException { + this.underlyingSocket.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + this.underlyingSocket.shutdownOutput(); + } + + @Override + public String toString() { + return this.underlyingSocket.toString(); + } + + @Override + public boolean isConnected() { + return this.underlyingSocket.isConnected(); + } + + @Override + public boolean isBound() { + return this.underlyingSocket.isBound(); + } + + @Override + public boolean isClosed() { + return this.underlyingSocket.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return this.underlyingSocket.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return this.underlyingSocket.isOutputShutdown(); + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + this.underlyingSocket.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + @Override + public int hashCode() { + return this.underlyingSocket.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this.underlyingSocket.equals(obj); + } + } + + private static class TestBug73053InputStreamWrapper extends InputStream { + final InputStream underlyingInputStream; + int loopCount = 0; + + public TestBug73053InputStreamWrapper(InputStream underlyingInputStream) { + this.underlyingInputStream = underlyingInputStream; + } + + @Override + public int read() throws IOException { + this.loopCount = 0; + return this.underlyingInputStream.read(); + } + + @Override + public int read(byte[] b) throws IOException { + this.loopCount = 0; + return this.underlyingInputStream.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + try { + int readCount = this.underlyingInputStream.read(b, off, len); + this.loopCount = 0; + return readCount; + } catch (SocketTimeoutException e) { + this.loopCount++; + if (this.loopCount > 10) { + fail("Probable infinite loop at MySQLIO.clearInputStream()."); + } + return -1; + } + } + + @Override + public long skip(long n) throws IOException { + return this.underlyingInputStream.skip(n); + } + + @Override + public int available() throws IOException { + // In some older Linux kernels the underlying system call may return 1 when actually no bytes are available in a CLOSE_WAIT state socket, even if EOF + // has been reached. + int available = this.underlyingInputStream.available(); + return available == 0 ? 1 : available; + } + + @Override + public void close() throws IOException { + this.underlyingInputStream.close(); + } + + @Override + public synchronized void mark(int readlimit) { + this.underlyingInputStream.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + this.underlyingInputStream.reset(); + } + + @Override + public boolean markSupported() { + return this.underlyingInputStream.markSupported(); + } + + @Override + public int hashCode() { + return this.underlyingInputStream.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this.underlyingInputStream.equals(obj); + } + + @Override + public String toString() { + return this.underlyingInputStream.toString(); + } + } + + /** + * Tests fix for BUG#19354014 - CHANGEUSER() CALL RESULTS IN "PACKETS OUT OF ORDER" ERROR + * + * @throws Exception + */ + public void testBug19354014() throws Exception { + if (versionMeetsMinimum(5, 5, 7)) { + Connection con = null; + createUser("'bug19354014user'@'%'", "identified WITH mysql_native_password"); + this.stmt.executeUpdate("grant all on *.* to 'bug19354014user'@'%'"); + this.stmt.executeUpdate(versionMeetsMinimum(5, 7, 6) ? "ALTER USER 'bug19354014user'@'%' IDENTIFIED BY 'pwd'" + : "set password for 'bug19354014user'@'%' = PASSWORD('pwd')"); + this.stmt.executeUpdate("flush privileges"); + + try { + Properties props = new Properties(); + props.setProperty("useCompression", "true"); + + con = getConnectionWithProps(props); + ((MySQLConnection) con).changeUser("bug19354014user", "pwd"); + } finally { + this.stmt.executeUpdate("flush privileges"); + + if (con != null) { + con.close(); + } + } + } + } + + /** + * Tests fix for Bug#75168 - loadBalanceExceptionChecker interface cannot work using JDBC4/JDK7 + * + * Bug observed only with JDBC4 classes. This test is a duplication of testsuite.regression.jdbc4.ConnectionRegressionTest#testBug75168(). + * The two nested static classes, Bug75168LoadBalanceExceptionChecker and Bug75168StatementInterceptor are shared between the two tests. + * + * @throws Exception + */ + public void testBug75168() throws Exception { + final Properties props = new Properties(); + props.setProperty("loadBalanceExceptionChecker", "testsuite.regression.ConnectionRegressionTest$Bug75168LoadBalanceExceptionChecker"); + props.setProperty("statementInterceptors", Bug75168StatementInterceptor.class.getName()); + + Connection connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers + for (int i = 0; i < 3; i++) { + Statement stmtTest = null; + try { + stmtTest = connTest.createStatement(); + stmtTest.execute("SELECT * FROM nonexistent_table"); + fail("'Table doesn't exist' exception was expected."); + } catch (SQLException e) { + assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); + } finally { + if (stmtTest != null) { + stmtTest.close(); + } + } + } + connTest.close(); + + boolean stop = false; + do { + connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers + for (int i = 0; i < 3; i++) { + PreparedStatement pstmtTest = null; + try { + pstmtTest = connTest.prepareStatement("SELECT * FROM nonexistent_table"); + pstmtTest.execute(); + fail("'Table doesn't exist' exception was expected."); + } catch (SQLException e) { + assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); + } finally { + if (pstmtTest != null) { + pstmtTest.close(); + } + } + } + connTest.close(); + + // do it again with server prepared statements + props.setProperty("useServerPrepStmts", "true"); + } while (stop = !stop); + } + + public static class Bug75168LoadBalanceExceptionChecker implements LoadBalanceExceptionChecker { + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + } + + public void destroy() { + } + + public boolean shouldExceptionTriggerFailover(SQLException ex) { + return ex.getMessage().endsWith("nonexistent_table' doesn't exist"); + } + } + + public static class Bug75168StatementInterceptor extends BaseStatementInterceptor { + static Connection previousConnection = null; + + @Override + public void destroy() { + if (previousConnection == null) { + fail("Test testBug75168 didn't run as expected."); + } + } + + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql == null) { + sql = ""; + } + if (sql.length() == 0 && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + sql = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).asSql(); + } + if (sql.indexOf("nonexistent_table") >= 0) { + assertTrue("Different connection expected.", !connection.equals(previousConnection)); + previousConnection = connection; + } + return null; + } + } + + /** + * Tests fix for BUG#71084 - Wrong java.sql.Date stored if client and server time zones differ + * + * This tests the behavior of the new connection property 'noTimezoneConversionForDateType' + * + * @throws Exception + * if the test fails. + */ + public void testBug71084() throws Exception { + createTable("testBug71084", "(id INT, dt DATE)"); + + Properties connProps = new Properties(); + connProps.setProperty("cacheDefaultTimezone", "false"); + + /* + * case 0: default settings (no conversions) + */ + testBug71084AssertCase(connProps, "GMT+2", "GMT+6", null, "1998-05-21", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-6", "GMT+2", null, "1998-05-21", "1998-05-21", "1998-05-21 0:00:00"); + + /* + * case 1: connection property 'useLegacyDatetimeCode=false' + */ + connProps.setProperty("useLegacyDatetimeCode", "false"); + + // client 25 hours behind server + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); + // client 25 hours behind server, 24 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); + + // client 24 hours behind server + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); + // client 24 hours behind server, 25 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); + + // client 2 hours behind server + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); + // client 2 hours behind server, 2 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); + + // client and server in the same time zone + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client, server and target calendar in the same time zone + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + + // client 2 hours ahead of server + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); + // client 2 hours ahead of server, 2 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); + + // client 24 hours ahead of server + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); + // client 24 hours ahead of server, 25 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); + + // client 25 hours ahead of server + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); + // client 25 hours ahead of server, 24 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); + connProps.remove("useLegacyDatetimeCode"); + + /* + * case 2: connection property 'useTimezone=true' + */ + connProps.setProperty("useTimezone", "true"); + + // client 25 hours behind server + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 25 hours behind server, 24 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-20 23:59:59", "1998-05-21", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 0:00:00", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-21 23:59:59", "1998-05-22", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-13", "GMT+12", "GMT+11", "1998-05-22 0:00:00", "1998-05-23", "1998-05-22 0:00:00"); + + // client 24 hours behind server + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 24 hours behind server, 25 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 22:59:59", "1998-05-22", "1998-05-20 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-21 23:00:00", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 22:59:59", "1998-05-23", "1998-05-21 23:00:00"); + testBug71084AssertCase(connProps, "GMT-10", "GMT+14", "GMT+15", "1998-05-22 23:00:00", "1998-05-24", "1998-05-22 23:00:00"); + + // client 2 hours behind server + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 2 hours behind server, 2 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 1:59:59", "1998-05-20", "1998-05-20 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-21 2:00:00", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 1:59:59", "1998-05-21", "1998-05-21 2:00:00"); + testBug71084AssertCase(connProps, "GMT+8", "GMT+10", "GMT+6", "1998-05-22 2:00:00", "1998-05-22", "1998-05-22 2:00:00"); + + // client and server in the same time zone + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client, server and target calendar in the same time zone + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+7", "GMT+7", "GMT+7", "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + + // client 2 hours ahead of server + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 2 hours ahead of server, 2 hours behind target calendar + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 21:59:59", "1998-05-21", "1998-05-20 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-21 22:00:00", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 21:59:59", "1998-05-22", "1998-05-21 22:00:00"); + testBug71084AssertCase(connProps, "GMT-9", "GMT-11", "GMT-7", "1998-05-22 22:00:00", "1998-05-23", "1998-05-22 22:00:00"); + + // client 24 hours ahead of server + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 24 hours ahead of server, 25 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 0:59:59", "1998-05-19", "1998-05-20 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-21 1:00:00", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 0:59:59", "1998-05-20", "1998-05-21 1:00:00"); + testBug71084AssertCase(connProps, "GMT+12", "GMT-12", "GMT-13", "1998-05-22 1:00:00", "1998-05-21", "1998-05-22 1:00:00"); + + // client 25 hours ahead of server + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-20 23:59:59", "1998-05-20", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 0:00:00", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-21 23:59:59", "1998-05-21", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", null, "1998-05-22 0:00:00", "1998-05-22", "1998-05-22 0:00:00"); + // client 25 hours ahead of server, 24 hours ahead of target calendar + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-20 23:59:59", "1998-05-19", "1998-05-20 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 0:00:00", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-21 23:59:59", "1998-05-20", "1998-05-21 0:00:00"); + testBug71084AssertCase(connProps, "GMT+13", "GMT-12", "GMT-11", "1998-05-22 0:00:00", "1998-05-21", "1998-05-22 0:00:00"); + connProps.remove("useTimezone"); + } + + private void testBug71084AssertCase(Properties connProps, String clientTZ, String serverTZ, String targetTZ, String insertDate, String expectedStoredDate, + String expectedRetrievedDate) throws Exception { + final TimeZone defaultTZ = TimeZone.getDefault(); + final boolean useTargetCal = targetTZ != null; + final Properties testExtraProperties = new Properties(); + + testExtraProperties.setProperty("", ""); + testExtraProperties.setProperty("useFastDateParsing", "false"); + testExtraProperties.setProperty("useJDBCCompliantTimezoneShift", "true"); + testExtraProperties.setProperty("useSSPSCompatibleTimezoneShift", "true"); + + this.stmt.execute("DELETE FROM testBug71084"); + + try { + TimeZone.setDefault(TimeZone.getTimeZone(clientTZ)); + + SimpleDateFormat longDateFrmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + longDateFrmt.setTimeZone(TimeZone.getDefault()); + SimpleDateFormat shortDateFrmt = new SimpleDateFormat("yyyy-MM-dd"); + shortDateFrmt.setTimeZone(TimeZone.getDefault()); + + Calendar targetCal = null; + String targetCalMsg = null; + if (useTargetCal) { + targetCal = Calendar.getInstance(TimeZone.getTimeZone(targetTZ)); + targetCalMsg = " (Calendar methods)"; + } else { + targetCalMsg = " (non-Calendar methods)"; + } + + Date dateIn = insertDate.length() == 10 ? shortDateFrmt.parse(insertDate) : longDateFrmt.parse(insertDate); + String expectedDateInDB = expectedStoredDate; + Date expectedDateInRS = longDateFrmt.parse(expectedRetrievedDate); + String expectedDateInDBNoConv = shortDateFrmt.format(dateIn); + Date expectedDateInRSNoConv = shortDateFrmt.parse(expectedDateInDBNoConv); + + int id = 0; + for (Entry prop : testExtraProperties.entrySet()) { + id++; + + String key = (String) prop.getKey(); + String value = (String) prop.getValue(); + Properties connPropsLocal = new Properties(); + String propsList = "..."; + + connPropsLocal.putAll(connProps); + if (key.length() > 0) { + connPropsLocal.setProperty(key, value); + } + for (Object k : connPropsLocal.keySet()) { + if (!"cacheDefaultTimezone".equalsIgnoreCase((String) k)) { + propsList += "," + (String) k; + } + } + + connPropsLocal.setProperty("serverTimezone", serverTZ); + + /* + * Test using the property "noTimezoneConversionForDateType=false". Conversions should occur. + */ + connPropsLocal.setProperty("noTimezoneConversionForDateType", "false"); + Connection testConn = getConnectionWithProps(connPropsLocal); + + PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testBug71084 VALUES (?, ?)"); + testPstmt.setInt(1, id); + if (useTargetCal) { + testPstmt.setDate(2, new java.sql.Date(dateIn.getTime()), targetCal); + } else { + testPstmt.setDate(2, new java.sql.Date(dateIn.getTime())); + } + testPstmt.execute(); + testPstmt.close(); + + Statement testStmt = testConn.createStatement(); + // Get date value from database: Column `dt` - allowing time zone conversion by returning it as is; Column `dtStr` - preventing time zone + // conversion by returning it as String and invalidating the date format so that no automatic conversion can ever happen. + ResultSet restRs = testStmt.executeQuery("SELECT dt, CONCAT('$', dt) AS dtStr FROM testBug71084 WHERE id = " + id); + restRs.next(); + java.sql.Date dateOut = useTargetCal ? restRs.getDate(1, targetCal) : restRs.getDate(1); + String dateInDB = restRs.getString(2).substring(1); + restRs.close(); + testStmt.close(); + + testConn.close(); + + assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDB, dateInDB); + assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRS), longDateFrmt.format(dateOut)); + + /* + * Repeat the test using the property "noTimezoneConversionForDateType=true". No conversions should occur now. + */ + id++; + + propsList += ",noTimezoneConversionForDateType"; + + connPropsLocal.setProperty("noTimezoneConversionForDateType", "true"); + testConn = getConnectionWithProps(connPropsLocal); + + testPstmt = testConn.prepareStatement("INSERT INTO testBug71084 VALUES (?, ?)"); + testPstmt.setInt(1, id); + if (useTargetCal) { + testPstmt.setDate(2, new java.sql.Date(dateIn.getTime()), targetCal); + } else { + testPstmt.setDate(2, new java.sql.Date(dateIn.getTime())); + } + testPstmt.execute(); + testPstmt.close(); + + testStmt = testConn.createStatement(); + // Get date value from database: Column `dt` - allowing time zone conversion by returning it as is; Column `dtStr` - preventing time zone + // conversion by returning it as String and invalidating the date format so that no automatic conversion can ever happen. + restRs = testStmt.executeQuery("SELECT dt, CONCAT('$', dt) AS dtStr FROM testBug71084 WHERE id = " + id); + restRs.next(); + dateOut = useTargetCal ? restRs.getDate(1, targetCal) : restRs.getDate(1); + dateInDB = restRs.getString(2).substring(1); + restRs.close(); + testStmt.close(); + + testConn.close(); + + if (useTargetCal) { + assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDB, dateInDB); + assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRS), + longDateFrmt.format(dateOut)); + } else { + assertEquals(id + ". [" + propsList + "] Date stored" + targetCalMsg, expectedDateInDBNoConv, dateInDB); + assertEquals(id + ". [" + propsList + "] Date retrieved" + targetCalMsg, longDateFrmt.format(expectedDateInRSNoConv), + longDateFrmt.format(dateOut)); + } + } + } finally { + TimeZone.setDefault(defaultTZ); + } + } + + /** + * Tests fix for BUG#20685022 - SSL CONNECTION TO MYSQL 5.7.6 COMMUNITY SERVER FAILS + * + * This test is duplicated in testuite.regression.ConnectionRegressionTest.jdbc4.testBug20685022(). + * + * @throws Exception + * if the test fails. + */ + public void testBug20685022() throws Exception { + if (!isCommunityEdition()) { + return; + } + + final Properties props = new Properties(); + + /* + * case 1: non verifying server certificate + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "false"); + + getConnectionWithProps(props); + + /* + * case 2: verifying server certificate using key store provided by connection properties + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "true"); + props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store"); + props.setProperty("trustCertificateKeyStoreType", "JKS"); + props.setProperty("trustCertificateKeyStorePassword", "password"); + + getConnectionWithProps(props); + + /* + * case 3: verifying server certificate using key store provided by system properties + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "true"); + + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + + getConnectionWithProps(props); + } + + /** + * Tests fix for BUG#75592 - "SHOW VARIABLES WHERE" is expensive. + * + * @throws Exception + * if the test fails. + */ + public void testBug75592() throws Exception { + if (versionMeetsMinimum(5, 0, 3)) { + + MySQLConnection con = (MySQLConnection) getConnectionWithProps("statementInterceptors=" + Bug75592StatementInterceptor.class.getName()); + + // reference values + Map serverVariables = new HashMap(); + this.rs = con.createStatement().executeQuery("SHOW VARIABLES"); + while (this.rs.next()) { + serverVariables.put(this.rs.getString(1), this.rs.getString(2)); + } + + // check values from "select @@var..." + assertEquals(serverVariables.get("auto_increment_increment"), con.getServerVariable("auto_increment_increment")); + assertEquals(serverVariables.get("character_set_client"), con.getServerVariable("character_set_client")); + assertEquals(serverVariables.get("character_set_connection"), con.getServerVariable("character_set_connection")); + + // we override character_set_results sometimes when configuring client charsets, thus need to check against actual value + if (con.getServerVariable(ConnectionImpl.JDBC_LOCAL_CHARACTER_SET_RESULTS) == null) { + assertEquals("", serverVariables.get("character_set_results")); + } else { + assertEquals(serverVariables.get("character_set_results"), con.getServerVariable(ConnectionImpl.JDBC_LOCAL_CHARACTER_SET_RESULTS)); + } + + assertEquals(serverVariables.get("character_set_server"), con.getServerVariable("character_set_server")); + assertEquals(serverVariables.get("init_connect"), con.getServerVariable("init_connect")); + assertEquals(serverVariables.get("interactive_timeout"), con.getServerVariable("interactive_timeout")); + assertEquals(serverVariables.get("license"), con.getServerVariable("license")); + assertEquals(serverVariables.get("lower_case_table_names"), con.getServerVariable("lower_case_table_names")); + assertEquals(serverVariables.get("max_allowed_packet"), con.getServerVariable("max_allowed_packet")); + assertEquals(serverVariables.get("net_buffer_length"), con.getServerVariable("net_buffer_length")); + assertEquals(serverVariables.get("net_write_timeout"), con.getServerVariable("net_write_timeout")); + assertEquals(serverVariables.get("query_cache_size"), con.getServerVariable("query_cache_size")); + assertEquals(serverVariables.get("query_cache_type"), con.getServerVariable("query_cache_type")); + + // not necessarily contains STRICT_TRANS_TABLES + for (String sm : serverVariables.get("sql_mode").split(",")) { + if (!sm.equals("STRICT_TRANS_TABLES")) { + assertTrue(con.getServerVariable("sql_mode").contains(sm)); + } + } + + assertEquals(serverVariables.get("system_time_zone"), con.getServerVariable("system_time_zone")); + assertEquals(serverVariables.get("time_zone"), con.getServerVariable("time_zone")); + assertEquals(serverVariables.get("tx_isolation"), con.getServerVariable("tx_isolation")); + assertEquals(serverVariables.get("wait_timeout"), con.getServerVariable("wait_timeout")); + if (!versionMeetsMinimum(5, 5, 0)) { + assertEquals(serverVariables.get("language"), con.getServerVariable("language")); + } + } + } + + /** + * Statement interceptor for preceding testBug75592(). + */ + public static class Bug75592StatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql.contains("SHOW VARIABLES WHERE")) { + throw new SQLException("'SHOW VARIABLES WHERE' statement issued: " + sql); + } + return null; + } + } + + /** + * Tests fix for BUG#20825727 - CONNECT FAILURE WHEN TRY TO CONNECT SHA USER WITH DIFFERENT CHARSET. + * + * This test runs through all authentication plugins when one of the following server requirements is met: + * 1. Default connection string points to a server configured with both SSL *and* RSA encryption. + * or + * 2. Default connection string points to a server configured with SSL enabled but no RSA encryption *and* the property + * com.mysql.jdbc.testsuite.url.sha256default points to an additional server configured with + * default-authentication-plugin=sha256_password and RSA encryption. + * + * If none of the servers has SSL and RSA encryption enabled then only 'mysql_native_password' and 'mysql_old_password' plugins are tested. + * + * @throws Exception + * if the test fails. + */ + public void testBug20825727() throws Exception { + if (!versionMeetsMinimum(5, 5, 7)) { + return; + } + + final String[] testDbUrls; + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + + if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 5, 7)) { + testDbUrls = new String[] { BaseTestCase.dbUrl, sha256Url }; + } else { + testDbUrls = new String[] { BaseTestCase.dbUrl }; + } + + for (String testDbUrl : testDbUrls) { + com.mysql.jdbc.Connection testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); + Statement testStmt = testConn.createStatement(); + + this.rs = testStmt.executeQuery("SELECT @@GLOBAL.HAVE_SSL = 'YES' AS have_ssl"); + final boolean sslEnabled = this.rs.next() && this.rs.getBoolean(1); + + this.rs = testStmt.executeQuery("SHOW STATUS LIKE '%Rsa_public_key%'"); + final boolean rsaEnabled = this.rs.next() && this.rs.getString(1).length() > 0; + + System.out.println(); + System.out.println("* Testing URL: " + testDbUrl + " [SSL enabled: " + sslEnabled + "] [RSA enabled: " + rsaEnabled + "]"); + System.out.println("******************************************************************************************************************************" + + "*************"); + System.out.printf("%-25s : %-25s : %s : %-25s : %-18s : %-18s [%s]%n", "Connection Type", "Auth. Plugin", "pwd ", "Encoding Prop.", + "Encoding Value", "Server Encoding", "TstRes"); + System.out.println("------------------------------------------------------------------------------------------------------------------------------" + + "-------------"); + + boolean clearTextPluginInstalled = false; + boolean secureAuthChanged = false; + try { + String[] plugins; + + // install cleartext plugin if required + this.rs = testStmt.executeQuery( + "SELECT (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') FROM INFORMATION_SCHEMA.PLUGINS" + " WHERE PLUGIN_NAME='cleartext_plugin_server'"); + if (!this.rs.next() || !this.rs.getBoolean(1)) { + String ext = System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") > -1 ? ".dll" : ".so"; + testStmt.execute("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin" + ext + "'"); + clearTextPluginInstalled = true; + } + + if (testConn.versionMeetsMinimum(5, 7, 5)) { + // mysql_old_password plugin not supported + plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "sha256_password,2" }; + } else if (testConn.versionMeetsMinimum(5, 6, 6)) { + plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "mysql_old_password,1", "sha256_password,2" }; + + // temporarily disable --secure-auth mode to allow old format passwords + testStmt.executeUpdate("SET @current_secure_auth = @@global.secure_auth"); + testStmt.executeUpdate("SET @@global.secure_auth = off"); + secureAuthChanged = true; + } else { + // sha256_password plugin not supported + plugins = new String[] { "cleartext_plugin_server,-1", "mysql_native_password,0", "mysql_old_password,1" }; + } + + final String simplePwd = "my\tpass word"; + final String complexPwd = "my\tp\u00e4ss w\u263ard"; + + for (String encoding : new String[] { "", "UTF-8", "ISO-8859-1", "US-ASCII" }) { + for (String plugin : plugins) { + + String pluginName = plugin.split(",")[0]; + int pwdHashingMethod = Integer.parseInt(plugin.split(",")[1]); + + String testStep = ""; + try { + testStep = "create user"; + testBug20825727CreateUser(testDbUrl, "testBug20825727", simplePwd, encoding, pluginName, pwdHashingMethod); + testStep = "login with simple password"; + testBug20825727TestLogin(testDbUrl, testConn.getEncoding(), sslEnabled, rsaEnabled, "testBug20825727", simplePwd, encoding, + pluginName); + + testStep = "change password"; + testBug20825727ChangePassword(testDbUrl, "testBug20825727", complexPwd, encoding, pluginName, pwdHashingMethod); + testStep = "login with complex password"; + testBug20825727TestLogin(testDbUrl, testConn.getEncoding(), sslEnabled, rsaEnabled, "testBug20825727", complexPwd, encoding, + pluginName); + } catch (SQLException e) { + e.printStackTrace(); + fail("Failed at '" + testStep + "' using encoding '" + encoding + "' and plugin '" + pluginName + + "'. See also system output for more details."); + } finally { + try { + dropUser(testStmt, "'testBug20825727'@'%'"); + } catch (Exception e) { + } + } + } + } + } finally { + if (clearTextPluginInstalled) { + testStmt.executeUpdate("UNINSTALL PLUGIN cleartext_plugin_server"); + } + if (secureAuthChanged) { + testStmt.executeUpdate("SET @@global.secure_auth = @current_secure_auth"); + } + + testStmt.close(); + testConn.close(); + } + } + } + + private void testBug20825727CreateUser(String testDbUrl, String user, String password, String encoding, String pluginName, int pwdHashingMethod) + throws SQLException { + com.mysql.jdbc.Connection testConn = null; + try { + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + if (encoding.length() > 0) { + props.setProperty("characterEncoding", encoding); + } + testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); + Statement testStmt = testConn.createStatement(); + + if (testConn.versionMeetsMinimum(5, 7, 6)) { + testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " BY '" + password + "'"); + } else if (pwdHashingMethod >= 0) { + // for mysql_native_password, mysql_old_password and sha256_password plugins + testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName); + testStmt.execute("SET @@session.old_passwords = " + pwdHashingMethod); + testStmt.execute("SET PASSWORD FOR '" + user + "'@'%' = PASSWORD('" + password + "')"); + testStmt.execute("SET @@session.old_passwords = @@global.old_passwords"); + } else { + // for cleartext_plugin_server plugin + testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " AS '" + password + "'"); + } + testStmt.execute("GRANT ALL ON *.* TO '" + user + "'@'%'"); + testStmt.close(); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + + private void testBug20825727ChangePassword(String testDbUrl, String user, String password, String encoding, String pluginName, int pwdHashingMethod) + throws SQLException { + com.mysql.jdbc.Connection testConn = null; + try { + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + if (encoding.length() > 0) { + props.setProperty("characterEncoding", encoding); + } + testConn = (com.mysql.jdbc.Connection) getConnectionWithProps(testDbUrl, props); + Statement testStmt = testConn.createStatement(); + + if (testConn.versionMeetsMinimum(5, 7, 6)) { + testStmt.execute("ALTER USER '" + user + "'@'%' IDENTIFIED BY '" + password + "'"); + } else if (pwdHashingMethod >= 0) { + // for mysql_native_password, mysql_old_password and sha256_password plugins + testStmt.execute("SET @@session.old_passwords = " + pwdHashingMethod); + testStmt.execute("SET PASSWORD FOR '" + user + "'@'%' = PASSWORD('" + password + "')"); + testStmt.execute("SET @@session.old_passwords = @@global.old_passwords"); + } else { + // for cleartext_plugin_server plugin + dropUser(testStmt, "'" + user + "'@'%'"); + testStmt.execute("CREATE USER '" + user + "'@'%' IDENTIFIED WITH " + pluginName + " AS '" + password + "'"); + testStmt.execute("GRANT ALL ON *.* TO '" + user + "'@'%'"); + } + testStmt.close(); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + + private void testBug20825727TestLogin(final String testDbUrl, String defaultServerEncoding, boolean sslEnabled, boolean rsaEnabled, String user, + String password, String encoding, String pluginName) throws SQLException { + final Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + final com.mysql.jdbc.MySQLConnection testBaseConn = (com.mysql.jdbc.MySQLConnection) getConnectionWithProps(testDbUrl, props); + final boolean pwdIsComplex = !Charset.forName("US-ASCII").newEncoder().canEncode(password); + + for (String encProp : encoding.length() == 0 ? new String[] { "*none*" } : new String[] { "characterEncoding", "passwordCharacterEncoding" }) { + for (int testCase = 1; testCase <= 4; testCase++) { + props.setProperty("user", user); + props.setProperty("password", password); + if (encoding.length() > 0) { + props.setProperty(encProp, encoding); + } + + String testCaseMsg = "*none*"; + switch (testCase) { + case 1: + /* + * Test with an SSL disabled connection. + * Can't be used with plugins 'cleartext_plugin_server' and 'sha256_password'. + */ + if (pluginName.equals("cleartext_plugin_server") || pluginName.equals("sha256_password")) { + continue; + } + props.setProperty("allowPublicKeyRetrieval", "true"); + props.setProperty("useSSL", "false"); + props.setProperty("requireSSL", "false"); + testCaseMsg = "Non-SSL/Non-RSA"; + break; + + case 2: + /* + * Test with an SSL enabled connection. + */ + if (!sslEnabled) { + continue; + } + props.setProperty("allowPublicKeyRetrieval", "false"); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "false"); + testCaseMsg = "SSL"; + break; + + case 3: + /* + * Test with an RSA encryption enabled connection, using public key retrieved from server. + * Requires additional server instance pointed by 'com.mysql.jdbc.testsuite.url.sha256default'. + * Can't be used with plugin 'cleartext_plugin_server'. + */ + if (pluginName.equals("cleartext_plugin_server") || !rsaEnabled) { + continue; + } + props.setProperty("allowPublicKeyRetrieval", "true"); + testCaseMsg = "RSA [pubkey-retrieval]"; + break; + + case 4: + /* + * Test with an RSA encryption enabled connection, using public key pointed by the property 'serverRSAPublicKeyFile'. + * Requires additional server instance pointed by 'com.mysql.jdbc.testsuite.url.sha256default'. + * Can't be used with plugin 'cleartext_plugin_server'. + */ + if (pluginName.equals("cleartext_plugin_server") || !rsaEnabled) { + continue; + } + props.setProperty("allowPublicKeyRetrieval", "false"); + props.setProperty("serverRSAPublicKeyFile", "src/testsuite/ssl-test-certs/mykey.pub"); + testCaseMsg = "RSA [pubkey-file]"; + break; + } + + boolean testShouldPass = true; + if (pwdIsComplex) { + // if no encoding is specifically defined then our default password encoding ('UTF-8') and server's encoding must coincide + testShouldPass = encoding.length() > 0 || defaultServerEncoding.equalsIgnoreCase("UTF-8"); + + if (!testBaseConn.versionMeetsMinimum(5, 7, 6) && pluginName.equals("cleartext_plugin_server")) { + // 'cleartext_plugin_server' from servers below version 5.7.6 requires UTF-8 encoding + testShouldPass = encoding.equals("UTF-8") || (encoding.length() == 0 && defaultServerEncoding.equals("UTF-8")); + } + } + + System.out.printf("%-25s : %-25s : %s : %-25s : %-18s : %-18s [%s]%n", testCaseMsg, pluginName, pwdIsComplex ? "cplx" : "smpl", encProp, + encoding.length() == 0 ? "-" : encoding, defaultServerEncoding, testShouldPass); + + Connection testConn = null; + try { + if (testShouldPass) { + testConn = getConnectionWithProps(testDbUrl, props); + Statement testStmt = testConn.createStatement(); + + this.rs = testStmt.executeQuery("SELECT USER(), CURRENT_USER()"); + assertTrue(this.rs.next()); + if (!this.rs.getString(1).startsWith(user) || !this.rs.getString(2).startsWith(user)) { + fail("Unexpected failure in test case '" + testCaseMsg + "' using encoding '" + encoding + "' in property '" + encProp + "'."); + } + this.rs.close(); + testStmt.close(); + } else { + assertThrows(SQLException.class, "Access denied for user 'testBug20825727'@.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(testDbUrl, props); + return null; + } + }); + } + } finally { + if (testConn != null) { + try { + testConn.close(); + } catch (SQLException e) { + } + } + } + } + } + + testBaseConn.close(); + } + + /** + * Tests fix for BUG#75670 - Connection fails with "Public Key Retrieval is not allowed" for native auth. + * + * Requires additional server instance pointed by com.mysql.jdbc.testsuite.url.sha256default variable configured with + * default-authentication-plugin=sha256_password and RSA encryption enabled. + * + * @throws Exception + * if the test fails. + */ + public void testBug75670() throws Exception { + if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 6, 6)) { + + if (!pluginIsActive(this.sha256Stmt, "sha256_password")) { + fail("sha256_password required to run this test"); + } + + try { + this.sha256Stmt.executeUpdate("SET @current_old_passwords = @@global.old_passwords"); + + createUser(this.sha256Stmt, "'bug75670user'@'%'", ""); // let --default-authentication-plugin option force sha256_password + this.rs = this.sha256Stmt.executeQuery("SELECT plugin FROM mysql.user WHERE user='bug75670user'"); + assertTrue(this.rs.next()); + assertEquals("Wrong default authentication plugin (check test conditions):", "sha256_password", this.rs.getString(1)); + + if (((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 7, 6)) { + createUser(this.sha256Stmt, "'bug75670user_mnp'@'%'", "IDENTIFIED WITH mysql_native_password BY 'bug75670user_mnp'"); + createUser(this.sha256Stmt, "'bug75670user_sha'@'%'", "IDENTIFIED WITH sha256_password BY 'bug75670user_sha'"); + } else { + this.sha256Stmt.execute("SET @@session.old_passwords = 0"); + createUser(this.sha256Stmt, "'bug75670user_mnp'@'%'", "IDENTIFIED WITH mysql_native_password"); + this.sha256Stmt.execute("SET PASSWORD FOR 'bug75670user_mnp'@'%' = PASSWORD('bug75670user_mnp')"); + this.sha256Stmt.execute("SET @@session.old_passwords = 2"); + createUser(this.sha256Stmt, "'bug75670user_sha'@'%'", "IDENTIFIED WITH sha256_password"); + this.sha256Stmt.execute("SET PASSWORD FOR 'bug75670user_sha'@'%' = PASSWORD('bug75670user_sha')"); + } + this.sha256Stmt.execute("GRANT ALL ON *.* TO 'bug75670user_mnp'@'%'"); + this.sha256Stmt.execute("GRANT ALL ON *.* TO 'bug75670user_sha'@'%'"); + + System.out.println(); + System.out.printf("%-25s : %-18s : %-25s : %-25s : %s%n", "DefAuthPlugin", "AllowPubKeyRet", "User", "Passwd", "Test result"); + System.out.println("----------------------------------------------------------------------------------------------------" + + "------------------------------"); + + for (Class defAuthPlugin : new Class[] { MysqlNativePasswordPlugin.class, Sha256PasswordPlugin.class }) { + for (String user : new String[] { "bug75670user_mnp", "bug75670user_sha" }) { + for (String pwd : new String[] { user, "wrong*pwd", "" }) { + for (boolean allowPubKeyRetrieval : new boolean[] { true, false }) { + final Connection testConn; + Statement testStmt; + + boolean expectedPubKeyRetrievalFail = (user.endsWith("_sha") + || user.endsWith("_mnp") && defAuthPlugin.equals(Sha256PasswordPlugin.class)) && !allowPubKeyRetrieval + && pwd.length() > 0; + boolean expectedAccessDeniedFail = !user.equals(pwd); + System.out.printf("%-25s : %-18s : %-25s : %-25s : %s%n", defAuthPlugin.getSimpleName(), allowPubKeyRetrieval, user, pwd, + expectedPubKeyRetrievalFail ? "Fail [Pub. Key retrieval]" : expectedAccessDeniedFail ? "Fail [Access denied]" : "Ok"); + + final Properties props = new Properties(); + props.setProperty("user", user); + props.setProperty("password", pwd); + props.setProperty("defaultAuthenticationPlugin", defAuthPlugin.getName()); + props.setProperty("allowPublicKeyRetrieval", Boolean.toString(allowPubKeyRetrieval)); + props.setProperty("useSSL", "false"); + + if (expectedPubKeyRetrievalFail) { + // connection will fail due to public key retrieval failure + assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, props); + return null; + } + }); + + } else if (expectedAccessDeniedFail) { + // connection will fail due to wrong password + assertThrows(SQLException.class, "Access denied for user '" + user + "'@.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(sha256Url, props); + return null; + } + }); + + } else { + // connection will succeed + testConn = getConnectionWithProps(sha256Url, props); + testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT USER(), CURRENT_USER()"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(1).startsWith(user)); + assertTrue(this.rs.getString(2).startsWith(user)); + this.rs.close(); + testStmt.close(); + + // change user using same credentials will succeed + System.out.printf("%25s : %-18s : %-25s : %-25s : %s%n", "| ChangeUser (same)", allowPubKeyRetrieval, user, pwd, "Ok"); + ((com.mysql.jdbc.Connection) testConn).changeUser(user, user); + testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT USER(), CURRENT_USER()"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(1).startsWith(user)); + assertTrue(this.rs.getString(2).startsWith(user)); + this.rs.close(); + testStmt.close(); + + // change user using different credentials + final String swapUser = user.indexOf("_sha") == -1 ? "bug75670user_sha" : "bug75670user_mnp"; + expectedPubKeyRetrievalFail = (swapUser.endsWith("_sha") + || swapUser.endsWith("_mnp") && defAuthPlugin.equals(Sha256PasswordPlugin.class)) && !allowPubKeyRetrieval; + System.out.printf("%25s : %-18s : %-25s : %-25s : %s%n", "| ChangeUser (diff)", allowPubKeyRetrieval, swapUser, swapUser, + expectedPubKeyRetrievalFail ? "Fail [Pub. Key retrieval]" : "Ok"); + + if (expectedPubKeyRetrievalFail) { + // change user will fail due to public key retrieval failure + assertThrows(SQLException.class, "Public Key Retrieval is not allowed", new Callable() { + public Void call() throws Exception { + ((com.mysql.jdbc.Connection) testConn).changeUser(swapUser, swapUser); + return null; + } + }); + } else { + // change user will succeed + ((com.mysql.jdbc.Connection) testConn).changeUser(swapUser, swapUser); + testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT USER(), CURRENT_USER()"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(1).startsWith(swapUser)); + assertTrue(this.rs.getString(2).startsWith(swapUser)); + this.rs.close(); + } + + testConn.close(); + } + } + } + } + } + } finally { + this.sha256Stmt.executeUpdate("SET GLOBAL old_passwords = @current_old_passwords"); + } + } + } + + /** + * Tests fix for Bug#16634180 - LOCK WAIT TIMEOUT EXCEEDED CAUSES SQLEXCEPTION, SHOULD CAUSE SQLTRANSIENTEXCEPTION + * + * @throws Exception + * if the test fails. + */ + public void testBug16634180() throws Exception { + + if (Util.isJdbc4()) { + // relevant JDBC4+ test is testsuite.regression.jdbc4.ConnectionRegressionTest.testBug16634180() + return; + } + + createTable("testBug16634180", "(pk integer primary key, val integer)", "InnoDB"); + this.stmt.executeUpdate("insert into testBug16634180 values(0,0)"); + + Connection c1 = null; + Connection c2 = null; + + try { + c1 = getConnectionWithProps(new Properties()); + c1.setAutoCommit(false); + Statement s1 = c1.createStatement(); + s1.executeUpdate("update testBug16634180 set val=val+1 where pk=0"); + + c2 = getConnectionWithProps(new Properties()); + c2.setAutoCommit(false); + Statement s2 = c2.createStatement(); + try { + s2.executeUpdate("update testBug16634180 set val=val+1 where pk=0"); + fail("ER_LOCK_WAIT_TIMEOUT should be thrown."); + } catch (MySQLTransientException ex) { + assertEquals(MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT, ex.getErrorCode()); + assertEquals(SQLError.SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE, ex.getSQLState()); + assertEquals("Lock wait timeout exceeded; try restarting transaction", ex.getMessage()); + } + } finally { + if (c1 != null) { + c1.close(); + } + if (c2 != null) { + c2.close(); + } + } + } + + /** + * Tests fix for Bug#21934573 - FABRIC CODE INVOLVED IN THREAD DEADLOCK. + * (Duplicate Bug#78710 (21966391) - Deadlock on ReplicationConnection and ReplicationConnectionGroup when failover) + * + * Two threads with different Fabric connections using the same server group (and consequently the same {@link ReplicationConnectionGroup}) may hit a + * deadlock when one executes a failover procedure and the other, simultaneously, calls a method that acquires a lock on the {@link ReplicationConnection} + * instance monitor. + * + * This happens when, in one thread, a Fabric connection (performing the failover) and while owning a lock on {@link ReplicationConnectionGroup}, + * sequentially tries to lock the object monitor from each {@link ReplicationConnection} belonging to the same {@link ReplicationConnectionGroup}, in the + * attempt of updating their servers lists by calling the synchronized methods {@link ReplicationConnection#removeMasterHost(String)}, + * {@link ReplicationConnection#addSlaveHost(String)}, {@link ReplicationConnection#removeSlaveHost(String)} or + * {@link ReplicationConnection#promoteSlaveToMaster(String)} while, at the same time, a second thread is executing one of the synchronized methods from the + * {@link ReplicationConnection} instance, such as {@link ReplicationConnection#close()} or {@link ReplicationConnection#doPing()} (*), in one of those + * connections. Later on, the second thread, eventually initiates a failover procedure too and hits the lock on {@link ReplicationConnectionGroup} owned by + * the first thread. The first thread, at the same time, requires that the lock on {@link ReplicationConnection} is released by the second thread to be able + * to complete the failover procedure is has initiated before. + * (*) Executing a query may trigger this too via locking on {@link LoadBalancedConnectionProxy}. + * + * This test simulates the way Fabric connections operate when they need to synchronize the list of servers from a {@link ReplicationConnection} with the + * Fabric's server group. In that operation we, like Fabric connections, use an {@link ExceptionInterceptor} that ends up changing the + * {@link ReplicationConnection}s from a given {@link ReplicationConnectionGroup}. + * + * This test is unable to cover the failing scenario since the fix in the main code was also reproduced here, with the addition of the {@link ReentrantLock} + * {@code singleSynchWorkerMonitor} in the {@link TestBug21934573ExceptionInterceptor} the same way as in {@link ErrorReportingExceptionInterceptor}. The + * way to reproduce it and observe the deadlock happening is by setting the connection property {@code __useReplConnGroupLocks__} to {@code False}. + * + * WARNING! If this test fails there is no guarantee that the JVM will remain stable and won't affect any other tests. It is imperative that this test + * passes to ensure other tests results. + */ + public void testBug21934573() throws Exception { + Properties props = new Properties(); + props.setProperty("exceptionInterceptors", TestBug21934573ExceptionInterceptor.class.getName()); + props.setProperty("replicationConnectionGroup", "deadlock"); + props.setProperty("allowMultiQueries", "true"); + props.setProperty("__useReplConnGroupLocks__", "true"); // Set this to 'false' to observe the deadlock. + + final Connection connA = getMasterSlaveReplicationConnection(props); + final Connection connB = getMasterSlaveReplicationConnection(props); + + for (final Connection testConn : new Connection[] { connA, connB }) { + new Thread(new Runnable() { + public void run() { + try { + // Lock on testConn to emulate runtime locking behavior of Repl/LB connections. + synchronized (testConn) { + testConn.createStatement().executeQuery("SELECT column FROM table"); + } + } catch (Exception e) { + } + } + }, testConn.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(testConn)) + "_thread").start(); + } + + // Let the two concurrent threads run concurrently for 2secs, at the most, before checking if they hit a deadlock situation. + // Wait two times 1sec as TestBug21934573ExceptionInterceptor.mainThreadLock.notify() should be called twice (once per secondary thread). + synchronized (TestBug21934573ExceptionInterceptor.mainThreadLock) { + TestBug21934573ExceptionInterceptor.mainThreadLock.wait(1000); + TestBug21934573ExceptionInterceptor.mainThreadLock.wait(1000); + } + + int deadlockCount = 0; + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + long[] threadIds = threadMXBean.findMonitorDeadlockedThreads(); + if (threadIds != null) { + ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE); + for (ThreadInfo ti : threadInfos) { + System.out.println(); + System.out.println(ti); + System.out.println("Stack trace:"); + for (StackTraceElement ste : ti.getStackTrace()) { + System.out.println(" " + ste); + } + if (ti.getThreadName().equals("early_syncing_thread") || ti.getThreadName().equals("late_syncing_thread")) { + deadlockCount++; + } + } + if (deadlockCount == 2) {// Acquire the connection's monitor to mimic the behavior of other synchronized methods (like close() or doPing()). + fail("Deadlock detected. WARNING: this failure may lead to JVM instability."); + } else { + fail("Unexpected deadlock detected. Consult system output for more details. WARNING: this failure may lead to JVM instability."); + } + } + } + + /* + * Mimics the behavior of ErrorReportingExceptionInterceptor/FabricMySQLConnectionProxy.syncGroupServersToReplicationConnectionGroup() but actuates on any + * SQLException (not only communication related exceptions) and calls directly methods changing servers lists from ReplicationConnectionGroup. + */ + public static class TestBug21934573ExceptionInterceptor implements ExceptionInterceptor { + static Object mainThreadLock = new Object(); + private static boolean threadIsWaiting = false; + private static final Set replConnGroupLocks = Collections.synchronizedSet(new HashSet()); + + private boolean useSyncGroupServersLock = true; + + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + if (props.containsKey("__useReplConnGroupLocks__")) { + this.useSyncGroupServersLock = Boolean.parseBoolean(props.getProperty("__useReplConnGroupLocks__")); + } + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { + // Make sure both threads execute the code after the synchronized block concurrently. + synchronized (TestBug21934573ExceptionInterceptor.class) { + if (threadIsWaiting) { + TestBug21934573ExceptionInterceptor.class.notify(); + } else { + threadIsWaiting = true; + try { + TestBug21934573ExceptionInterceptor.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + com.mysql.jdbc.ReplicationConnectionGroup replConnGrp = ReplicationConnectionGroupManager.getConnectionGroup("deadlock"); + if (!this.useSyncGroupServersLock || replConnGroupLocks.add(replConnGrp.getGroupName())) { + try { + System.out.println("Emulating syncing state in: " + replConnGrp + " on thread " + Thread.currentThread().getName() + "."); + replConnGrp.removeMasterHost("localhost:1234"); + replConnGrp.addSlaveHost("localhost:1234"); + replConnGrp.removeSlaveHost("localhost:1234", false); + replConnGrp.promoteSlaveToMaster("localhost:1234"); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } finally { + if (this.useSyncGroupServersLock) { + replConnGroupLocks.remove(replConnGrp.getGroupName()); + } + } + } else { + System.out.println("Giving up syncing state on thread " + Thread.currentThread() + ". Let the other thread do it!"); + } + + synchronized (TestBug21934573ExceptionInterceptor.mainThreadLock) { + TestBug21934573ExceptionInterceptor.mainThreadLock.notify(); + } + return null; + } + } + + /** + * Tests fix for BUG#21947042, PREFER TLS WHERE SUPPORTED BY MYSQL SERVER. + * + * Requires test certificates from testsuite/ssl-test-certs to be installed + * on the server being tested. + * + * @throws Exception + * if the test fails. + */ + public void testBug21947042() throws Exception { + Connection sslConn = null; + Properties props = new Properties(); + props.setProperty("logger", "StandardLogger"); + + StandardLogger.startLoggingToBuffer(); + + try { + int searchFrom = 0; + int found = 0; + + // 1. No explicit useSSL + sslConn = getConnectionWithProps(props); + if (versionMeetsMinimum(5, 7)) { + assertTrue(((MySQLConnection) sslConn).getUseSSL()); + assertFalse(((MySQLConnection) sslConn).getVerifyServerCertificate()); + assertTrue(((MySQLConnection) sslConn).getIO().isSSLEstablished()); + } else { + assertFalse(((MySQLConnection) sslConn).getUseSSL()); + assertTrue(((MySQLConnection) sslConn).getVerifyServerCertificate()); + assertFalse(((MySQLConnection) sslConn).getIO().isSSLEstablished()); + } + + ResultSet rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_cipher'"); + assertTrue(rset.next()); + String cipher = rset.getString(2); + System.out.println("ssl_cipher=" + cipher); + + rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'"); + assertTrue(rset.next()); + cipher = rset.getString(2); + System.out.println("ssl_version=" + cipher); + + sslConn.close(); + + // check for warning + String log = StandardLogger.getBuffer().toString(); + found = log.indexOf(Messages.getString("MysqlIO.SSLWarning"), searchFrom); + searchFrom = found + 1; + if (versionMeetsMinimum(5, 7)) { + assertTrue(found != -1); + } + + // 2. Explicit useSSL=false + props.setProperty("useSSL", "false"); + sslConn = getConnectionWithProps(props); + assertFalse(((MySQLConnection) sslConn).getUseSSL()); + assertTrue(((MySQLConnection) sslConn).getVerifyServerCertificate()); // we left with default value here + assertFalse(((MySQLConnection) sslConn).getIO().isSSLEstablished()); + + rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_cipher'"); + assertTrue(rset.next()); + cipher = rset.getString(2); + System.out.println("ssl_cipher=" + cipher); + + rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'"); + assertTrue(rset.next()); + cipher = rset.getString(2); + System.out.println("ssl_version=" + cipher); + + sslConn.close(); + + // check for warning + log = StandardLogger.getBuffer().toString(); + found = log.indexOf(Messages.getString("MysqlIO.SSLWarning"), searchFrom); + if (found != -1) { + searchFrom = found + 1; + fail("Warning is not expected when useSSL is explicitly set to 'false'."); + } + + // 3. Explicit useSSL=true + props.setProperty("useSSL", "true"); + props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store"); + props.setProperty("trustCertificateKeyStoreType", "JKS"); + props.setProperty("trustCertificateKeyStorePassword", "password"); + sslConn = getConnectionWithProps(props); + assertTrue(((MySQLConnection) sslConn).getUseSSL()); + assertTrue(((MySQLConnection) sslConn).getVerifyServerCertificate()); // we left with default value here + assertTrue(((MySQLConnection) sslConn).getIO().isSSLEstablished()); + + rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_cipher'"); + assertTrue(rset.next()); + cipher = rset.getString(2); + System.out.println("ssl_cipher=" + cipher); + + rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'"); + assertTrue(rset.next()); + cipher = rset.getString(2); + System.out.println("ssl_version=" + cipher); + + sslConn.close(); + + // check for warning + log = StandardLogger.getBuffer().toString(); + found = log.indexOf(Messages.getString("MysqlIO.SSLWarning"), searchFrom); + if (found != -1) { + searchFrom = found + 1; + fail("Warning is not expected when useSSL is explicitly set to 'false'."); + } + + } finally { + StandardLogger.dropBuffer(); + } + } + + /** + * Tests fix for Bug#56100 - Replication driver routes DML statements to read-only slaves. + */ + public void testBug56100() throws Exception { + final String port = getPort(null, new NonRegisteringDriver()); + final String hostMaster = "master:" + port; + final String hostSlave = "slave:" + port; + + final Properties props = new Properties(); + props.setProperty("statementInterceptors", Bug56100StatementInterceptor.class.getName()); + + final ReplicationConnection testConn = getUnreliableReplicationConnection(new String[] { "master", "slave" }, props); + + assertTrue(testConn.isHostMaster(hostMaster)); + assertTrue(testConn.isHostSlave(hostSlave)); + + // verify that current connection is 'master' + assertTrue(testConn.isMasterConnection()); + + final Statement testStmt1 = testConn.createStatement(); + testBug56100AssertHost(testStmt1, "master"); + + // set connection to read-only state and verify that current connection is 'slave' now + testConn.setReadOnly(true); + assertFalse(testConn.isMasterConnection()); + + final Statement testStmt2 = testConn.createStatement(); + testBug56100AssertHost(testStmt1, "slave"); + testBug56100AssertHost(testStmt2, "slave"); + + // set connection to read/write state and verify that current connection is 'master' again + testConn.setReadOnly(false); + assertTrue(testConn.isMasterConnection()); + + final Statement testStmt3 = testConn.createStatement(); + testBug56100AssertHost(testStmt1, "master"); + testBug56100AssertHost(testStmt2, "master"); + testBug56100AssertHost(testStmt3, "master"); + + // let Connection.close() also close open statements + testConn.close(); + + assertThrows(SQLException.class, "No operations allowed after statement closed.", new Callable() { + public Void call() throws Exception { + testStmt1.execute("SELECT 'Bug56100'"); + return null; + } + }); + + assertThrows(SQLException.class, "No operations allowed after statement closed.", new Callable() { + public Void call() throws Exception { + testStmt2.execute("SELECT 'Bug56100'"); + return null; + } + }); + + assertThrows(SQLException.class, "No operations allowed after statement closed.", new Callable() { + public Void call() throws Exception { + testStmt3.execute("SELECT 'Bug56100'"); + return null; + } + }); + } + + private void testBug56100AssertHost(Statement testStmt, String expectedHost) throws SQLException { + this.rs = testStmt.executeQuery("SELECT ''"); + assertTrue(this.rs.next()); + assertEquals(expectedHost, this.rs.getString(1)); + this.rs.close(); + } + + public static class Bug56100StatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql.contains("")) { + return (ResultSetInternalMethods) interceptedStatement.executeQuery(sql.replace("", connection.getHost())); + } + return super.preProcess(sql, interceptedStatement, connection); + } + } + + /** + * Tests fix for WL#8196, Support for TLSv1.2 Protocol. + * + * This test requires community server (with yaSSL) in -Dcom.mysql.jdbc.testsuite.url and + * commercial server (with OpenSSL) in -Dcom.mysql.jdbc.testsuite.url.sha256default + * + * Test certificates from testsuite/ssl-test-certs must be installed on both servers. + * + * @throws Exception + * if the test fails. + */ + public void testTLSVersion() throws Exception { + + final String[] testDbUrls; + Properties props = new Properties(); + props.setProperty("allowPublicKeyRetrieval", "true"); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store"); + props.setProperty("trustCertificateKeyStoreType", "JKS"); + props.setProperty("trustCertificateKeyStorePassword", "password"); + + if (this.sha256Conn != null && ((MySQLConnection) this.sha256Conn).versionMeetsMinimum(5, 5, 7)) { + testDbUrls = new String[] { BaseTestCase.dbUrl, sha256Url }; + } else { + testDbUrls = new String[] { BaseTestCase.dbUrl }; + } + + for (String testDbUrl : testDbUrls) { + System.out.println(testDbUrl); + System.out.println(System.getProperty("java.version")); + Connection sslConn = getConnectionWithProps(testDbUrl, props); + assertTrue(((MySQLConnection) sslConn).getIO().isSSLEstablished()); + + ResultSet rset = sslConn.createStatement().executeQuery("SHOW STATUS LIKE 'ssl_version'"); + assertTrue(rset.next()); + String tlsVersion = rset.getString(2); + System.out.println(tlsVersion); + System.out.println(); + + if (((MySQLConnection) sslConn).versionMeetsMinimum(5, 7, 10) && Util.getJVMVersion() > 6) { + if (Util.isEnterpriseEdition(((MySQLConnection) sslConn).getServerVersion())) { + assertEquals("TLSv1.2", tlsVersion); + } else { + assertEquals("TLSv1.1", tlsVersion); + } + } else { + assertEquals("TLSv1", tlsVersion); + } + + sslConn.close(); + } + } + + /** + * Tests fix for Bug#21286268 - CONNECTOR/J REPLICATION USE MASTER IF SLAVE IS UNAVAILABLE. + */ + public void testBug21286268() throws Exception { + final String MASTER = "master"; + final String SLAVE = "slave"; + + final String MASTER_OK = UnreliableSocketFactory.getHostConnectedStatus(MASTER); + final String MASTER_FAIL = UnreliableSocketFactory.getHostFailedStatus(MASTER); + final String SLAVE_OK = UnreliableSocketFactory.getHostConnectedStatus(SLAVE); + final String SLAVE_FAIL = UnreliableSocketFactory.getHostFailedStatus(SLAVE); + + final String[] hosts = new String[] { MASTER, SLAVE }; + final Properties props = new Properties(); + props.setProperty("connectTimeout", "100"); + props.setProperty("retriesAllDown", "2"); // Failed connection attempts will show up twice. + final Set downedHosts = new HashSet(); + Connection testConn = null; + + /* + * Initialization case 1: Masters and Slaves up. + */ + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + /* + * Initialization case 2a: Masters up and Slaves down (readFromMasterWhenNoSlaves=false). + */ + props.setProperty("readFromMasterWhenNoSlaves", "false"); + downedHosts.clear(); + downedHosts.add(SLAVE); + UnreliableSocketFactory.flushAllStaticData(); + + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getUnreliableReplicationConnection(hosts, props, downedHosts); + return null; + } + }); + assertConnectionsHistory(SLAVE_FAIL); + props.remove("readFromMasterWhenNoSlaves"); + + /* + * Initialization case 2b: Masters up and Slaves down (allowSlaveDownConnections=true). + */ + props.setProperty("allowSlaveDownConnections", "true"); + downedHosts.clear(); + downedHosts.add(SLAVE); + UnreliableSocketFactory.flushAllStaticData(); + + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_FAIL, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + props.remove("allowSlaveDownConnections"); + + /* + * Initialization case 3a: Masters down and Slaves up (allowSlaveDownConnections=false). + */ + props.setProperty("allowSlaveDownConnections", "false"); + downedHosts.clear(); + downedHosts.add(MASTER); + UnreliableSocketFactory.flushAllStaticData(); + + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getUnreliableReplicationConnection(hosts, props, downedHosts); + return null; + } + }); + assertConnectionsHistory(SLAVE_OK, MASTER_FAIL, MASTER_FAIL); + props.remove("allowSlaveDownConnections"); + + /* + * Initialization case 3b: Masters down and Slaves up (allowMasterDownConnections=true). + */ + props.setProperty("allowMasterDownConnections", "true"); + downedHosts.clear(); + downedHosts.add(MASTER); + UnreliableSocketFactory.flushAllStaticData(); + + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_FAIL, MASTER_FAIL); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + props.remove("allowMasterDownConnections"); + + /* + * Initialization case 4: Masters down and Slaves down (allowMasterDownConnections=[false|true] + allowSlaveDownConnections=[false|true]). + */ + for (int tst = 0; tst < 4; tst++) { + boolean allowMasterDownConnections = (tst & 0x1) != 0; + boolean allowSlaveDownConnections = (tst & 0x2) != 0; + + String testCase = String.format("Case: %d [ %s | %s ]", tst, allowMasterDownConnections ? "alwMstDn" : "-", + allowSlaveDownConnections ? "alwSlvDn" : "-"); + System.out.println(testCase); + + props.setProperty("allowMasterDownConnections", Boolean.toString(allowMasterDownConnections)); + props.setProperty("allowSlaveDownConnections", Boolean.toString(allowSlaveDownConnections)); + downedHosts.clear(); + downedHosts.add(MASTER); + downedHosts.add(SLAVE); + UnreliableSocketFactory.flushAllStaticData(); + + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getUnreliableReplicationConnection(hosts, props, downedHosts); + return null; + } + }); + if (allowSlaveDownConnections) { + assertConnectionsHistory(SLAVE_FAIL, SLAVE_FAIL, MASTER_FAIL, MASTER_FAIL); + } else { + assertConnectionsHistory(SLAVE_FAIL, SLAVE_FAIL); + } + props.remove("allowMasterDownConnections"); + props.remove("allowSlaveDownConnections"); + } + + /* + * Run-time case 1: Switching between masters and slaves. + */ + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + // Use Masters. + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Use Slaves. + testConn.setReadOnly(true); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + + // Use Masters. + testConn.setReadOnly(false); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + /* + * Run-time case 2a: Running with Masters down (Masters doesn't recover). + */ + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + // Use Masters. + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Master server down. + UnreliableSocketFactory.downHost(MASTER); + + // Use Slaves. + testConn.setReadOnly(true); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + + // Use Masters (fail!). + testConn.setReadOnly(false); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); // No changes so far. + { + final Connection localTestConn = testConn; + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + ResultSet rset = localTestConn.createStatement().executeQuery("SELECT 1"); + rset.next(); + return null; + } + }); + } + assertConnectionsHistory(SLAVE_OK, MASTER_OK, MASTER_FAIL, MASTER_FAIL); + + /* + * Run-time case 2b: Running with Masters down (Masters recover in time). + */ + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + // Use Masters. + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Find Masters conn ID. + long connId = ((MySQLConnection) testConn).getId(); + + // Master server down. + UnreliableSocketFactory.downHost(MASTER); + this.stmt.execute("KILL CONNECTION " + connId); // Actually kill the Masters connection at server side. + + // Use Slaves. + testConn.setReadOnly(true); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + + // Master server up. + UnreliableSocketFactory.dontDownHost(MASTER); + + // Use Masters. + testConn.setReadOnly(false); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); // No changes so far. + { + final Connection localTestConn = testConn; + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + ResultSet rset = localTestConn.createStatement().executeQuery("SELECT 1"); + rset.next(); + return null; + } + }); + } + assertConnectionsHistory(SLAVE_OK, MASTER_OK, MASTER_OK); // Masters connection re-initialized. + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + /* + * Run-time case 3a: Running with Slaves down (readFromMasterWhenNoSlaves=false). + */ + props.setProperty("readFromMasterWhenNoSlaves", "false"); + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + // Use Masters. + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Find Slaves conn ID. + testConn.setReadOnly(true); + connId = ((MySQLConnection) testConn).getId(); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + testConn.setReadOnly(false); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Slave server down. + UnreliableSocketFactory.downHost(SLAVE); + this.stmt.execute("KILL CONNECTION " + connId); // Actually kill the Slaves connection at server side. + + // Use Slaves. + testConn.setReadOnly(true); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); // No changes so far. + { + final Connection localTestConn = testConn; + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + ResultSet rset = localTestConn.createStatement().executeQuery("SELECT 1"); + rset.next(); + return null; + } + }); + } + assertConnectionsHistory(SLAVE_OK, MASTER_OK, SLAVE_FAIL, SLAVE_FAIL); // Failed re-initializing Slaves. + + // Retry using Slaves. Will fail definitely. + { + final Connection localTestConn = testConn; + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + localTestConn.setReadOnly(true); + return null; + } + }); + } + assertConnectionsHistory(SLAVE_OK, MASTER_OK, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL); // Failed connecting to Slaves. + + /* + * Run-time case 3b: Running with Slaves down (readFromMasterWhenNoSlaves=true). + */ + props.setProperty("readFromMasterWhenNoSlaves", "true"); + downedHosts.clear(); + UnreliableSocketFactory.flushAllStaticData(); + + // Use Masters. + testConn = getUnreliableReplicationConnection(hosts, props, downedHosts); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Find Slaves conn ID. + testConn.setReadOnly(true); + connId = ((MySQLConnection) testConn).getId(); + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + testConn.setReadOnly(false); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Slave server down. + UnreliableSocketFactory.downHost(SLAVE); + this.stmt.execute("KILL CONNECTION " + connId); // Actually kill the Slaves connection at server side. + + // Use Slaves. + testConn.setReadOnly(true); + assertConnectionsHistory(SLAVE_OK, MASTER_OK); // No changes so far. + { + final Connection localTestConn = testConn; + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + ResultSet rset = localTestConn.createStatement().executeQuery("SELECT 1"); + rset.next(); + return null; + } + }); + } + assertConnectionsHistory(SLAVE_OK, MASTER_OK, SLAVE_FAIL, SLAVE_FAIL); // Failed re-initializing Slaves. + + // Retry using Slaves. Will fall-back to Masters as read-only. + testConn.setReadOnly(true); + assertConnectionsHistory(SLAVE_OK, MASTER_OK, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL); // Failed connecting to Slaves, failed-over to Masters. + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, true); + + // Use Masters. + testConn.setReadOnly(false); + testBug21286268AssertConnectedToAndReadOnly(testConn, MASTER, false); + + // Slave server up. + UnreliableSocketFactory.dontDownHost(SLAVE); + + // Use Slaves. + testConn.setReadOnly(true); + assertConnectionsHistory(SLAVE_OK, MASTER_OK, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL, SLAVE_FAIL, SLAVE_OK); // Slaves connection re-initialized. + testBug21286268AssertConnectedToAndReadOnly(testConn, SLAVE, true); + props.remove("readFromMasterWhenNoSlaves"); + } + + private void testBug21286268AssertConnectedToAndReadOnly(Connection testConn, String expectedHost, boolean expectedReadOnly) throws SQLException { + this.rs = testConn.createStatement().executeQuery("SELECT 1"); + assertEquals(expectedHost, ((MySQLConnection) testConn).getHost()); + assertEquals(expectedReadOnly, testConn.isReadOnly()); + } + + /** + * Tests fix for Bug#77171 - On every connect getting sql_mode from server creates unnecessary exception. + * + * This fix is a refactoring on ConnectorImpl.initializePropsFromServer() to improve performance when processing the SQL_MODE value. No behavior was + * changed. This test guarantees that nothing was broken in these matters, for the relevant MySQL versions, after this fix. + */ + public void testBug77171() throws Exception { + String sqlMode = getMysqlVariable("sql_mode"); + sqlMode = removeSqlMode("ANSI_QUOTES", sqlMode); + sqlMode = removeSqlMode("NO_BACKSLASH_ESCAPES", sqlMode); + String newSqlMode = sqlMode; + if (sqlMode.length() > 0) { + sqlMode += ","; + } + + Properties props = new Properties(); + props.put("sessionVariables", "sql_mode='" + newSqlMode + "'"); + Connection testConn = getConnectionWithProps(props); + assertFalse(((MySQLConnection) testConn).useAnsiQuotedIdentifiers()); + assertFalse(((MySQLConnection) testConn).isNoBackslashEscapesSet()); + testConn.close(); + + props.clear(); + newSqlMode = sqlMode + "ANSI_QUOTES"; + props.put("sessionVariables", "sql_mode='" + newSqlMode + "'"); + testConn = getConnectionWithProps(props); + assertTrue(((MySQLConnection) testConn).useAnsiQuotedIdentifiers()); + assertFalse(((MySQLConnection) testConn).isNoBackslashEscapesSet()); + testConn.close(); + + props.clear(); + newSqlMode = sqlMode + "NO_BACKSLASH_ESCAPES"; + props.put("sessionVariables", "sql_mode='" + newSqlMode + "'"); + testConn = getConnectionWithProps(props); + assertFalse(((MySQLConnection) testConn).useAnsiQuotedIdentifiers()); + assertTrue(((MySQLConnection) testConn).isNoBackslashEscapesSet()); + testConn.close(); + + props.clear(); + newSqlMode = sqlMode + "ANSI_QUOTES,NO_BACKSLASH_ESCAPES"; + props.put("sessionVariables", "sql_mode='" + newSqlMode + "'"); + testConn = getConnectionWithProps(props); + assertTrue(((MySQLConnection) testConn).useAnsiQuotedIdentifiers()); + assertTrue(((MySQLConnection) testConn).isNoBackslashEscapesSet()); + testConn.close(); + } + + /** + * Tests fix for Bug#22730682 - ARRAYINDEXOUTOFBOUNDSEXCEPTION FROM CONNECTIONGROUPMANAGER.REMOVEHOST(). + * + * This bug was caused by an incorrect array handling when removing an host from a load balanced connection group, with the option to affect existing + * connections. + */ + public void testBug22730682() throws Exception { + Properties connProps = getPropertiesFromTestsuiteUrl(); + String host = connProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + String port = connProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + + final String currentHost = host + ":" + port; + final String dummyHost = "bug22730682:12345"; + + final Properties props = new Properties(); + Connection testConn; + + final String lbConnGroup1 = "Bug22730682LB1"; + props.setProperty("loadBalanceConnectionGroup", lbConnGroup1); + testConn = getLoadBalancedConnection(3, dummyHost, props); + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup1).contains(dummyHost)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup1).contains(currentHost)); + ConnectionGroupManager.removeHost(lbConnGroup1, dummyHost); + assertEquals(1, ConnectionGroupManager.getActiveHostCount(lbConnGroup1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup1).contains(currentHost)); + testConn.close(); + + final String lbConnGroup2 = "Bug22730682LB2"; + props.setProperty("loadBalanceConnectionGroup", lbConnGroup2); + testConn = getLoadBalancedConnection(3, dummyHost, props); + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup2).contains(dummyHost)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup2).contains(currentHost)); + ConnectionGroupManager.removeHost(lbConnGroup2, dummyHost, true); + assertEquals(1, ConnectionGroupManager.getActiveHostCount(lbConnGroup2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup2).contains(currentHost)); + testConn.close(); + } + + /** + * Tests fix for Bug#22848249 - LOADBALANCECONNECTIONGROUPMANAGER.REMOVEHOST() NOT WORKING AS EXPECTED. + * + * Tests a sequence of additions and removals of hosts from a load-balanced connection group. + */ + public void testBug22848249() throws Exception { + /* + * Remove and add hosts to the connection group, other than the one from the active underlying connection. + * Changes affecting active l/b connections. + */ + subTestBug22848249A(); + + /* + * Remove and add hosts to the connection group, including the host from the active underlying connection. + * Changes affecting active l/b connections. + */ + subTestBug22848249B(); + + /* + * Remove hosts from the connection group with changes not affecting active l/b connections. + */ + subTestBug22848249C(); + /* + * Add hosts to the connection group with changes not affecting active l/b connections. + */ + subTestBug22848249D(); + } + + /* + * Tests removing and adding hosts (excluding the host from the underlying physical connection) to the connection group with the option to propagate + * changes to all active load-balanced connections. + */ + private void subTestBug22848249A() throws Exception { + final String defaultHost = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + final String defaultPort = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + final String host1 = "first"; + final String host2 = "second"; + final String host3 = "third"; + final String host4 = "fourth"; + final String hostPort1 = host1 + ":" + defaultPort; + final String hostPort2 = host2 + ":" + defaultPort; + final String hostPort3 = host3 + ":" + defaultPort; + final String hostPort4 = host4 + ":" + defaultPort; + final String lbConnGroup = "Bug22848249A"; + + System.out.println("testBug22848249A:"); + System.out.println("********************************************************************************"); + + Properties props = new Properties(); + props.setProperty("loadBalanceConnectionGroup", lbConnGroup); + Connection testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2, host3 }, props); + testConn.setAutoCommit(false); + + String connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertEquals(3, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + + /* + * The l/b connection won't be able to use removed unused hosts. + */ + + // Remove a non-connected host: host2 or host3. + String removedHost = connectedHost.equals(host3) ? host2 : host3; + String removedHostPort = removedHost + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort, true); + assertEquals(connectedHost, ((com.mysql.jdbc.MySQLConnection) testConn).getHost()); // Still connected to the initital host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort.equals(hostPort2)); // Only one can be true. + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort.equals(hostPort3)); + + // Force some transaction boundaries while checking that the removed host is never used. + int connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(removedHost)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t1. Swapped connections " + connectionSwaps + " times out of 100, without hitting the removed host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertFalse(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(removedHost))); + + /* + * The l/b connection will be able to use a host added back to the connection group. + */ + + // Add back the previously removed host. + ConnectionGroupManager.addHost(lbConnGroup, removedHostPort, true); + assertEquals(3, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + + // Force transaction boundaries until the new host is selected or a limit number of attempts is reached. + String newHost = removedHost; + connectionSwaps = 0; + int attemptsLeft = 100; + while (!(connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost()).equals(newHost)) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + if (--attemptsLeft == 0) { + fail("Failed to swap to the newly added host after 100 transaction boundaries and " + connectionSwaps + " connection swaps."); + } + } + System.out.println("\t2. Swapped connections " + connectionSwaps + " times before hitting the new host."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertTrue(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(newHost))); + + /* + * The l/b connection will be able to use new hosts added to the connection group. + */ + + // Add a completely new host. + UnreliableSocketFactory.mapHost(host4, defaultHost); + ConnectionGroupManager.addHost(lbConnGroup, hostPort4, true); + assertEquals(4, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4)); + + // Force transaction boundaries until the new host is selected or a limit number of attempts is reached. + newHost = host4; + connectionSwaps = 0; + attemptsLeft = 100; + while (!(connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost()).equals(newHost)) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + if (--attemptsLeft == 0) { + fail("Failed to swap to the newly added host after 100 transaction boundaries and " + connectionSwaps + " connection swaps."); + } + } + System.out.println("\t3. Swapped connections " + connectionSwaps + " times before hitting the new host."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertTrue(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(newHost))); + + /* + * The l/b connection won't be able to use any number of removed hosts (excluding the current active host). + */ + + // Remove any two hosts, other than the one used in the active connection. + String removedHost1 = connectedHost.equals(host2) ? host1 : host2; + String removedHostPort1 = removedHost1 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort1, true); + String removedHost2 = connectedHost.equals(host4) ? host3 : host4; + String removedHostPort2 = removedHost2 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort2, true); + assertEquals(connectedHost, ((com.mysql.jdbc.MySQLConnection) testConn).getHost()); // Still connected to the same host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1) ^ removedHostPort1.equals(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort1.equals(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort2.equals(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4) ^ removedHostPort2.equals(hostPort4)); + + // Force some transaction boundaries while checking that the removed hosts are never used. + connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(removedHost1)); + assertFalse(newConnectedHost.equals(removedHost2)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t4. Swapped connections " + connectionSwaps + " times out of 100, without hitting the removed host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + } + + /* + * Tests removing and adding hosts (including the host from the underlying physical connection) to the connection group with the option to propagate + * changes to all active load-balanced connections. + */ + private void subTestBug22848249B() throws Exception { + final String defaultHost = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + final String defaultPort = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + final String host1 = "first"; + final String host2 = "second"; + final String host3 = "third"; + final String host4 = "fourth"; + final String hostPort1 = host1 + ":" + defaultPort; + final String hostPort2 = host2 + ":" + defaultPort; + final String hostPort3 = host3 + ":" + defaultPort; + final String hostPort4 = host4 + ":" + defaultPort; + final String lbConnGroup = "Bug22848249B"; + + System.out.println("testBug22848249B:"); + System.out.println("********************************************************************************"); + + Properties props = new Properties(); + props.setProperty("loadBalanceConnectionGroup", lbConnGroup); + Connection testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2, host3 }, props); + testConn.setAutoCommit(false); + + String connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertEquals(3, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + + /* + * The l/b connection won't be able to use removed hosts. + * Undelying connection is invalidated after removing the host currently being used. + */ + + // Remove the connected host. + String removedHost = connectedHost; + String removedHostPort = removedHost + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort, true); + assertFalse(((com.mysql.jdbc.MySQLConnection) testConn).getHost().equals(connectedHost)); // No longer connected to the removed host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1) ^ removedHostPort.equals(hostPort1)); // Only one can be true. + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort.equals(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort.equals(hostPort3)); + + // Force some transaction boundaries while checking that the removed host is never used again. + UnreliableSocketFactory.flushConnectionAttempts(); + int connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(removedHost)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t1. Swapped connections " + connectionSwaps + " times out of 100, without hitting the removed host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertFalse(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(removedHost))); + + /* + * The l/b connection will be able to use a host added back to the connection group. + */ + + // Add back the previously removed host. + ConnectionGroupManager.addHost(lbConnGroup, removedHostPort, true); + assertEquals(3, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + + // Force transaction boundaries until the new host is selected or a limit number of attempts is reached. + String newHost = removedHost; + connectionSwaps = 0; + int attemptsLeft = 100; + while (!(connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost()).equals(newHost)) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + if (--attemptsLeft == 0) { + fail("Failed to swap to the newly added host after 100 transaction boundaries and " + connectionSwaps + " connection swaps."); + } + } + System.out.println("\t2. Swapped connections " + connectionSwaps + " times before hitting the new host."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertTrue(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(newHost))); + + /* + * The l/b connection will be able to use new hosts added to the connection group. + */ + + // Add a completely new host. + UnreliableSocketFactory.mapHost(host4, defaultHost); + ConnectionGroupManager.addHost(lbConnGroup, hostPort4, true); + assertEquals(4, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4)); + + // Force transaction boundaries until the new host is selected or a limit number of attempts is reached. + newHost = host4; + connectionSwaps = 0; + attemptsLeft = 100; + while (!(connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost()).equals(newHost)) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + if (--attemptsLeft == 0) { + fail("Failed to swap to the newly added host after 100 transaction boundaries and " + connectionSwaps + " connection swaps."); + } + } + System.out.println("\t3. Swapped connections " + connectionSwaps + " times before hitting the new host."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertTrue(UnreliableSocketFactory.getHostsFromAllConnections().contains(UnreliableSocketFactory.getHostConnectedStatus(newHost))); + + /* + * The l/b connection won't be able to use any number of removed hosts (including the current active host). + * Undelying connection is invalidated after removing the host currently being used. + */ + + // Remove two hosts, one of them is from the active connection. + String removedHost1 = connectedHost.equals(host1) ? host1 : host2; + String removedHostPort1 = removedHost1 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort1, true); + String removedHost2 = connectedHost.equals(host3) ? host3 : host4; + String removedHostPort2 = removedHost2 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort2, true); + assertFalse(((com.mysql.jdbc.MySQLConnection) testConn).getHost().equals(removedHost1)); // Not connected to the first removed host. + assertFalse(((com.mysql.jdbc.MySQLConnection) testConn).getHost().equals(removedHost2)); // Not connected to the second removed host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1) ^ removedHostPort1.equals(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort1.equals(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort2.equals(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4) ^ removedHostPort2.equals(hostPort4)); + + // Force some transaction boundaries while checking that the removed hosts are never used. + connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(removedHost1)); + assertFalse(newConnectedHost.equals(removedHost2)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t4. Swapped connections " + connectionSwaps + " times out of 100, without hitting the removed host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + } + + /* + * Tests removing hosts from the connection group without affecting current active connections. + */ + private void subTestBug22848249C() throws Exception { + final String defaultPort = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + final String host1 = "first"; + final String host2 = "second"; + final String host3 = "third"; + final String host4 = "fourth"; + final String hostPort1 = host1 + ":" + defaultPort; + final String hostPort2 = host2 + ":" + defaultPort; + final String hostPort3 = host3 + ":" + defaultPort; + final String hostPort4 = host4 + ":" + defaultPort; + final String lbConnGroup = "Bug22848249C"; + + System.out.println("testBug22848249C:"); + System.out.println("********************************************************************************"); + + /* + * Initial connection will be able to use all hosts, even after removed from the connection group. + */ + Properties props = new Properties(); + props.setProperty("loadBalanceConnectionGroup", lbConnGroup); + Connection testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2, host3, host4 }, props); + testConn.setAutoCommit(false); + + String connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertEquals(4, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4)); + + // Remove two hosts, one of them is from the active connection. + String removedHost1 = connectedHost.equals(host1) ? host1 : host2; + String removedHostPort1 = removedHost1 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort1, false); + String removedHost2 = connectedHost.equals(host3) ? host3 : host4; + String removedHostPort2 = removedHost2 + ":" + defaultPort; + ConnectionGroupManager.removeHost(lbConnGroup, removedHostPort2, false); + assertEquals(connectedHost, ((com.mysql.jdbc.MySQLConnection) testConn).getHost()); // Still connected to the same host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1) ^ removedHostPort1.equals(hostPort1)); // Only one can be true. + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort1.equals(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort2.equals(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4) ^ removedHostPort2.equals(hostPort4)); + + // Force some transaction boundaries and check that all hosts are being used. + int connectionSwaps = 0; + Set hostsUsed = new HashSet(); + for (int i = 0; i < 100 && hostsUsed.size() < 4; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + hostsUsed.add(newConnectedHost); + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t1. Swapped connections " + connectionSwaps + " times out of 100 or before using all hosts."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertEquals(4, hostsUsed.size()); + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + + /* + * New connection wont be able to use the previously removed hosts. + */ + testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2, host3, host4 }, props); + testConn.setAutoCommit(false); + + connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertFalse(((com.mysql.jdbc.MySQLConnection) testConn).getHost().equals(removedHost1)); // Not connected to the removed host. + assertFalse(((com.mysql.jdbc.MySQLConnection) testConn).getHost().equals(removedHost2)); // Not connected to the removed host. + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1) ^ removedHostPort1.equals(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2) ^ removedHostPort1.equals(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3) ^ removedHostPort2.equals(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4) ^ removedHostPort2.equals(hostPort4)); + + // Force some transaction boundaries while checking that the removed hosts are never used. + connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(removedHost1)); + assertFalse(newConnectedHost.equals(removedHost2)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t2. Swapped connections " + connectionSwaps + " times out of 100, without hitting the removed host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + } + + /* + * Tests adding hosts from the connection group without affecting current active connections. + */ + private void subTestBug22848249D() throws Exception { + final String defaultHost = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + final String defaultPort = getPropertiesFromTestsuiteUrl().getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + final String host1 = "first"; + final String host2 = "second"; + final String host3 = "third"; + final String host4 = "fourth"; + final String hostPort1 = host1 + ":" + defaultPort; + final String hostPort2 = host2 + ":" + defaultPort; + final String hostPort3 = host3 + ":" + defaultPort; + final String hostPort4 = host4 + ":" + defaultPort; + final String lbConnGroup = "Bug22848249D"; + + System.out.println("testBug22848249D:"); + System.out.println("********************************************************************************"); + + /* + * Initial connection will be able to use only the hosts available when it was initialized, even after adding new ones to the connection group. + */ + Properties props = new Properties(); + props.setProperty("loadBalanceConnectionGroup", lbConnGroup); + Connection testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2 }, props); + testConn.setAutoCommit(false); + + String connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertEquals(2, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + + // Add two hosts. + UnreliableSocketFactory.mapHost(host3, defaultHost); + ConnectionGroupManager.addHost(lbConnGroup, hostPort3, false); + UnreliableSocketFactory.mapHost(host4, defaultHost); + ConnectionGroupManager.addHost(lbConnGroup, hostPort4, false); + assertEquals(4, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4)); + + // Force some transaction boundaries and check that the new hosts aren't used. + int connectionSwaps = 0; + for (int i = 0; i < 100; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertFalse(newConnectedHost.equals(host3)); + assertFalse(newConnectedHost.equals(host4)); + if (!connectedHost.equals(newConnectedHost)) { + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t1. Swapped connections " + connectionSwaps + " times out of 100, without hitting the newly added host(s)."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + + /* + * New connection will be able to use all hosts. + */ + testConn = getUnreliableLoadBalancedConnection(new String[] { host1, host2, host3, host4 }, props); + testConn.setAutoCommit(false); + + connectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + assertConnectionsHistory(UnreliableSocketFactory.getHostConnectedStatus(connectedHost)); + + assertEquals(4, ConnectionGroupManager.getActiveHostCount(lbConnGroup)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort1)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort2)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort3)); + assertTrue(ConnectionGroupManager.getActiveHostLists(lbConnGroup).contains(hostPort4)); + + // Force some transaction boundaries while checking that the removed hosts are never used. + connectionSwaps = 0; + Set hostsUsed = new HashSet(); + for (int i = 0; i < 100 && hostsUsed.size() < 4; i++) { + testConn.rollback(); + String newConnectedHost = ((com.mysql.jdbc.MySQLConnection) testConn).getHost(); + if (!connectedHost.equals(newConnectedHost)) { + hostsUsed.add(newConnectedHost); + connectedHost = newConnectedHost; + connectionSwaps++; + } + } + System.out.println("\t2. Swapped connections " + connectionSwaps + " times out of 100 or before using all hosts."); + assertTrue(connectionSwaps > 0); // Non-deterministic, but something must be wrong if there are no swaps after 100 transaction boundaries. + assertEquals(4, hostsUsed.size()); + + // Make sure the connection is working fine. + this.rs = testConn.createStatement().executeQuery("SELECT 'testBug22848249'"); + assertTrue(this.rs.next()); + assertEquals("testBug22848249", this.rs.getString(1)); + testConn.close(); + } + + /** + * Tests fix for Bug#22678872 - NPE DURING UPDATE WITH FABRIC. + * + * Although the bug was reported against a Fabric connection, it can't be systematically reproduced there. A deep analysis revealed that the bug occurs due + * to a defect in the dynamic hosts management of replication connections, specifically when one or both of the internal hosts lists (masters and/or slaves) + * becomes empty. As such, the bug is reproducible and tested resorting to replication connections and dynamic hosts management of replication connections + * only. + * This test reproduces the relevant steps involved in the original stack trace, originated in the FabricMySQLConnectionProxy.getActiveConnection() code: + * - The replication connections are initialized with the same properties as in a Fabric connection. + * - Hosts are removed using the same options as in a Fabric connection. + * - The method tested after any host change is Connection.setAutoCommit(), which is the method that triggered the original NPE. + */ + public void testBug22678872() throws Exception { + final Properties connProps = getPropertiesFromTestsuiteUrl(); + final String host = connProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + final String port = connProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + final String hostPortPair = host + ":" + port; + final String database = connProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + final String username = connProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + final String password = connProps.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, ""); + + final Properties props = new Properties(); + props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, username); + props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, password); + props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, database); + props.setProperty("useSSL", "false"); + props.setProperty("loadBalanceHostRemovalGracePeriod", "0"); // Speed up the test execution. + // Replicate the properties used in FabricMySQLConnectionProxy.getActiveConnection(). + props.setProperty("retriesAllDown", "1"); + props.setProperty("allowMasterDownConnections", "true"); + props.setProperty("allowSlaveDownConnections", "true"); + props.setProperty("readFromMasterWhenNoSlaves", "true"); + + String replConnGroup = ""; + final List emptyHostsList = Collections.emptyList(); + final List singleHostList = Collections.singletonList(hostPortPair); + + /* + * Case A: + * - Initialize a replication connection with masters and slaves lists empty. + */ + replConnGroup = "Bug22678872A"; + props.setProperty("replicationConnectionGroup", replConnGroup); + assertThrows(SQLException.class, "A replication connection cannot be initialized without master hosts and slave hosts, simultaneously\\.", + new Callable() { + public Void call() throws Exception { + ReplicationConnectionProxy.createProxyInstance(emptyHostsList, props, emptyHostsList, props); + return null; + } + }); + + /* + * Case B: + * - Initialize a replication connection with one master and no slaves. + * - Then remove the master and add it back as a slave, followed by a promotion to master. + */ + replConnGroup = "Bug22678872B"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnB = ReplicationConnectionProxy.createProxyInstance(singleHostList, props, emptyHostsList, props); + assertTrue(testConnB.isMasterConnection()); // Connected to a master host. + assertFalse(testConnB.isReadOnly()); + testConnB.setAutoCommit(false); // This was the method that triggered the original NPE. + ReplicationConnectionGroupManager.removeMasterHost(replConnGroup, hostPortPair, false); + assertThrows(SQLException.class, "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", + new Callable() { + public Void call() throws Exception { + testConnB.setAutoCommit(false); // JDBC interface method throws SQLException. + return null; + } + }); + assertThrows(IllegalStateException.class, + "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", new Callable() { + public Void call() throws Exception { + testConnB.isMasterConnection(); // Some Connector/J internal methods don't throw compatible exceptions. They have to be wrapped. + return null; + } + }); + + ReplicationConnectionGroupManager.addSlaveHost(replConnGroup, hostPortPair); + assertFalse(testConnB.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnB.isReadOnly()); + testConnB.setAutoCommit(false); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replConnGroup, hostPortPair); + assertTrue(testConnB.isMasterConnection()); // Connected to a master host. + assertFalse(testConnB.isReadOnly()); + testConnB.setAutoCommit(false); + testConnB.close(); + + /* + * Case C: + * - Initialize a replication connection with no masters and one slave. + * - Then remove the slave and add it back, followed by a promotion to master. + */ + replConnGroup = "Bug22678872C"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnC = ReplicationConnectionProxy.createProxyInstance(emptyHostsList, props, singleHostList, props); + assertFalse(testConnC.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnC.isReadOnly()); + testConnC.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeSlaveHost(replConnGroup, hostPortPair, true); + assertThrows(SQLException.class, "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", + new Callable() { + public Void call() throws Exception { + testConnC.setAutoCommit(false); + return null; + } + }); + + ReplicationConnectionGroupManager.addSlaveHost(replConnGroup, hostPortPair); + assertFalse(testConnC.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnC.isReadOnly()); + testConnC.setAutoCommit(false); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replConnGroup, hostPortPair); + assertTrue(testConnC.isMasterConnection()); // Connected to a master host ... + assertTrue(testConnC.isReadOnly()); // ... but the connection is read-only because it was initialized with no masters. + testConnC.setAutoCommit(false); + testConnC.close(); + + /* + * Case D: + * - Initialize a replication connection with one master and one slave. + * - Then remove the master host, followed by removing the slave host. + * - Finally add the slave host back and promote it to master. + */ + replConnGroup = "Bug22678872D"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnD = ReplicationConnectionProxy.createProxyInstance(singleHostList, props, singleHostList, props); + assertTrue(testConnD.isMasterConnection()); // Connected to a master host. + assertFalse(testConnD.isReadOnly()); + testConnD.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeMasterHost(replConnGroup, hostPortPair, false); + assertFalse(testConnD.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnD.isReadOnly()); + testConnD.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeSlaveHost(replConnGroup, hostPortPair, true); + assertThrows(SQLException.class, "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", + new Callable() { + public Void call() throws Exception { + testConnD.setAutoCommit(false); + return null; + } + }); + + ReplicationConnectionGroupManager.addSlaveHost(replConnGroup, hostPortPair); + assertFalse(testConnD.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnD.isReadOnly()); + testConnD.setAutoCommit(false); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replConnGroup, hostPortPair); + assertTrue(testConnD.isMasterConnection()); // Connected to a master host. + assertFalse(testConnD.isReadOnly()); + testConnD.setAutoCommit(false); + testConnD.close(); + + /* + * Case E: + * - Initialize a replication connection with one master and one slave. + * - Set read-only. + * - Then remove the slave host, followed by removing the master host. + * - Finally add the slave host back and promote it to master. + */ + replConnGroup = "Bug22678872E"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnE = ReplicationConnectionProxy.createProxyInstance(singleHostList, props, singleHostList, props); + assertTrue(testConnE.isMasterConnection()); // Connected to a master host. + assertFalse(testConnE.isReadOnly()); + testConnE.setAutoCommit(false); + + testConnE.setReadOnly(true); + assertFalse(testConnE.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnE.isReadOnly()); + testConnE.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeSlaveHost(replConnGroup, hostPortPair, true); + assertTrue(testConnE.isMasterConnection()); // Connected to a master host... + assertTrue(testConnE.isReadOnly()); // ... but the connection is read-only because that's how it was previously set. + testConnE.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeMasterHost(replConnGroup, hostPortPair, false); + assertThrows(SQLException.class, "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", + new Callable() { + public Void call() throws Exception { + testConnE.setAutoCommit(false); + return null; + } + }); + + ReplicationConnectionGroupManager.addSlaveHost(replConnGroup, hostPortPair); + assertFalse(testConnE.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnE.isReadOnly()); + testConnE.setAutoCommit(false); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replConnGroup, hostPortPair); + assertTrue(testConnE.isMasterConnection()); // Connected to a master host... + assertTrue(testConnE.isReadOnly()); // ... but the connection is read-only because that's how it was previously set. + testConnE.setAutoCommit(false); + testConnE.close(); + + /* + * Case F: + * - Initialize a replication connection with one master and one slave. + * - Then remove the slave host, followed by removing the master host. + * - Finally add the slave host back and promote it to master. + */ + replConnGroup = "Bug22678872F"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnF = ReplicationConnectionProxy.createProxyInstance(singleHostList, props, singleHostList, props); + assertTrue(testConnF.isMasterConnection()); // Connected to a master host. + assertFalse(testConnF.isReadOnly()); + testConnF.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeSlaveHost(replConnGroup, hostPortPair, true); + assertTrue(testConnF.isMasterConnection()); // Connected to a master host. + assertFalse(testConnF.isReadOnly()); + testConnF.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeMasterHost(replConnGroup, hostPortPair, false); + assertThrows(SQLException.class, "The replication connection is an inconsistent state due to non existing hosts in both its internal hosts lists\\.", + new Callable() { + public Void call() throws Exception { + testConnF.setAutoCommit(false); + return null; + } + }); + + ReplicationConnectionGroupManager.addSlaveHost(replConnGroup, hostPortPair); + assertFalse(testConnF.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnF.isReadOnly()); + testConnF.setAutoCommit(false); + + ReplicationConnectionGroupManager.promoteSlaveToMaster(replConnGroup, hostPortPair); + assertTrue(testConnF.isMasterConnection()); // Connected to a master host. + assertFalse(testConnF.isReadOnly()); + testConnF.setAutoCommit(false); + testConnF.close(); + + /* + * Case G: + * This covers one corner case where the attribute ReplicationConnectionProxy.currentConnection can still be null even when there are known hosts. It + * results from a combination of empty hosts lists with downed hosts: + * - Start with one host in each list. + * - Switch to the slaves connection (set read-only). + * - Remove the master host. + * - Make the slave only unavailable. + * - Promote the slave host to master. + * - (At this point the active connection is "null") + * - Finally bring up the host again and check the connection status. + */ + // Use the UnreliableSocketFactory to control when the host must be downed. + final String newHost = "bug22678872"; + final String newHostPortPair = newHost + ":" + port; + final String hostConnected = UnreliableSocketFactory.getHostConnectedStatus(newHost); + final String hostNotConnected = UnreliableSocketFactory.getHostFailedStatus(newHost); + final List newSingleHostList = Collections.singletonList(newHostPortPair); + UnreliableSocketFactory.flushAllStaticData(); + UnreliableSocketFactory.mapHost(newHost, host); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + replConnGroup = "Bug22678872G"; + props.setProperty("replicationConnectionGroup", replConnGroup); + final ReplicationConnection testConnG = ReplicationConnectionProxy.createProxyInstance(newSingleHostList, props, newSingleHostList, props); + assertTrue(testConnG.isMasterConnection()); // Connected to a master host. + assertFalse(testConnG.isReadOnly()); + testConnG.setAutoCommit(false); + + testBug22678872CheckConnectionsHistory(hostConnected, hostConnected); // Two successful connections. + + testConnG.setReadOnly(true); + assertFalse(testConnG.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnG.isReadOnly()); + testConnG.setAutoCommit(false); + + ReplicationConnectionGroupManager.removeMasterHost(replConnGroup, newHostPortPair, false); + assertFalse(testConnG.isMasterConnection()); // Connected to a slave host. + assertTrue(testConnG.isReadOnly()); + testConnG.setAutoCommit(false); + + UnreliableSocketFactory.downHost(newHost); // The host (currently a slave) goes down before being promoted to master. + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + testConnG.promoteSlaveToMaster(newHostPortPair); + return null; + } + }); + + testBug22678872CheckConnectionsHistory(hostNotConnected); // One failed connection attempt. + + assertFalse(testConnG.isMasterConnection()); // Actually not connected, but the promotion to master succeeded. + assertThrows(SQLException.class, "The connection is unusable at the current state\\. There may be no hosts to connect to or all hosts this " + + "connection knows may be down at the moment\\.", new Callable() { + public Void call() throws Exception { + testConnG.setAutoCommit(false); + return null; + } + }); + + testBug22678872CheckConnectionsHistory(hostNotConnected); // Another failed connection attempt. + + assertThrows(SQLException.class, "(?s)Communications link failure.*", new Callable() { + public Void call() throws Exception { + testConnG.setReadOnly(false); // Triggers a reconnection that fails. The read-only state change is canceled by the exception. + return null; + } + }); // This throws a comm failure because it tried to connect to the existing server and failed. The internal read-only state didn't change. + + testBug22678872CheckConnectionsHistory(hostNotConnected); // Another failed connection attempt. + + UnreliableSocketFactory.dontDownHost(newHost); // The host (currently a master) is up again. + testConnG.setAutoCommit(false); // Triggers a reconnection that succeeds. + + testBug22678872CheckConnectionsHistory(hostConnected); // One successful connection. + + assertTrue(testConnG.isMasterConnection()); // Connected to a master host... + assertTrue(testConnG.isReadOnly()); // ... but the connection is read-only because that's how it was previously set. + testConnG.setAutoCommit(false); + + testConnG.close(); + } + + private void testBug22678872CheckConnectionsHistory(String... expectedConnectionsHistory) { + assertConnectionsHistory(expectedConnectionsHistory); + assertEquals(UnreliableSocketFactory.getHostsFromAllConnections().size(), expectedConnectionsHistory.length); + UnreliableSocketFactory.flushConnectionAttempts(); + } + + /** + * Tests fix for Bug#77649 - URL start with word "address",JDBC can't parse the "host:port" Correctly. + */ + public void testBug77649() throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + String[] hosts = new String[] { host, "address", "address.somewhere", "addressing", "addressing.somewhere" }; + + UnreliableSocketFactory.flushAllStaticData(); + for (int i = 1; i < hosts.length; i++) { // Don't map the first host. + UnreliableSocketFactory.mapHost(hosts[i], host); + } + + props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("socketFactory", UnreliableSocketFactory.class.getName()); + for (String h : hosts) { + getConnectionWithProps(String.format("jdbc:mysql://%s:%s", h, port), props).close(); + getConnectionWithProps(String.format("jdbc:mysql://address=(protocol=tcp)(host=%s)(port=%s)", h, port), props).close(); + } + } + + /** + * Tests fix for Bug#74711 - FORGOTTEN WORKAROUND FOR BUG#36326. + * + * This test requires a server started with the options '--query_cache_type=1' and '--query_cache_size=N', (N > 0). + */ + public void testBug74711() throws Exception { + this.rs = this.stmt.executeQuery("SELECT @@global.query_cache_type, @@global.query_cache_size"); + this.rs.next(); + if (!"ON".equalsIgnoreCase(this.rs.getString(1)) || "0".equals(this.rs.getString(2))) { + System.err + .println("Warning! testBug77411() requires a server started with the options '--query_cache_type=1' and '--query_cache_size=N', (N > 0)."); + return; + } + + boolean useLocTransSt = false; + boolean useElideSetAC = false; + do { + final String testCase = String.format("Case: [LocTransSt: %s, ElideAC: %s ]", useLocTransSt ? "Y" : "N", useElideSetAC ? "Y" : "N"); + final Properties props = new Properties(); + props.setProperty("useLocalTransactionState", Boolean.toString(useLocTransSt)); + props.setProperty("elideSetAutoCommits", Boolean.toString(useElideSetAC)); + Connection testConn = getConnectionWithProps(props); + + assertEquals(testCase, useLocTransSt, ((ConnectionProperties) testConn).getUseLocalTransactionState()); + assertEquals(testCase, useElideSetAC, ((ConnectionProperties) testConn).getElideSetAutoCommits()); + + testConn.close(); + } while ((useLocTransSt = !useLocTransSt) || (useElideSetAC = !useElideSetAC)); + } + + /** + * Tests fix for Bug#75209 - Set useLocalTransactionState may result in partially committed transaction. + */ + public void testBug75209() throws Exception { + createTable("testBug75209", "(id INT PRIMARY KEY)", "InnoDB"); + + boolean useLocTransSt = false; + do { + this.stmt.executeUpdate("TRUNCATE TABLE testBug75209"); + this.stmt.executeUpdate("INSERT INTO testBug75209 VALUES (1)"); + + final String testCase = String.format("Case: [LocTransSt: %s]", useLocTransSt ? "Y" : "N"); + + final Connection testConn = getConnectionWithProps("useLocalTransactionState=" + useLocTransSt); + testConn.setAutoCommit(false); + + final Statement testStmt = testConn.createStatement(); + try { + assertEquals(testCase, 1, testStmt.executeUpdate("INSERT INTO testBug75209 VALUES(2)")); + + // This triggers Duplicate-key exception + testStmt.executeUpdate("INSERT INTO testBug75209 VALUES(2)"); + fail(testCase + ": SQLException expected here!"); + } catch (Exception e) { + testConn.rollback(); + } + testStmt.close(); + + testConn.setAutoCommit(true); + testConn.close(); + + this.rs = this.stmt.executeQuery("SELECT COUNT(*) FROM testBug75209"); + assertTrue(this.rs.next()); + assertEquals(testCase, 1, this.rs.getInt(1)); + } while (useLocTransSt = !useLocTransSt); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/DataSourceRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/DataSourceRegressionTest.java new file mode 100644 index 0000000..91bbc94 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/DataSourceRegressionTest.java @@ -0,0 +1,617 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Hashtable; +import java.util.concurrent.Callable; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.DataSource; +import javax.sql.PooledConnection; +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker; +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlDataSourceFactory; +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXid; + +import testsuite.BaseTestCase; +import testsuite.simple.DataSourceTest; + +/** + * Tests fixes for bugs related to datasources. + */ +public class DataSourceRegressionTest extends BaseTestCase { + + public final static String DS_DATABASE_PROP_NAME = "com.mysql.jdbc.test.ds.db"; + + public final static String DS_HOST_PROP_NAME = "com.mysql.jdbc.test.ds.host"; + + public final static String DS_PASSWORD_PROP_NAME = "com.mysql.jdbc.test.ds.password"; + + public final static String DS_PORT_PROP_NAME = "com.mysql.jdbc.test.ds.port"; + + public final static String DS_USER_PROP_NAME = "com.mysql.jdbc.test.ds.user"; + + private Context ctx; + + private File tempDir; + + /** + * Creates a new DataSourceRegressionTest suite for the given test name + * + * @param name + * the name of the testcase to run. + */ + public DataSourceRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(DataSourceTest.class); + } + + /** + * Sets up this test, calling registerDataSource() to bind a DataSource into + * JNDI, using the FSContext JNDI provider from Sun + * + * @throws Exception + * if an error occurs. + */ + @Override + public void setUp() throws Exception { + super.setUp(); + createJNDIContext(); + } + + /** + * Un-binds the DataSource, and cleans up the filesystem + * + * @throws Exception + * if an error occurs + */ + @Override + public void tearDown() throws Exception { + this.ctx.unbind(this.tempDir.getAbsolutePath() + "/test"); + this.ctx.unbind(this.tempDir.getAbsolutePath() + "/testNoUrl"); + this.ctx.close(); + this.tempDir.delete(); + + super.tearDown(); + } + + /** + * Tests fix for BUG#4808- Calling .close() twice on a PooledConnection + * causes NPE. + * + * @throws Exception + * if an error occurs. + */ + public void testBug4808() throws Exception { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + ds.setURL(BaseTestCase.dbUrl); + PooledConnection closeMeTwice = ds.getPooledConnection(); + closeMeTwice.close(); + closeMeTwice.close(); + + } + + /** + * Tests fix for Bug#3848, port # alone parsed incorrectly + * + * @throws Exception + * ... + */ + public void testBug3848() throws Exception { + String jndiName = "/testBug3848"; + + String databaseName = System.getProperty(DS_DATABASE_PROP_NAME); + String userName = System.getProperty(DS_USER_PROP_NAME); + String password = System.getProperty(DS_PASSWORD_PROP_NAME); + String port = System.getProperty(DS_PORT_PROP_NAME); + + // Only run this test if at least one of the above are set + if ((databaseName != null) || (userName != null) || (password != null) || (port != null)) { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + + if (databaseName != null) { + ds.setDatabaseName(databaseName); + } + + if (userName != null) { + ds.setUser(userName); + } + + if (password != null) { + ds.setPassword(password); + } + + if (port != null) { + ds.setPortNumber(Integer.parseInt(port)); + } + + bindDataSource(jndiName, ds); + + ConnectionPoolDataSource boundDs = null; + + try { + boundDs = (ConnectionPoolDataSource) lookupDatasourceInJNDI(jndiName); + + assertTrue("Datasource not bound", boundDs != null); + + Connection dsConn = null; + + try { + dsConn = boundDs.getPooledConnection().getConnection(); + } finally { + if (dsConn != null) { + dsConn.close(); + } + } + } finally { + if (boundDs != null) { + this.ctx.unbind(jndiName); + } + } + } + } + + /** + * Tests that we can get a connection from the DataSource bound in JNDI + * during test setup + * + * @throws Exception + * if an error occurs + */ + public void testBug3920() throws Exception { + String jndiName = "/testBug3920"; + + String databaseName = System.getProperty(DS_DATABASE_PROP_NAME); + String userName = System.getProperty(DS_USER_PROP_NAME); + String password = System.getProperty(DS_PASSWORD_PROP_NAME); + String port = System.getProperty(DS_PORT_PROP_NAME); + String serverName = System.getProperty(DS_HOST_PROP_NAME); + + // Only run this test if at least one of the above are set + if ((databaseName != null) || (serverName != null) || (userName != null) || (password != null) || (port != null)) { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + + if (databaseName != null) { + ds.setDatabaseName(databaseName); + } + + if (userName != null) { + ds.setUser(userName); + } + + if (password != null) { + ds.setPassword(password); + } + + if (port != null) { + ds.setPortNumber(Integer.parseInt(port)); + } + + if (serverName != null) { + ds.setServerName(serverName); + } + + bindDataSource(jndiName, ds); + + ConnectionPoolDataSource boundDs = null; + + try { + boundDs = (ConnectionPoolDataSource) lookupDatasourceInJNDI(jndiName); + + assertTrue("Datasource not bound", boundDs != null); + + Connection dsCon = null; + Statement dsStmt = null; + + try { + dsCon = boundDs.getPooledConnection().getConnection(); + dsStmt = dsCon.createStatement(); + dsStmt.executeUpdate("DROP TABLE IF EXISTS testBug3920"); + dsStmt.executeUpdate("CREATE TABLE testBug3920 (field1 varchar(32))"); + + assertTrue("Connection can not be obtained from data source", dsCon != null); + } finally { + if (dsStmt != null) { + dsStmt.executeUpdate("DROP TABLE IF EXISTS testBug3920"); + + dsStmt.close(); + } + if (dsCon != null) { + dsCon.close(); + } + } + } finally { + if (boundDs != null) { + this.ctx.unbind(jndiName); + } + } + } + } + + /** + * Tests fix for BUG#19169 - ConnectionProperties (and thus some + * subclasses) are not serializable, even though some J2EE containers + * expect them to be. + * + * @throws Exception + * if the test fails. + */ + public void testBug19169() throws Exception { + MysqlDataSource toSerialize = new MysqlDataSource(); + toSerialize.setZeroDateTimeBehavior("convertToNull"); + + boolean testBooleanFlag = !toSerialize.getAllowLoadLocalInfile(); + toSerialize.setAllowLoadLocalInfile(testBooleanFlag); + + int testIntFlag = toSerialize.getBlobSendChunkSize() + 1; + toSerialize.setBlobSendChunkSize(String.valueOf(testIntFlag)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream objOut = new ObjectOutputStream(bOut); + objOut.writeObject(toSerialize); + objOut.flush(); + + ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + MysqlDataSource thawedDs = (MysqlDataSource) objIn.readObject(); + + assertEquals("convertToNull", thawedDs.getZeroDateTimeBehavior()); + assertEquals(testBooleanFlag, thawedDs.getAllowLoadLocalInfile()); + assertEquals(testIntFlag, thawedDs.getBlobSendChunkSize()); + } + + /** + * Tests fix for BUG#20242 - MysqlValidConnectionChecker for JBoss doesn't + * work with MySQLXADataSources. + * + * @throws Exception + * if the test fails. + */ + public void testBug20242() throws Exception { + if (versionMeetsMinimum(5, 0)) { + try { + Class.forName("org.jboss.resource.adapter.jdbc.ValidConnectionChecker"); + } catch (Exception ex) { + System.out.println("The testBug20242() is ignored because required class isn't available:"); + ex.printStackTrace(); + return; // class not available for testing + } + + MysqlXADataSource xaDs = new MysqlXADataSource(); + xaDs.setUrl(dbUrl); + + MysqlValidConnectionChecker checker = new MysqlValidConnectionChecker(); + assertNull(checker.isValidConnection(xaDs.getXAConnection().getConnection())); + } + } + + private void bindDataSource(String name, DataSource ds) throws Exception { + this.ctx.bind(this.tempDir.getAbsolutePath() + name, ds); + } + + /** + * This method is separated from the rest of the example since you normally + * would NOT register a JDBC driver in your code. It would likely be + * configered into your naming and directory service using some GUI. + * + * @throws Exception + * if an error occurs + */ + private void createJNDIContext() throws Exception { + this.tempDir = File.createTempFile("jnditest", null); + this.tempDir.delete(); + this.tempDir.mkdir(); + this.tempDir.deleteOnExit(); + + MysqlConnectionPoolDataSource ds; + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); + this.ctx = new InitialContext(env); + assertTrue("Naming Context not created", this.ctx != null); + ds = new MysqlConnectionPoolDataSource(); + ds.setUrl(dbUrl); // from BaseTestCase + ds.setDatabaseName("test"); + this.ctx.bind(this.tempDir.getAbsolutePath() + "/test", ds); + } + + private DataSource lookupDatasourceInJNDI(String jndiName) throws Exception { + NameParser nameParser = this.ctx.getNameParser(""); + Name datasourceName = nameParser.parse(this.tempDir.getAbsolutePath() + jndiName); + Object obj = this.ctx.lookup(datasourceName); + DataSource boundDs = null; + + if (obj instanceof DataSource) { + boundDs = (DataSource) obj; + } else if (obj instanceof Reference) { + // + // For some reason, this comes back as a Reference instance under CruiseControl !? + // + Reference objAsRef = (Reference) obj; + ObjectFactory factory = (ObjectFactory) Class.forName(objAsRef.getFactoryClassName()).newInstance(); + boundDs = (DataSource) factory.getObjectInstance(objAsRef, datasourceName, this.ctx, new Hashtable()); + } + + return boundDs; + } + + public void testCSC4616() throws Exception { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + ds.setURL(BaseTestCase.dbUrl); + PooledConnection pooledConn = ds.getPooledConnection(); + Connection physConn = pooledConn.getConnection(); + Statement physStatement = physConn.createStatement(); + + Method enableStreamingResultsMethodStmt = Class.forName("com.mysql.jdbc.jdbc2.optional.StatementWrapper").getMethod("enableStreamingResults", + new Class[0]); + enableStreamingResultsMethodStmt.invoke(physStatement, (Object[]) null); + this.rs = physStatement.executeQuery("SELECT 1"); + + try { + this.rs = physConn.createStatement().executeQuery("SELECT 2"); + fail("Should have caught a streaming exception here"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage() != null && sqlEx.getMessage().indexOf("Streaming") != -1); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + + PreparedStatement physPrepStmt = physConn.prepareStatement("SELECT 1"); + Method enableStreamingResultsMethodPstmt = Class.forName("com.mysql.jdbc.jdbc2.optional.PreparedStatementWrapper").getMethod("enableStreamingResults", + (Class[]) null); + enableStreamingResultsMethodPstmt.invoke(physPrepStmt, (Object[]) null); + + this.rs = physPrepStmt.executeQuery(); + + try { + this.rs = physConn.createStatement().executeQuery("SELECT 2"); + fail("Should have caught a streaming exception here"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage() != null && sqlEx.getMessage().indexOf("Streaming") != -1); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + } + + /** + * Tests fix for BUG#16791 - NullPointerException in MysqlDataSourceFactory + * due to Reference containing RefAddrs with null content. + * + * @throws Exception + * if the test fails + */ + public void testBug16791() throws Exception { + MysqlDataSource myDs = new MysqlDataSource(); + myDs.setUrl(dbUrl); + Reference asRef = myDs.getReference(); + System.out.println(asRef); + + removeFromRef(asRef, "port"); + removeFromRef(asRef, NonRegisteringDriver.USER_PROPERTY_KEY); + removeFromRef(asRef, NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + removeFromRef(asRef, "serverName"); + removeFromRef(asRef, "databaseName"); + + //MysqlDataSource newDs = (MysqlDataSource) + new MysqlDataSourceFactory().getObjectInstance(asRef, null, null, null); + } + + private void removeFromRef(Reference ref, String key) { + int size = ref.size(); + + for (int i = 0; i < size; i++) { + RefAddr refAddr = ref.get(i); + if (refAddr.getType().equals(key)) { + ref.remove(i); + break; + } + } + } + + /** + * Tests fix for BUG#32101 - When using a connection from our ConnectionPoolDataSource, + * some Connection.prepareStatement() methods would return null instead of + * a prepared statement. + * + * @throws Exception + */ + public void testBug32101() throws Exception { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + ds.setURL(BaseTestCase.dbUrl); + PooledConnection pc = ds.getPooledConnection(); + assertNotNull(pc.getConnection().prepareStatement("SELECT 1")); + assertNotNull(pc.getConnection().prepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS)); + assertNotNull(pc.getConnection().prepareStatement("SELECT 1", new int[0])); + assertNotNull(pc.getConnection().prepareStatement("SELECT 1", new String[0])); + assertNotNull(pc.getConnection().prepareStatement("SELECT 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); + assertNotNull( + pc.getConnection().prepareStatement("SELECT 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT)); + } + + public void testBug35810() throws Exception { + int defaultConnectTimeout = ((ConnectionProperties) this.conn).getConnectTimeout(); + int nonDefaultConnectTimeout = defaultConnectTimeout + 1000 * 2; + MysqlConnectionPoolDataSource cpds = new MysqlConnectionPoolDataSource(); + String dsUrl = BaseTestCase.dbUrl; + if (dsUrl.indexOf("?") == -1) { + dsUrl += "?"; + } else { + dsUrl += "&"; + } + + dsUrl += "connectTimeout=" + nonDefaultConnectTimeout; + cpds.setUrl(dsUrl); + + Connection dsConn = cpds.getPooledConnection().getConnection(); + int configuredConnectTimeout = ((ConnectionProperties) dsConn).getConnectTimeout(); + + assertEquals("Connect timeout spec'd by URL didn't take", nonDefaultConnectTimeout, configuredConnectTimeout); + assertFalse("Connect timeout spec'd by URL didn't take", defaultConnectTimeout == configuredConnectTimeout); + } + + public void testBug42267() throws Exception { + MysqlDataSource ds = new MysqlDataSource(); + ds.setUrl(dbUrl); + Connection c = ds.getConnection(); + String query = "select 1,2,345"; + PreparedStatement ps = c.prepareStatement(query); + String psString = ps.toString(); + assertTrue("String representation of wrapped ps should contain query string", psString.endsWith(": " + query)); + ps.close(); + ps.toString(); + c.close(); + } + + /** + * Tests fix for BUG#72890 - Java jdbc driver returns incorrect return code when it's part of XA transaction + * + * @throws Exception + * if the test fails. + */ + public void testBug72890() throws Exception { + MysqlXADataSource myDs = new MysqlXADataSource(); + myDs.setUrl(BaseTestCase.dbUrl); + + try { + final Xid xid = new MysqlXid("72890".getBytes(), "72890".getBytes(), 1); + + final XAConnection xaConn = myDs.getXAConnection(); + final XAResource xaRes = xaConn.getXAResource(); + final Connection dbConn = xaConn.getConnection(); + final long connId = ((MySQLConnection) ((com.mysql.jdbc.Connection) dbConn).getConnectionMutex()).getId(); + + xaRes.start(xid, XAResource.TMNOFLAGS); + xaRes.end(xid, XAResource.TMSUCCESS); + assertEquals(XAResource.XA_OK, xaRes.prepare(xid)); + + // Simulate a connection hang and make sure the connection really dies. + this.stmt.execute("KILL CONNECTION " + connId); + int connAliveChecks = 4; + while (connAliveChecks > 0) { + this.rs = this.stmt.executeQuery("SHOW PROCESSLIST"); + boolean connIsAlive = false; + while (!connIsAlive && this.rs.next()) { + connIsAlive = this.rs.getInt(1) == connId; + } + this.rs.close(); + if (connIsAlive) { + connAliveChecks--; + System.out.println("Connection id " + connId + " is still alive. Checking " + connAliveChecks + " more times."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + } else { + connAliveChecks = -1; + } + } + if (connAliveChecks == 0) { + fail("Failed to kill the Connection id " + connId + " in a timely manner."); + } + + XAException xaEx = assertThrows(XAException.class, "Undetermined error occurred in the underlying Connection - check your data for consistency", + new Callable() { + public Void call() throws Exception { + xaRes.commit(xid, false); + return null; + } + }); + assertEquals("XAException error code", XAException.XAER_RMFAIL, xaEx.errorCode); + + dbConn.close(); + xaConn.close(); + + } finally { + /* + * After MySQL 5.7.7 a prepared XA transaction is no longer rolled back at disconnect. It needs to be rolled back manually to prevent test failures + * in subsequent runs. + * Other MySQL versions won't have any transactions to recover. + */ + final XAConnection xaConnRecovery = myDs.getXAConnection(); + final XAResource xaResRecovery = xaConnRecovery.getXAResource(); + + final Xid[] xidsToRecover = xaResRecovery.recover(XAResource.TMSTARTRSCAN); + for (Xid xidToRecover : xidsToRecover) { + xaResRecovery.rollback(xidToRecover); + } + + xaConnRecovery.close(); + } + } + + /** + * Tests fix for Bug#72632 - NullPointerException for invalid JDBC URL. + */ + public void testBug72632() throws Exception { + final MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl("bad-connection-string"); + assertThrows(SQLException.class, "Failed to get a connection using the URL 'bad-connection-string'.", new Callable() { + public Void call() throws Exception { + dataSource.getConnection(); + return null; + } + }); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/EscapeProcessorRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/EscapeProcessorRegressionTest.java new file mode 100644 index 0000000..1ae4709 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/EscapeProcessorRegressionTest.java @@ -0,0 +1,152 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.sql.Connection; +import java.util.Properties; +import java.util.TimeZone; + +import testsuite.BaseTestCase; + +/** + * Tests regressions w/ the Escape Processor code. + */ +public class EscapeProcessorRegressionTest extends BaseTestCase { + + public EscapeProcessorRegressionTest(String name) { + super(name); + } + + /** + * Tests fix for BUG#11797 - Escape tokenizer doesn't respect stacked single + * quotes for escapes. + * + * @throws Exception + * if the test fails. + */ + public void testBug11797() throws Exception { + assertEquals("select 'ESCAPED BY ''\\'' ON {tbl_name | * | *.* | db_name.*}'", + this.conn.nativeSQL("select 'ESCAPED BY ''\\'' ON {tbl_name | * | *.* | db_name.*}'")); + } + + /** + * Tests fix for BUG#11498 - Escape processor didn't honor strings + * demarcated with double quotes. + * + * @throws Exception + * if the test fails. + */ + public void testBug11498() throws Exception { + assertEquals( + "replace into t1 (id, f1, f4) VALUES(1,\"\",\"tko { zna gdje se sakrio\"),(2,\"a\",\"sedmi { kontinentio\"),(3,\"a\",\"a } cigov si ti?\")", + this.conn.nativeSQL( + "replace into t1 (id, f1, f4) VALUES(1,\"\",\"tko { zna gdje se sakrio\"),(2,\"a\",\"sedmi { kontinentio\"),(3,\"a\",\"a } cigov si ti?\")")); + + } + + /** + * Tests fix for BUG#14909 - escape processor replaces quote character in + * quoted string with string delimiter. + * + * @throws Exception + */ + public void testBug14909() throws Exception { + assertEquals("select '{\"','}'", this.conn.nativeSQL("select '{\"','}'")); + } + + /** + * Tests fix for BUG#25399 - EscapeProcessor gets confused by multiple backslashes + * + * @throws Exception + * if the test fails. + */ + public void testBug25399() throws Exception { + assertEquals("\\' {d}", getSingleValueWithQuery("SELECT '\\\\\\' {d}'")); + } + + /** + * Tests fix for BUG#63526 - Unhandled case of {data...} + * + * @throws Exception + * if the test fails. + */ + public void testBug63526() throws Exception { + createTable("bug63526", "(`{123}` INT UNSIGNED NOT NULL)", "INNODB"); + } + + /** + * Tests fix for BUG#60598 - nativeSQL() truncates fractional seconds + * + * @throws Exception + */ + public void testBug60598() throws Exception { + + String expected = versionMeetsMinimum(5, 6, 4) ? "SELECT '2001-02-03 04:05:06' , '2001-02-03 04:05:06.007' , '11:22:33.444'" + : "SELECT '2001-02-03 04:05:06' , '2001-02-03 04:05:06' , '11:22:33'"; + + Connection conn_nolegacy = null; + Connection conn_legacy = null; + Connection conn_legacy_tz = null; + + try { + Properties props = new Properties(); + + props.setProperty("serverTimezone", TimeZone.getDefault().getID() + ""); + props.setProperty("useLegacyDatetimeCode", "false"); + conn_nolegacy = getConnectionWithProps(props); + + props.setProperty("useLegacyDatetimeCode", "true"); + conn_legacy = getConnectionWithProps(props); + + props.setProperty("useLegacyDatetimeCode", "true"); + props.setProperty("useTimezone", "true"); + props.setProperty("useJDBCCompliantTimezoneShift", "true"); + conn_legacy_tz = getConnectionWithProps(props); + + String input = "SELECT {ts '2001-02-03 04:05:06' } , {ts '2001-02-03 04:05:06.007' } , {t '11:22:33.444' }"; + + String output = conn_nolegacy.nativeSQL(input); + assertEquals(expected, output); + + output = conn_legacy.nativeSQL(input); + assertEquals(expected, output); + + output = conn_legacy_tz.nativeSQL(input); + assertEquals(expected, output); + + } finally { + if (conn_nolegacy != null) { + conn_nolegacy.close(); + } + if (conn_legacy != null) { + conn_legacy.close(); + } + if (conn_legacy_tz != null) { + conn_legacy_tz.close(); + } + } + + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/MetaDataRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/MetaDataRegressionTest.java new file mode 100644 index 0000000..9c8bfdd --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/MetaDataRegressionTest.java @@ -0,0 +1,4229 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.Driver; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; + +import testsuite.BaseStatementInterceptor; +import testsuite.BaseTestCase; + +import junit.framework.ComparisonFailure; + +/** + * Regression tests for DatabaseMetaData + */ +public class MetaDataRegressionTest extends BaseTestCase { + /** + * Creates a new MetaDataRegressionTest. + * + * @param name + * the name of the test + */ + public MetaDataRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MetaDataRegressionTest.class); + } + + public void testBug2607() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2607"); + this.stmt.executeUpdate("CREATE TABLE testBug2607 (field1 INT PRIMARY KEY)"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug2607"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertTrue(!rsmd.isAutoIncrement(1)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2607"); + } + } + + /** + * Tests fix for BUG#2852, where RSMD is not returning correct (or matching) + * types for TINYINT and SMALLINT. + * + * @throws Exception + * if the test fails. + */ + public void testBug2852() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2852"); + this.stmt.executeUpdate("CREATE TABLE testBug2852 (field1 TINYINT, field2 SMALLINT)"); + this.stmt.executeUpdate("INSERT INTO testBug2852 VALUES (1,1)"); + + this.rs = this.stmt.executeQuery("SELECT * from testBug2852"); + + assertTrue(this.rs.next()); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertTrue(rsmd.getColumnClassName(1).equals(this.rs.getObject(1).getClass().getName())); + assertTrue("java.lang.Integer".equals(rsmd.getColumnClassName(1))); + + assertTrue(rsmd.getColumnClassName(2).equals(this.rs.getObject(2).getClass().getName())); + assertTrue("java.lang.Integer".equals(rsmd.getColumnClassName(2))); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2852"); + } + } + + /** + * Tests fix for BUG#2855, where RSMD is not returning correct (or matching) + * types for FLOAT. + * + * @throws Exception + * if the test fails. + */ + public void testBug2855() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2855"); + this.stmt.executeUpdate("CREATE TABLE testBug2855 (field1 FLOAT)"); + this.stmt.executeUpdate("INSERT INTO testBug2855 VALUES (1)"); + + this.rs = this.stmt.executeQuery("SELECT * from testBug2855"); + + assertTrue(this.rs.next()); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertTrue(rsmd.getColumnClassName(1).equals(this.rs.getObject(1).getClass().getName())); + assertTrue("java.lang.Float".equals(rsmd.getColumnClassName(1))); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2855"); + } + } + + /** + * Tests fix for BUG#3570 -- inconsistent reporting of column type + * + * @throws Exception + * if an error occurs + */ + public void testBug3570() throws Exception { + String createTableQuery = " CREATE TABLE testBug3570(field_tinyint TINYINT,field_smallint SMALLINT,field_mediumint MEDIUMINT" + + ",field_int INT,field_integer INTEGER,field_bigint BIGINT,field_real REAL,field_float FLOAT,field_decimal DECIMAL" + + ",field_numeric NUMERIC,field_double DOUBLE,field_char CHAR(3),field_varchar VARCHAR(255),field_date DATE" + + ",field_time TIME,field_year YEAR,field_timestamp TIMESTAMP,field_datetime DATETIME,field_tinyblob TINYBLOB" + + ",field_blob BLOB,field_mediumblob MEDIUMBLOB,field_longblob LONGBLOB,field_tinytext TINYTEXT,field_text TEXT" + + ",field_mediumtext MEDIUMTEXT,field_longtext LONGTEXT,field_enum ENUM('1','2','3'),field_set SET('1','2','3'))"; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3570"); + this.stmt.executeUpdate(createTableQuery); + + ResultSet dbmdRs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testBug3570", "%"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testBug3570"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + while (dbmdRs.next()) { + String columnName = dbmdRs.getString(4); + int typeFromGetColumns = dbmdRs.getInt(5); + int typeFromRSMD = rsmd.getColumnType(this.rs.findColumn(columnName)); + + // + // TODO: Server needs to send these types correctly.... + // + if (!"field_tinyblob".equals(columnName) && !"field_tinytext".equals(columnName)) { + assertTrue(columnName + " -> type from DBMD.getColumns(" + typeFromGetColumns + ") != type from RSMD.getColumnType(" + typeFromRSMD + ")", + typeFromGetColumns == typeFromRSMD); + } + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3570"); + } + } + + /** + * Tests char/varchar bug + * + * @throws Exception + * if any errors occur + */ + public void testCharVarchar() throws Exception { + try { + this.stmt.execute("DROP TABLE IF EXISTS charVarCharTest"); + this.stmt.execute("CREATE TABLE charVarCharTest ( TableName VARCHAR(64), FieldName VARCHAR(64), NextCounter INTEGER);"); + + String query = "SELECT TableName, FieldName, NextCounter FROM charVarCharTest"; + this.rs = this.stmt.executeQuery(query); + + ResultSetMetaData rsmeta = this.rs.getMetaData(); + + assertTrue(rsmeta.getColumnTypeName(1).equalsIgnoreCase("VARCHAR")); + + // is "CHAR", expected "VARCHAR" + assertTrue(rsmeta.getColumnType(1) == 12); + + // is 1 (java.sql.Types.CHAR), expected 12 (java.sql.Types.VARCHAR) + } finally { + this.stmt.execute("DROP TABLE IF EXISTS charVarCharTest"); + } + } + + /** + * Tests fix for BUG#1673, where DatabaseMetaData.getColumns() is not + * returning correct column ordinal info for non '%' column name patterns. + * + * @throws Exception + * if the test fails for any reason + */ + public void testFixForBug1673() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1673"); + this.stmt.executeUpdate("CREATE TABLE testBug1673 (field_1 INT, field_2 INT)"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + int ordinalPosOfCol2Full = 0; + + this.rs = dbmd.getColumns(this.conn.getCatalog(), null, "testBug1673", null); + + while (this.rs.next()) { + if (this.rs.getString(4).equals("field_2")) { + ordinalPosOfCol2Full = this.rs.getInt(17); + } + } + + int ordinalPosOfCol2Scoped = 0; + + this.rs = dbmd.getColumns(this.conn.getCatalog(), null, "testBug1673", "field_2"); + + while (this.rs.next()) { + if (this.rs.getString(4).equals("field_2")) { + ordinalPosOfCol2Scoped = this.rs.getInt(17); + } + } + + assertTrue("Ordinal position in full column list of '" + ordinalPosOfCol2Full + "' != ordinal position in pattern search, '" + + ordinalPosOfCol2Scoped + "'.", + (ordinalPosOfCol2Full != 0) && (ordinalPosOfCol2Scoped != 0) && (ordinalPosOfCol2Scoped == ordinalPosOfCol2Full)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1673"); + } + } + + /** + * Tests bug reported by OpenOffice team with getColumns and LONGBLOB + * + * @throws Exception + * if any errors occur + */ + public void testGetColumns() throws Exception { + try { + this.stmt.execute("CREATE TABLE IF NOT EXISTS longblob_regress(field_1 longblob)"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + ResultSet dbmdRs = null; + + try { + dbmdRs = dbmd.getColumns("", "", "longblob_regress", "%"); + + while (dbmdRs.next()) { + dbmdRs.getInt(7); + } + } finally { + if (dbmdRs != null) { + try { + dbmdRs.close(); + } catch (SQLException ex) { + } + } + } + } finally { + this.stmt.execute("DROP TABLE IF EXISTS longblob_regress"); + } + } + + /** + * Tests fix for Bug# + * + * @throws Exception + * if an error occurs + */ + public void testGetColumnsBug1099() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetColumnsBug1099"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getTypeInfo(); + + StringBuilder types = new StringBuilder(); + + HashMap alreadyDoneTypes = new HashMap(); + + while (this.rs.next()) { + String typeName = this.rs.getString("TYPE_NAME"); + //String createParams = this.rs.getString("CREATE_PARAMS"); + + if ((typeName.indexOf("BINARY") == -1) && !typeName.equals("LONG VARCHAR")) { + if (!alreadyDoneTypes.containsKey(typeName)) { + alreadyDoneTypes.put(typeName, null); + + if (types.length() != 0) { + types.append(", \n"); + } + + int typeNameLength = typeName.length(); + StringBuilder safeTypeName = new StringBuilder(typeNameLength); + + for (int i = 0; i < typeNameLength; i++) { + char c = typeName.charAt(i); + + if (Character.isWhitespace(c)) { + safeTypeName.append("_"); + } else { + safeTypeName.append(c); + } + } + + types.append(safeTypeName.toString()); + types.append("Column "); + types.append(typeName); + + if (typeName.indexOf("CHAR") != -1) { + types.append(" (1)"); + } else if (typeName.equalsIgnoreCase("enum") || typeName.equalsIgnoreCase("set")) { + types.append("('a', 'b', 'c')"); + } + } + } + } + + this.stmt.executeUpdate("CREATE TABLE testGetColumnsBug1099(" + types.toString() + ")"); + + dbmd.getColumns(null, this.conn.getCatalog(), "testGetColumnsBug1099", "%"); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetColumnsBug1099"); + } + } + + /** + * Tests whether or not unsigned columns are reported correctly in + * DBMD.getColumns + * + * @throws Exception + */ + public void testGetColumnsUnsigned() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetUnsignedCols"); + this.stmt.executeUpdate("CREATE TABLE testGetUnsignedCols (field1 BIGINT, field2 BIGINT UNSIGNED)"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getColumns(this.conn.getCatalog(), null, "testGetUnsignedCols", "%"); + + assertTrue(this.rs.next()); + // This row doesn't have 'unsigned' attribute + assertTrue(this.rs.next()); + assertTrue(this.rs.getString(6).toLowerCase().indexOf("unsigned") != -1); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetUnsignedCols"); + } + } + + /** + * Tests whether bogus parameters break Driver.getPropertyInfo(). + * + * @throws Exception + * if an error occurs. + */ + public void testGetPropertyInfo() throws Exception { + new Driver().getPropertyInfo("", null); + } + + /** + * Tests whether ResultSetMetaData returns correct info for CHAR/VARCHAR + * columns. + * + * @throws Exception + * if the test fails + */ + public void testIsCaseSensitive() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitive"); + this.stmt.executeUpdate( + "CREATE TABLE testIsCaseSensitive (bin_char CHAR(1) BINARY, bin_varchar VARCHAR(64) BINARY, ci_char CHAR(1), ci_varchar VARCHAR(64))"); + this.rs = this.stmt.executeQuery("SELECT bin_char, bin_varchar, ci_char, ci_varchar FROM testIsCaseSensitive"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertTrue(rsmd.isCaseSensitive(1)); + assertTrue(rsmd.isCaseSensitive(2)); + assertTrue(!rsmd.isCaseSensitive(3)); + assertTrue(!rsmd.isCaseSensitive(4)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitive"); + } + + if (versionMeetsMinimum(4, 1)) { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitiveCs"); + this.stmt.executeUpdate("CREATE TABLE testIsCaseSensitiveCs (bin_char CHAR(1) CHARACTER SET latin1 COLLATE latin1_general_cs," + + "bin_varchar VARCHAR(64) CHARACTER SET latin1 COLLATE latin1_general_cs," + + "ci_char CHAR(1) CHARACTER SET latin1 COLLATE latin1_general_ci," + + "ci_varchar VARCHAR(64) CHARACTER SET latin1 COLLATE latin1_general_ci, " + + "bin_tinytext TINYTEXT CHARACTER SET latin1 COLLATE latin1_general_cs," + + "bin_text TEXT CHARACTER SET latin1 COLLATE latin1_general_cs," + + "bin_med_text MEDIUMTEXT CHARACTER SET latin1 COLLATE latin1_general_cs," + + "bin_long_text LONGTEXT CHARACTER SET latin1 COLLATE latin1_general_cs," + + "ci_tinytext TINYTEXT CHARACTER SET latin1 COLLATE latin1_general_ci," + + "ci_text TEXT CHARACTER SET latin1 COLLATE latin1_general_ci," + + "ci_med_text MEDIUMTEXT CHARACTER SET latin1 COLLATE latin1_general_ci," + + "ci_long_text LONGTEXT CHARACTER SET latin1 COLLATE latin1_general_ci)"); + + this.rs = this.stmt.executeQuery("SELECT bin_char, bin_varchar, ci_char, ci_varchar, bin_tinytext, bin_text, bin_med_text, bin_long_text, " + + "ci_tinytext, ci_text, ci_med_text, ci_long_text FROM testIsCaseSensitiveCs"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertTrue(rsmd.isCaseSensitive(1)); + assertTrue(rsmd.isCaseSensitive(2)); + assertTrue(!rsmd.isCaseSensitive(3)); + assertTrue(!rsmd.isCaseSensitive(4)); + + assertTrue(rsmd.isCaseSensitive(5)); + assertTrue(rsmd.isCaseSensitive(6)); + assertTrue(rsmd.isCaseSensitive(7)); + assertTrue(rsmd.isCaseSensitive(8)); + + assertTrue(!rsmd.isCaseSensitive(9)); + assertTrue(!rsmd.isCaseSensitive(10)); + assertTrue(!rsmd.isCaseSensitive(11)); + assertTrue(!rsmd.isCaseSensitive(12)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitiveCs"); + } + } + } + + /** + * Tests whether or not DatabaseMetaData.getColumns() returns the correct + * java.sql.Types info. + * + * @throws Exception + * if the test fails. + */ + public void testLongText() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testLongText"); + this.stmt.executeUpdate("CREATE TABLE testLongText (field1 LONGTEXT)"); + + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testLongText", "%"); + + assertTrue(this.rs.next()); + + assertTrue(this.rs.getInt("DATA_TYPE") == java.sql.Types.LONGVARCHAR); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testLongText"); + } + } + + /** + * Tests for types being returned correctly + * + * @throws Exception + * if an error occurs. + */ + public void testTypes() throws Exception { + try { + this.stmt.execute("DROP TABLE IF EXISTS typesRegressTest"); + this.stmt.execute("CREATE TABLE typesRegressTest (varcharField VARCHAR(32), charField CHAR(2), enumField ENUM('1','2')," + + "setField SET('1','2','3'), tinyblobField TINYBLOB, mediumBlobField MEDIUMBLOB, longblobField LONGBLOB, blobField BLOB)"); + + this.rs = this.stmt.executeQuery("SELECT * from typesRegressTest"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + int numCols = rsmd.getColumnCount(); + + for (int i = 0; i < numCols; i++) { + String columnName = rsmd.getColumnName(i + 1); + String columnTypeName = rsmd.getColumnTypeName(i + 1); + System.out.println(columnName + " -> " + columnTypeName); + } + } finally { + this.stmt.execute("DROP TABLE IF EXISTS typesRegressTest"); + } + } + + /** + * Tests fix for BUG#4742, 'DOUBLE' mapped twice in getTypeInfo(). + * + * @throws Exception + * if the test fails. + */ + public void testBug4742() throws Exception { + HashMap clashMap = new HashMap(); + + this.rs = this.conn.getMetaData().getTypeInfo(); + + while (this.rs.next()) { + String name = this.rs.getString(1); + assertTrue("Type represented twice in type info, '" + name + "'.", !clashMap.containsKey(name)); + clashMap.put(name, name); + } + } + + /** + * Tests fix for BUG#4138, getColumns() returns incorrect JDBC type for + * unsigned columns. + * + * @throws Exception + * if the test fails. + */ + public void testBug4138() throws Exception { + try { + String[] typesToTest = new String[] { "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT", "FLOAT", "DOUBLE", "DECIMAL" }; + + short[] jdbcMapping = new short[] { Types.TINYINT, Types.SMALLINT, Types.INTEGER, Types.INTEGER, Types.BIGINT, Types.REAL, Types.DOUBLE, + Types.DECIMAL }; + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4138"); + + StringBuilder createBuf = new StringBuilder(); + + createBuf.append("CREATE TABLE testBug4138 ("); + + boolean firstColumn = true; + + for (int i = 0; i < typesToTest.length; i++) { + if (!firstColumn) { + createBuf.append(", "); + } else { + firstColumn = false; + } + + createBuf.append("field"); + createBuf.append((i + 1)); + createBuf.append(" "); + createBuf.append(typesToTest[i]); + createBuf.append(" UNSIGNED"); + } + createBuf.append(")"); + this.stmt.executeUpdate(createBuf.toString()); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getColumns(this.conn.getCatalog(), null, "testBug4138", "field%"); + + assertTrue(this.rs.next()); + + for (int i = 0; i < typesToTest.length; i++) { + assertTrue( + "JDBC Data Type of " + this.rs.getShort("DATA_TYPE") + " for MySQL type '" + this.rs.getString("TYPE_NAME") + + "' from 'DATA_TYPE' column does not match expected value of " + jdbcMapping[i] + ".", + jdbcMapping[i] == this.rs.getShort("DATA_TYPE")); + this.rs.next(); + } + + this.rs.close(); + + StringBuilder queryBuf = new StringBuilder("SELECT "); + firstColumn = true; + + for (int i = 0; i < typesToTest.length; i++) { + if (!firstColumn) { + queryBuf.append(", "); + } else { + firstColumn = false; + } + + queryBuf.append("field"); + queryBuf.append((i + 1)); + } + + queryBuf.append(" FROM testBug4138"); + + this.rs = this.stmt.executeQuery(queryBuf.toString()); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + for (int i = 0; i < typesToTest.length; i++) { + + assertTrue(jdbcMapping[i] == rsmd.getColumnType(i + 1)); + String desiredTypeName = typesToTest[i] + " unsigned"; + + assertTrue(rsmd.getColumnTypeName((i + 1)) + " != " + desiredTypeName, desiredTypeName.equalsIgnoreCase(rsmd.getColumnTypeName(i + 1))); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4138"); + } + } + + /** + * Here for housekeeping only, the test is actually in testBug4138(). + * + * @throws Exception + * if the test fails. + */ + public void testBug4860() throws Exception { + testBug4138(); + } + + /** + * Tests fix for BUG#4880 - RSMD.getPrecision() returns '0' for non-numeric + * types. + * + * Why-oh-why is this not in the spec, nor the api-docs, but in some + * 'optional' book, _and_ it is a variance from both ODBC and the ANSI SQL + * standard :p + * + * (from the CTS testsuite).... + * + * The getPrecision(int colindex) method returns an integer value + * representing the number of decimal digits for number types,maximum length + * in characters for character types,maximum length in bytes for JDBC binary + * datatypes. + * + * (See Section 27.3 of JDBC 2.0 API Reference & Tutorial 2nd edition) + * + * @throws Exception + * if the test fails. + */ + + public void testBug4880() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4880"); + this.stmt.executeUpdate("CREATE TABLE testBug4880 (field1 VARCHAR(80), field2 TINYBLOB, field3 BLOB, field4 MEDIUMBLOB, field5 LONGBLOB)"); + this.rs = this.stmt.executeQuery("SELECT field1, field2, field3, field4, field5 FROM testBug4880"); + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertEquals(80, rsmd.getPrecision(1)); + assertEquals(Types.VARCHAR, rsmd.getColumnType(1)); + assertEquals(80, rsmd.getColumnDisplaySize(1)); + + assertEquals(255, rsmd.getPrecision(2)); + assertEquals(Types.VARBINARY, rsmd.getColumnType(2)); + assertTrue("TINYBLOB".equalsIgnoreCase(rsmd.getColumnTypeName(2))); + assertEquals(255, rsmd.getColumnDisplaySize(2)); + + assertEquals(65535, rsmd.getPrecision(3)); + assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(3)); + assertTrue("BLOB".equalsIgnoreCase(rsmd.getColumnTypeName(3))); + assertEquals(65535, rsmd.getColumnDisplaySize(3)); + + assertEquals(16777215, rsmd.getPrecision(4)); + assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(4)); + assertTrue("MEDIUMBLOB".equalsIgnoreCase(rsmd.getColumnTypeName(4))); + assertEquals(16777215, rsmd.getColumnDisplaySize(4)); + + if (versionMeetsMinimum(4, 1)) { + // Server doesn't send us enough information to detect LONGBLOB + // type + assertEquals(Integer.MAX_VALUE, rsmd.getPrecision(5)); + assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(5)); + assertTrue("LONGBLOB".equalsIgnoreCase(rsmd.getColumnTypeName(5))); + assertEquals(Integer.MAX_VALUE, rsmd.getColumnDisplaySize(5)); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4880"); + } + } + + /** + * Tests fix for BUG#6399, ResultSetMetaData.getDisplaySize() is wrong for + * multi-byte charsets. + * + * @throws Exception + * if the test fails + */ + public void testBug6399() throws Exception { + if (versionMeetsMinimum(4, 1)) { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6399"); + this.stmt.executeUpdate( + "CREATE TABLE testBug6399 (field1 CHAR(3) CHARACTER SET UTF8, field2 CHAR(3) CHARACTER SET LATIN1, field3 CHAR(3) CHARACTER SET SJIS)"); + this.stmt.executeUpdate("INSERT INTO testBug6399 VALUES ('a', 'a', 'a')"); + + this.rs = this.stmt.executeQuery("SELECT field1, field2, field3 FROM testBug6399"); + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertEquals(3, rsmd.getColumnDisplaySize(1)); + assertEquals(3, rsmd.getColumnDisplaySize(2)); + assertEquals(3, rsmd.getColumnDisplaySize(3)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6399"); + } + } + } + + /** + * Tests fix for BUG#7081, DatabaseMetaData.getIndexInfo() ignoring 'unique' + * parameters. + * + * @throws Exception + * if the test fails. + */ + public void testBug7081() throws Exception { + String tableName = "testBug7081"; + + try { + createTable(tableName, "(field1 INT, INDEX(field1))"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null, tableName, true, false); + assertTrue(!this.rs.next()); // there should be no rows that meet + // this requirement + + this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null, tableName, false, false); + assertTrue(this.rs.next()); // there should be one row that meets + // this requirement + assertTrue(!this.rs.next()); + + } finally { + dropTable(tableName); + } + } + + /** + * Tests fix for BUG#7033 - PreparedStatements don't encode Big5 (and other + * multibyte) character sets correctly in static SQL strings. + * + * @throws Exception + * if the test fails. + */ + public void testBug7033() throws Exception { + if (!this.DISABLED_testBug7033) { + Connection big5Conn = null; + Statement big5Stmt = null; + PreparedStatement big5PrepStmt = null; + + String testString = "\u5957 \u9910"; + + try { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "Big5"); + + big5Conn = getConnectionWithProps(props); + big5Stmt = big5Conn.createStatement(); + + byte[] foobar = testString.getBytes("Big5"); + System.out.println(Arrays.toString(foobar)); + + this.rs = big5Stmt.executeQuery("select 1 as '\u5957 \u9910'"); + String retrString = this.rs.getMetaData().getColumnName(1); + assertTrue(testString.equals(retrString)); + + big5PrepStmt = big5Conn.prepareStatement("select 1 as '\u5957 \u9910'"); + this.rs = big5PrepStmt.executeQuery(); + retrString = this.rs.getMetaData().getColumnName(1); + assertTrue(testString.equals(retrString)); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + + if (big5Stmt != null) { + big5Stmt.close(); + + } + + if (big5PrepStmt != null) { + big5PrepStmt.close(); + } + + if (big5Conn != null) { + big5Conn.close(); + } + } + } + } + + /** + * Tests fix for Bug#8812, DBMD.getIndexInfo() returning inverted values for + * 'NON_UNIQUE' column. + * + * @throws Exception + * if the test fails. + */ + public void testBug8812() throws Exception { + String tableName = "testBug8812"; + + try { + createTable(tableName, "(field1 INT, field2 INT, INDEX(field1), UNIQUE INDEX(field2))"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null, tableName, true, false); + assertTrue(this.rs.next()); // there should be one row that meets + // this requirement + assertEquals(this.rs.getBoolean("NON_UNIQUE"), false); + + this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null, tableName, false, false); + assertTrue(this.rs.next()); // there should be two rows that meets + // this requirement + assertEquals(this.rs.getBoolean("NON_UNIQUE"), false); + assertTrue(this.rs.next()); + assertEquals(this.rs.getBoolean("NON_UNIQUE"), true); + + } finally { + dropTable(tableName); + } + } + + /** + * Tests fix for BUG#8800 - supportsMixedCase*Identifiers() returns wrong + * value on servers running on case-sensitive filesystems. + */ + + public void testBug8800() throws Exception { + assertEquals(((com.mysql.jdbc.Connection) this.conn).lowerCaseTableNames(), !this.conn.getMetaData().supportsMixedCaseIdentifiers()); + assertEquals(((com.mysql.jdbc.Connection) this.conn).lowerCaseTableNames(), !this.conn.getMetaData().supportsMixedCaseQuotedIdentifiers()); + + } + + /** + * Tests fix for BUG#8792 - DBMD.supportsResultSetConcurrency() not + * returning true for forward-only/read-only result sets (we obviously + * support this). + * + * @throws Exception + * if the test fails. + */ + public void testBug8792() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + assertTrue(dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); + + assertTrue(dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)); + + assertTrue(dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); + + assertTrue(dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)); + + assertTrue(!dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY)); + + assertTrue(!dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)); + + // Check error conditions + try { + dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, Integer.MIN_VALUE); + fail("Exception should've been raised for bogus concurrency value"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + try { + assertTrue(dbmd.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_INSENSITIVE, Integer.MIN_VALUE)); + fail("Exception should've been raised for bogus concurrency value"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + try { + assertTrue(dbmd.supportsResultSetConcurrency(Integer.MIN_VALUE, Integer.MIN_VALUE)); + fail("Exception should've been raised for bogus concurrency value"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + } + + /** + * Tests fix for BUG#8803, 'DATA_TYPE' column from + * DBMD.getBestRowIdentifier() causes ArrayIndexOutOfBoundsException when + * accessed (and in fact, didn't return any value). + * + * @throws Exception + * if the test fails. + */ + public void testBug8803() throws Exception { + String tableName = "testBug8803"; + createTable(tableName, "(field1 INT NOT NULL PRIMARY KEY)"); + DatabaseMetaData metadata = this.conn.getMetaData(); + try { + this.rs = metadata.getBestRowIdentifier(this.conn.getCatalog(), null, tableName, DatabaseMetaData.bestRowNotPseudo, true); + + assertTrue(this.rs.next()); + + this.rs.getInt("DATA_TYPE"); // **** Fails here ***** + } finally { + if (this.rs != null) { + this.rs.close(); + + this.rs = null; + } + } + + } + + /** + * Tests fix for BUG#9320 - PreparedStatement.getMetaData() inserts blank + * row in database under certain conditions when not using server-side + * prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug9320() throws Exception { + createTable("testBug9320", "(field1 int)"); + + testAbsenceOfMetadataForQuery("INSERT INTO testBug9320 VALUES (?)"); + testAbsenceOfMetadataForQuery("UPDATE testBug9320 SET field1=?"); + testAbsenceOfMetadataForQuery("DELETE FROM testBug9320 WHERE field1=?"); + } + + /** + * Tests fix for BUG#9778, DBMD.getTables() shouldn't return tables if views + * are asked for, even if the database version doesn't support views. + * + * @throws Exception + * if the test fails. + */ + public void testBug9778() throws Exception { + String tableName = "testBug9778"; + + try { + createTable(tableName, "(field1 int)"); + this.rs = this.conn.getMetaData().getTables(null, null, tableName, new String[] { "VIEW" }); + assertEquals(false, this.rs.next()); + + this.rs = this.conn.getMetaData().getTables(null, null, tableName, new String[] { "TABLE" }); + assertEquals(true, this.rs.next()); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + } + + /** + * Tests fix for BUG#9769 - Should accept null for procedureNamePattern, + * even though it isn't JDBC compliant, for legacy's sake. + * + * @throws Exception + * if the test fails. + */ + public void testBug9769() throws Exception { + boolean defaultPatternConfig = ((com.mysql.jdbc.Connection) this.conn).getNullNamePatternMatchesAll(); + + // We're going to change this in 3.2.x, so make that test here, so we + // catch it. + + if (this.conn.getMetaData().getDriverMajorVersion() == 3 && this.conn.getMetaData().getDriverMinorVersion() >= 2) { + assertEquals(false, defaultPatternConfig); + } else { + assertEquals(true, defaultPatternConfig); + } + + try { + this.conn.getMetaData().getProcedures(this.conn.getCatalog(), "%", null); + + if (!defaultPatternConfig) { + // we shouldn't have gotten here + fail("Exception should've been thrown"); + } + } catch (SQLException sqlEx) { + if (!defaultPatternConfig) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } else { + throw sqlEx; // we shouldn't have gotten an exception here + } + } + + // FIXME: TO test for 3.1.9 + // getColumns(); + // getTablePrivileges(); + // getTables(); + + } + + /** + * Tests fix for BUG#9917 - Should accept null for catalog in DBMD methods, + * even though it's not JDBC-compliant for legacy's sake. + * + * @throws Exception + * if the test fails. + */ + public void testBug9917() throws Exception { + String tableName = "testBug9917"; + boolean defaultCatalogConfig = ((com.mysql.jdbc.Connection) this.conn).getNullCatalogMeansCurrent(); + + // We're going to change this in 3.2.x, so make that test here, so we + // catch it. + + if (this.conn.getMetaData().getDriverMajorVersion() == 3 && this.conn.getMetaData().getDriverMinorVersion() >= 2) { + assertEquals(false, defaultCatalogConfig); + } else { + assertEquals(true, defaultCatalogConfig); + } + + try { + createTable(tableName, "(field1 int)"); + String currentCatalog = this.conn.getCatalog(); + + try { + this.rs = this.conn.getMetaData().getTables(null, null, tableName, new String[] { "TABLE" }); + + if (!defaultCatalogConfig) { + // we shouldn't have gotten here + fail("Exception should've been thrown"); + } + + assertEquals(true, this.rs.next()); + assertEquals(currentCatalog, this.rs.getString("TABLE_CAT")); + + // FIXME: Methods to test for 3.1.9 + // + // getBestRowIdentifier() + // getColumns() + // getCrossReference() + // getExportedKeys() + // getImportedKeys() + // getIndexInfo() + // getPrimaryKeys() + // getProcedures() + + } catch (SQLException sqlEx) { + if (!defaultCatalogConfig) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } else { + throw sqlEx; // we shouldn't have gotten an exception + // here + } + } + + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + } + + /** + * Tests fix for BUG#11575 -- DBMD.storesLower/Mixed/UpperIdentifiers() + * reports incorrect values for servers deployed on Windows. + * + * @throws Exception + * if the test fails. + */ + public void testBug11575() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + if (isServerRunningOnWindows()) { + assertEquals(true, dbmd.storesLowerCaseIdentifiers()); + assertEquals(true, dbmd.storesLowerCaseQuotedIdentifiers()); + assertEquals(false, dbmd.storesMixedCaseIdentifiers()); + assertEquals(false, dbmd.storesMixedCaseQuotedIdentifiers()); + assertEquals(false, dbmd.storesUpperCaseIdentifiers()); + assertEquals(true, dbmd.storesUpperCaseQuotedIdentifiers()); + } else { + assertEquals(false, dbmd.storesLowerCaseIdentifiers()); + assertEquals(false, dbmd.storesLowerCaseQuotedIdentifiers()); + assertEquals(true, dbmd.storesMixedCaseIdentifiers()); + assertEquals(true, dbmd.storesMixedCaseQuotedIdentifiers()); + assertEquals(false, dbmd.storesUpperCaseIdentifiers()); + assertEquals(true, dbmd.storesUpperCaseQuotedIdentifiers()); + } + } + + /** + * Tests fix for BUG#11781, foreign key information that is quoted is parsed + * incorrectly. + */ + public void testBug11781() throws Exception { + + if (versionMeetsMinimum(5, 1)) { + if (!versionMeetsMinimum(5, 2)) { + // server bug prevents this test from functioning + + return; + } + } + + createTable("`app tab`", "( C1 int(11) NULL, C2 int(11) NULL, INDEX NEWINX (C1), INDEX NEWINX2 (C1, C2))", "InnoDB"); + + this.stmt.executeUpdate("ALTER TABLE `app tab` ADD CONSTRAINT APPFK FOREIGN KEY (C1) REFERENCES `app tab` (C1)"); + + /* + * this.rs = this.conn.getMetaData().getCrossReference( + * this.conn.getCatalog(), null, "app tab", this.conn.getCatalog(), + * null, "app tab"); + */ + this.rs = ((com.mysql.jdbc.DatabaseMetaData) this.conn.getMetaData()).extractForeignKeyFromCreateTable(this.conn.getCatalog(), "app tab"); + assertTrue("must return a row", this.rs.next()); + + String catalog = this.conn.getCatalog(); + + assertEquals("comment; APPFK(`C1`) REFER `" + catalog + "`/ `app tab` (`C1`)", this.rs.getString(3)); + + this.rs.close(); + + this.rs = this.conn.getMetaData().getImportedKeys(this.conn.getCatalog(), null, "app tab"); + + assertTrue(this.rs.next()); + + this.rs = this.conn.getMetaData().getExportedKeys(this.conn.getCatalog(), null, "app tab"); + + assertTrue(this.rs.next()); + } + + /** + * Tests fix for BUG#12970 - java.sql.Types.OTHER returned for binary and + * varbinary columns. + */ + public void testBug12970() throws Exception { + if (versionMeetsMinimum(5, 0, 8)) { + String tableName = "testBug12970"; + + createTable(tableName, "(binary_field BINARY(32), varbinary_field VARBINARY(64))"); + + try { + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, tableName, "%"); + assertTrue(this.rs.next()); + assertEquals(Types.BINARY, this.rs.getInt("DATA_TYPE")); + assertEquals(32, this.rs.getInt("COLUMN_SIZE")); + assertTrue(this.rs.next()); + assertEquals(Types.VARBINARY, this.rs.getInt("DATA_TYPE")); + assertEquals(64, this.rs.getInt("COLUMN_SIZE")); + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT binary_field, varbinary_field FROM " + tableName); + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(Types.BINARY, rsmd.getColumnType(1)); + assertEquals(32, rsmd.getPrecision(1)); + assertEquals(Types.VARBINARY, rsmd.getColumnType(2)); + assertEquals(64, rsmd.getPrecision(2)); + this.rs.close(); + } finally { + if (this.rs != null) { + this.rs.close(); + } + } + } + } + + /** + * Tests fix for BUG#12975 - OpenOffice expects DBMD.supportsIEF() to return + * "true" if foreign keys are supported by the datasource, even though this + * method also covers support for check constraints, which MySQL _doesn't_ + * have. + * + * @throws Exception + * if the test fails. + */ + public void testBug12975() throws Exception { + assertEquals(false, this.conn.getMetaData().supportsIntegrityEnhancementFacility()); + + Connection overrideConn = null; + + try { + Properties props = new Properties(); + + props.setProperty("overrideSupportsIntegrityEnhancementFacility", "true"); + + overrideConn = getConnectionWithProps(props); + assertEquals(true, overrideConn.getMetaData().supportsIntegrityEnhancementFacility()); + } finally { + if (overrideConn != null) { + overrideConn.close(); + } + } + } + + /** + * Tests fix for BUG#13277 - RSMD for generated keys has NPEs when a + * connection is referenced. + * + * @throws Exception + */ + public void testBug13277() throws Exception { + createTable("testBug13277", "(field1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT, field2 VARCHAR(32))"); + + try { + this.stmt.executeUpdate("INSERT INTO testBug13277 (field2) VALUES ('abcdefg')", Statement.RETURN_GENERATED_KEYS); + + this.rs = this.stmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + checkRsmdForBug13277(rsmd); + this.rs.close(); + + for (int i = 0; i < 5; i++) { + this.stmt.addBatch("INSERT INTO testBug13277 (field2) VALUES ('abcdefg')"); + } + + this.stmt.executeBatch(); + + this.rs = this.stmt.getGeneratedKeys(); + + rsmd = this.rs.getMetaData(); + checkRsmdForBug13277(rsmd); + this.rs.close(); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug13277 (field2) VALUES ('abcdefg')", Statement.RETURN_GENERATED_KEYS); + this.pstmt.executeUpdate(); + + this.rs = this.pstmt.getGeneratedKeys(); + + rsmd = this.rs.getMetaData(); + checkRsmdForBug13277(rsmd); + this.rs.close(); + + this.pstmt.addBatch(); + this.pstmt.addBatch(); + + this.pstmt.executeUpdate(); + + this.rs = this.pstmt.getGeneratedKeys(); + + rsmd = this.rs.getMetaData(); + checkRsmdForBug13277(rsmd); + this.rs.close(); + + } finally { + if (this.pstmt != null) { + this.pstmt.close(); + this.pstmt = null; + } + + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + } + + /** + * Tests BUG13601 (which doesn't seem to be present in 3.1.11, but we'll + * leave it in here for regression's-sake). + * + * @throws Exception + * if the test fails. + */ + public void testBug13601() throws Exception { + + if (versionMeetsMinimum(5, 0)) { + createTable("testBug13601", "(field1 BIGINT NOT NULL, field2 BIT default 0 NOT NULL) ENGINE=MyISAM"); + + this.rs = this.stmt.executeQuery("SELECT field1, field2 FROM testBug13601 WHERE 1=-1"); + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(Types.BIT, rsmd.getColumnType(2)); + assertEquals(Boolean.class.getName(), rsmd.getColumnClassName(2)); + + this.rs = this.conn.prepareStatement("SELECT field1, field2 FROM testBug13601 WHERE 1=-1").executeQuery(); + rsmd = this.rs.getMetaData(); + assertEquals(Types.BIT, rsmd.getColumnType(2)); + assertEquals(Boolean.class.getName(), rsmd.getColumnClassName(2)); + + } + } + + /** + * Tests fix for BUG#14815 - DBMD.getColumns() doesn't return TABLE_NAME + * correctly. + * + * @throws Exception + * if the test fails + */ + public void testBug14815() throws Exception { + try { + createTable("testBug14815_1", "(field_1_1 int)"); + createTable("testBug14815_2", "(field_2_1 int)"); + + boolean lcTableNames = this.conn.getMetaData().storesLowerCaseIdentifiers(); + + String tableName1 = lcTableNames ? "testbug14815_1" : "testBug14815_1"; + String tableName2 = lcTableNames ? "testbug14815_2" : "testBug14815_2"; + + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testBug14815%", "%"); + + assertTrue(this.rs.next()); + assertEquals(tableName1, this.rs.getString("TABLE_NAME")); + assertEquals("field_1_1", this.rs.getString("COLUMN_NAME")); + + assertTrue(this.rs.next()); + assertEquals(tableName2, this.rs.getString("TABLE_NAME")); + assertEquals("field_2_1", this.rs.getString("COLUMN_NAME")); + + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + } + } + + /** + * Tests fix for BUG#15854 - DBMD.getColumns() returns wrong type for BIT. + * + * @throws Exception + * if the test fails. + */ + public void testBug15854() throws Exception { + if (versionMeetsMinimum(5, 0)) { + createTable("testBug15854", "(field1 BIT)"); + try { + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testBug15854", "field1"); + assertTrue(this.rs.next()); + assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE")); + } finally { + if (this.rs != null) { + ResultSet toClose = this.rs; + this.rs = null; + toClose.close(); + } + } + + } + } + + /** + * Tests fix for BUG#16277 - Invalid classname returned for + * RSMD.getColumnClassName() for BIGINT type. + * + * @throws Exception + * if the test fails. + */ + public void testBug16277() throws Exception { + createTable("testBug16277", "(field1 BIGINT, field2 BIGINT UNSIGNED)"); + ResultSetMetaData rsmd = this.stmt.executeQuery("SELECT field1, field2 FROM testBug16277").getMetaData(); + assertEquals("java.lang.Long", rsmd.getColumnClassName(1)); + assertEquals("java.math.BigInteger", rsmd.getColumnClassName(2)); + } + + /** + * Tests fix for BUG#18554 - Aliased column names where length of name > 251 + * are corrupted. + * + * @throws Exception + * if the test fails. + */ + public void testBug18554() throws Exception { + testBug18554(249); + testBug18554(250); + testBug18554(251); + testBug18554(252); + testBug18554(253); + testBug18554(254); + testBug18554(255); + } + + private void testBug18554(int columnNameLength) throws Exception { + StringBuilder buf = new StringBuilder(columnNameLength + 2); + + for (int i = 0; i < columnNameLength; i++) { + buf.append((char) ((Math.random() * 26) + 65)); + } + + String colName = buf.toString(); + this.rs = this.stmt.executeQuery("select curtime() as `" + colName + "`"); + ResultSetMetaData meta = this.rs.getMetaData(); + + assertEquals(colName, meta.getColumnLabel(1)); + + } + + private void checkRsmdForBug13277(ResultSetMetaData rsmd) throws SQLException { + + int i = ((com.mysql.jdbc.ConnectionImpl) this.conn) + .getMaxBytesPerChar(CharsetMapping.getJavaEncodingForMysqlCharset(((com.mysql.jdbc.Connection) this.conn).getServerCharset())); + if (i == 1) { + // This is INT field but still processed in + // ResultsetMetaData.getColumnDisplaySize + assertEquals(20, rsmd.getColumnDisplaySize(1)); + } + + if (versionMeetsMinimum(4, 1)) { + assertEquals(false, rsmd.isDefinitelyWritable(1)); + assertEquals(true, rsmd.isReadOnly(1)); + assertEquals(false, rsmd.isWritable(1)); + } + } + + public void testSupportsCorrelatedSubqueries() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + assertEquals(versionMeetsMinimum(4, 1), dbmd.supportsCorrelatedSubqueries()); + } + + public void testSupportesGroupByUnrelated() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + assertEquals(true, dbmd.supportsGroupByUnrelated()); + } + + /** + * Tests fix for BUG#21267, ParameterMetaData throws NullPointerException + * when prepared SQL actually has a syntax error + * + * @throws Exception + */ + public void testBug21267() throws Exception { + createTable("bug21267", "(`Col1` int(11) NOT NULL,`Col2` varchar(45) default NULL,`Col3` varchar(45) default NULL,PRIMARY KEY (`Col1`))"); + + this.pstmt = this.conn.prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?"); + this.pstmt.setInt(1, 1); + + java.sql.ParameterMetaData psMeta = this.pstmt.getParameterMetaData(); + + try { + assertEquals(0, psMeta.getParameterType(1)); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, sqlEx.getSQLState()); + } + + this.pstmt.close(); + + Properties props = new Properties(); + props.setProperty("generateSimpleParameterMetadata", "true"); + + this.pstmt = getConnectionWithProps(props).prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?"); + + psMeta = this.pstmt.getParameterMetaData(); + + assertEquals(Types.VARCHAR, psMeta.getParameterType(1)); + } + + /** + * Tests fix for BUG#21544 - When using information_schema for metadata, + * COLUMN_SIZE for getColumns() is not clamped to range of java.lang.Integer + * as is the case when not using information_schema, thus leading to a + * truncation exception that isn't present when not using + * information_schema. + * + * @throws Exception + * if the test fails + */ + public void testBug21544() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testBug21544", "(foo_id INT NOT NULL, stuff LONGTEXT, PRIMARY KEY (foo_id))", "INNODB"); + + Connection infoSchemConn = null; + + Properties props = new Properties(); + props.setProperty("useInformationSchema", "true"); + props.setProperty("jdbcCompliantTruncation", "false"); + + infoSchemConn = getConnectionWithProps(props); + + try { + this.rs = infoSchemConn.getMetaData().getColumns(null, null, "testBug21544", null); + + while (this.rs.next()) { + this.rs.getInt("COLUMN_SIZE"); + } + } finally { + if (infoSchemConn != null) { + infoSchemConn.close(); + } + } + } + + /** + * Tests fix for BUG#22613 - DBMD.getColumns() does not return expected + * COLUMN_SIZE for the SET type (fixed to be consistent with the ODBC + * driver) + * + * @throws Exception + * if the test fails + */ + public void testBug22613() throws Exception { + + createTable("bug22613", + "( s set('a','bc','def','ghij') default NULL, t enum('a', 'ab', 'cdef'), s2 SET('1','2','3','4','1585','ONE','TWO','Y','N','THREE'))"); + + checkMetadataForBug22613(this.conn); + + if (versionMeetsMinimum(5, 0)) { + Connection infoSchemConn = null; + + try { + Properties props = new Properties(); + props.setProperty("useInformationSchema", "true"); + + infoSchemConn = getConnectionWithProps(props); + + checkMetadataForBug22613(infoSchemConn); + } finally { + if (infoSchemConn != null) { + infoSchemConn.close(); + } + } + } + } + + private void checkMetadataForBug22613(Connection c) throws Exception { + String maxValue = "a,bc,def,ghij"; + String maxValue2 = "1,2,3,4,1585,ONE,TWO,Y,N,THREE"; + + DatabaseMetaData meta = c.getMetaData(); + this.rs = meta.getColumns(null, this.conn.getCatalog(), "bug22613", "s"); + this.rs.first(); + + assertEquals(maxValue.length(), this.rs.getInt("COLUMN_SIZE")); + + this.rs = meta.getColumns(null, this.conn.getCatalog(), "bug22613", "s2"); + this.rs.first(); + + assertEquals(maxValue2.length(), this.rs.getInt("COLUMN_SIZE")); + + this.rs = meta.getColumns(null, c.getCatalog(), "bug22613", "t"); + this.rs.first(); + + assertEquals(4, this.rs.getInt("COLUMN_SIZE")); + } + + /** + * Fix for BUG#22628 - Driver.getPropertyInfo() throws NullPointerException + * for URL that only specifies host and/or port. + * + * @throws Exception + * if the test fails. + */ + public void testBug22628() throws Exception { + DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo("jdbc:mysql://bogus:9999", new Properties()); + + boolean foundHost = false; + boolean foundPort = false; + + for (int i = 0; i < dpi.length; i++) { + if ("bogus".equals(dpi[i].value)) { + foundHost = true; + } + + if ("9999".equals(dpi[i].value)) { + foundPort = true; + } + } + + assertTrue(foundHost && foundPort); + } + + private void testAbsenceOfMetadataForQuery(String query) throws Exception { + try { + this.pstmt = this.conn.prepareStatement(query); + ResultSetMetaData rsmd = this.pstmt.getMetaData(); + + assertNull(rsmd); + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement(query); + rsmd = this.pstmt.getMetaData(); + + assertNull(rsmd); + } finally { + if (this.pstmt != null) { + this.pstmt.close(); + } + } + } + + public void testRSMDToStringFromDBMD() throws Exception { + + this.rs = this.conn.getMetaData().getTypeInfo(); + + this.rs.getMetaData().toString(); // used to cause NPE + + } + + public void testCharacterSetForDBMD() throws Exception { + if (versionMeetsMinimum(4, 0)) { + // server is broken, fixed in 5.2/6.0? + + if (!versionMeetsMinimum(5, 2)) { + return; + } + } + + String quoteChar = this.conn.getMetaData().getIdentifierQuoteString(); + + String tableName = quoteChar + "\u00e9\u0074\u00e9" + quoteChar; + createTable(tableName, "(field1 int)"); + this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, tableName, new String[] { "TABLE" }); + assertEquals(true, this.rs.next()); + System.out.println(this.rs.getString("TABLE_NAME")); + System.out.println(new String(this.rs.getBytes("TABLE_NAME"), "UTF-8")); + } + + /** + * Tests fix for BUG#18258 - Nonexistent catalog/database causes + * SQLException to be raised, rather than returning empty result set. + * + * @throws Exception + * if the test fails. + */ + public void testBug18258() throws Exception { + String bogusDatabaseName = "abcdefghijklmnopqrstuvwxyz"; + this.conn.getMetaData().getTables(bogusDatabaseName, "%", "%", new String[] { "TABLE", "VIEW" }); + this.conn.getMetaData().getColumns(bogusDatabaseName, "%", "%", "%"); + this.conn.getMetaData().getProcedures(bogusDatabaseName, "%", "%"); + } + + /** + * Tests fix for BUG#23303 - DBMD.getSchemas() doesn't return a + * TABLE_CATALOG column. + * + * @throws Exception + * if the test fails. + */ + public void testBug23303() throws Exception { + + this.rs = this.conn.getMetaData().getSchemas(); + this.rs.findColumn("TABLE_CATALOG"); + + } + + /** + * Tests fix for BUG#23304 - DBMD using "show" and DBMD using + * information_schema do not return results consistent with eachother. + * + * (note this fix only addresses the inconsistencies, not the issue that the + * driver is treating schemas differently than some users expect. + * + * We will revisit this behavior when there is full support for schemas in + * MySQL). + * + * @throws Exception + */ + public void testBug23304() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection connShow = null; + Connection connInfoSchema = null; + + ResultSet rsShow = null; + ResultSet rsInfoSchema = null; + + try { + Properties noInfoSchemaProps = new Properties(); + noInfoSchemaProps.setProperty("useInformationSchema", "false"); + + Properties infoSchemaProps = new Properties(); + infoSchemaProps.setProperty("useInformationSchema", "true"); + infoSchemaProps.setProperty("dumpQueriesOnException", "true"); + + connShow = getConnectionWithProps(noInfoSchemaProps); + connInfoSchema = getConnectionWithProps(infoSchemaProps); + + DatabaseMetaData dbmdUsingShow = connShow.getMetaData(); + DatabaseMetaData dbmdUsingInfoSchema = connInfoSchema.getMetaData(); + + assertNotSame(dbmdUsingShow.getClass(), dbmdUsingInfoSchema.getClass()); + + rsShow = dbmdUsingShow.getSchemas(); + rsInfoSchema = dbmdUsingInfoSchema.getSchemas(); + + compareResultSets(rsShow, rsInfoSchema); + + /* + * rsShow = dbmdUsingShow.getTables(connShow.getCatalog(), null, + * "%", new String[] {"TABLE", "VIEW"}); rsInfoSchema = + * dbmdUsingInfoSchema.getTables(connInfoSchema.getCatalog(), null, + * "%", new String[] {"TABLE", "VIEW"}); + * + * compareResultSets(rsShow, rsInfoSchema); + * + * rsShow = dbmdUsingShow.getTables(null, null, "%", new String[] + * {"TABLE", "VIEW"}); rsInfoSchema = + * dbmdUsingInfoSchema.getTables(null, null, "%", new String[] + * {"TABLE", "VIEW"}); + * + * compareResultSets(rsShow, rsInfoSchema); + */ + + createTable("t_testBug23304", + "(field1 int primary key not null, field2 tinyint, field3 mediumint, field4 mediumint, field5 bigint, field6 float, field7 double, field8 decimal, field9 char(32), field10 varchar(32), field11 blob, field12 mediumblob, field13 longblob, field14 text, field15 mediumtext, field16 longtext, field17 date, field18 time, field19 datetime, field20 timestamp)"); + + rsShow = dbmdUsingShow.getColumns(connShow.getCatalog(), null, "t_testBug23304", "%"); + rsInfoSchema = dbmdUsingInfoSchema.getColumns(connInfoSchema.getCatalog(), null, "t_testBug23304", "%"); + + compareResultSets(rsShow, rsInfoSchema); + } finally { + if (rsShow != null) { + rsShow.close(); + } + + if (rsInfoSchema != null) { + rsInfoSchema.close(); + } + } + } + + private void compareResultSets(ResultSet expected, ResultSet actual) throws Exception { + if (expected == null) { + if (actual != null) { + fail("Expected null result set, actual was not null."); + } else { + return; + } + } else if (actual == null) { + fail("Expected non-null actual result set."); + } + + expected.last(); + + int expectedRows = expected.getRow(); + + actual.last(); + + int actualRows = actual.getRow(); + + assertEquals(expectedRows, actualRows); + + ResultSetMetaData metadataExpected = expected.getMetaData(); + ResultSetMetaData metadataActual = actual.getMetaData(); + + assertEquals(metadataExpected.getColumnCount(), metadataActual.getColumnCount()); + + for (int i = 0; i < metadataExpected.getColumnCount(); i++) { + assertEquals(metadataExpected.getColumnName(i + 1), metadataActual.getColumnName(i + 1)); + assertEquals(metadataExpected.getColumnType(i + 1), metadataActual.getColumnType(i + 1)); + assertEquals(metadataExpected.getColumnClassName(i + 1), metadataActual.getColumnClassName(i + 1)); + } + + expected.beforeFirst(); + actual.beforeFirst(); + + StringBuilder messageBuf = null; + + while (expected.next() && actual.next()) { + + if (messageBuf != null) { + messageBuf.append("\n"); + } + + for (int i = 0; i < metadataExpected.getColumnCount(); i++) { + if (expected.getObject(i + 1) == null && actual.getObject(i + 1) == null) { + continue; + } + + if ((expected.getObject(i + 1) == null && actual.getObject(i + 1) != null) + || (expected.getObject(i + 1) != null && actual.getObject(i + 1) == null) + || (!expected.getObject(i + 1).equals(actual.getObject(i + 1)))) { + if ("COLUMN_DEF".equals(metadataExpected.getColumnName(i + 1)) + && (expected.getObject(i + 1) == null && actual.getString(i + 1).length() == 0) + || (expected.getString(i + 1).length() == 0 && actual.getObject(i + 1) == null)) { + continue; // known bug with SHOW FULL COLUMNS, and we + // can't distinguish between null and '' + // for a default + } + + if ("CHAR_OCTET_LENGTH".equals(metadataExpected.getColumnName(i + 1))) { + if (((com.mysql.jdbc.ConnectionImpl) this.conn).getMaxBytesPerChar( + CharsetMapping.getJavaEncodingForMysqlCharset(((com.mysql.jdbc.Connection) this.conn).getServerCharset())) > 1) { + continue; // SHOW CREATE and CHAR_OCT *will* differ + } + } + + if (messageBuf == null) { + messageBuf = new StringBuilder(); + } else { + messageBuf.append("\n"); + } + + messageBuf.append("On row " + expected.getRow() + " ,for column named " + metadataExpected.getColumnName(i + 1) + ", expected '" + + expected.getObject(i + 1) + "', found '" + actual.getObject(i + 1) + "'"); + + } + } + } + + if (messageBuf != null) { + fail(messageBuf.toString()); + } + } + + /** + * Tests fix for BUG#25624 - Whitespace surrounding storage/size specifiers + * in stored procedure declaration causes NumberFormatException to be thrown + * when calling stored procedure. + * + * @throws Exception + */ + public void testBug25624() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + // + // we changed up the parameters to get coverage of the fixes, + // also note that whitespace _is_ significant in the DDL... + // + createProcedure("testBug25624", "(in _par1 decimal( 10 , 2 ) , in _par2 varchar( 4 )) BEGIN select 1; END"); + + this.conn.prepareCall("{call testBug25624(?,?)}").close(); + } + + /** + * Tests fix for BUG#27867 - Schema objects with identifiers other than the + * connection character aren't retrieved correctly in ResultSetMetadata. + * + * @throws Exception + * if the test fails. + */ + public void testBug27867() throws Exception { + if (!versionMeetsMinimum(4, 1)) { + return; + } + + String gbkColumnName = "\u00e4\u00b8\u00ad\u00e6\u2013\u2021\u00e6\u00b5\u2039\u00e8\u00af\u2022"; + createTable("ColumnNameEncoding", + "(`" + gbkColumnName + "` varchar(1) default NULL, `ASCIIColumn` varchar(1) default NULL" + ")ENGINE=MyISAM DEFAULT CHARSET=utf8"); + + this.rs = this.stmt.executeQuery("SELECT * FROM ColumnNameEncoding"); + java.sql.ResultSetMetaData tblMD = this.rs.getMetaData(); + + assertEquals(gbkColumnName, tblMD.getColumnName(1)); + assertEquals("ASCIIColumn", tblMD.getColumnName(2)); + } + + /** + * Fixed BUG#27915 - DatabaseMetaData.getColumns() doesn't contain SCOPE_* + * or IS_AUTOINCREMENT columns. + * + * @throws Exception + */ + public void testBug27915() throws Exception { + createTable("testBug27915", "(field1 int not null primary key auto_increment, field2 int)"); + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getColumns(this.conn.getCatalog(), null, "testBug27915", "%"); + this.rs.next(); + + checkBug27915(); + + if (versionMeetsMinimum(5, 0)) { + this.rs = getConnectionWithProps("useInformationSchema=true").getMetaData().getColumns(this.conn.getCatalog(), null, "testBug27915", "%"); + this.rs.next(); + + checkBug27915(); + } + } + + private void checkBug27915() throws SQLException { + assertNull(this.rs.getString("SCOPE_CATALOG")); + assertNull(this.rs.getString("SCOPE_SCHEMA")); + assertNull(this.rs.getString("SCOPE_TABLE")); + assertNull(this.rs.getString("SOURCE_DATA_TYPE")); + assertEquals("YES", this.rs.getString("IS_AUTOINCREMENT")); + + this.rs.next(); + + assertNull(this.rs.getString("SCOPE_CATALOG")); + assertNull(this.rs.getString("SCOPE_SCHEMA")); + assertNull(this.rs.getString("SCOPE_TABLE")); + assertNull(this.rs.getString("SOURCE_DATA_TYPE")); + assertEquals("NO", this.rs.getString("IS_AUTOINCREMENT")); + } + + /** + * Tests fix for BUG#27916 - UNSIGNED types not reported via + * DBMD.getTypeInfo(), and capitalization of types is not consistent between + * DBMD.getColumns(), RSMD.getColumnTypeName() and DBMD.getTypeInfo(). + * + * This fix also ensures that the precision of UNSIGNED MEDIUMINT and + * UNSIGNED BIGINT is reported correctly via DBMD.getColumns(). + * + * Second fix ensures that list values of ENUM and SET types containing + * 'unsigned' are not taken in account. + * + * @throws Exception + */ + public void testBug27916() throws Exception { + createTable("testBug27916", + "(field1 TINYINT UNSIGNED, field2 SMALLINT UNSIGNED, field3 INT UNSIGNED, field4 INTEGER UNSIGNED, field5 MEDIUMINT UNSIGNED, field6 BIGINT UNSIGNED)"); + + ResultSetMetaData rsmd = this.stmt.executeQuery("SELECT * FROM testBug27916").getMetaData(); + + HashMap typeNameToPrecision = new HashMap(); + this.rs = this.conn.getMetaData().getTypeInfo(); + + while (this.rs.next()) { + typeNameToPrecision.put(this.rs.getString("TYPE_NAME"), this.rs.getObject("PRECISION")); + } + + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testBug27916", "%"); + + for (int i = 0; i < rsmd.getColumnCount(); i++) { + this.rs.next(); + String typeName = this.rs.getString("TYPE_NAME"); + + assertEquals(typeName, rsmd.getColumnTypeName(i + 1)); + assertEquals(typeName, this.rs.getInt("COLUMN_SIZE"), rsmd.getPrecision(i + 1)); + assertEquals(typeName, new Integer(rsmd.getPrecision(i + 1)), typeNameToPrecision.get(typeName)); + } + + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Properties props = new Properties(); + props.setProperty("useInformationSchema", "false"); + ArrayList types = new ArrayList(); + Connection PropConn = getConnectionWithProps(props); + try { + DatabaseMetaData dbmd = PropConn.getMetaData(); + this.rs = dbmd.getTypeInfo(); + while (this.rs.next()) { + types.add(this.rs.getString("TYPE_NAME")); + } + this.rs.close(); + + this.rs = dbmd.getColumns("mysql", null, "time_zone_transition", "%"); + while (this.rs.next()) { + String typeName = this.rs.getString("TYPE_NAME"); + assertTrue(typeName, types.contains(typeName)); + } + this.rs.close(); + this.rs = dbmd.getColumns("mysql", null, "proc", "%"); + while (this.rs.next()) { + String typeName = this.rs.getString("TYPE_NAME"); + assertTrue(typeName, types.contains(typeName)); + } + this.rs.close(); + PropConn.close(); + props.clear(); + + props.setProperty("useInformationSchema", "true"); + PropConn = getConnectionWithProps(props); + dbmd = PropConn.getMetaData(); + + this.rs = dbmd.getColumns("mysql", null, "time_zone_transition", "%"); + while (this.rs.next()) { + String typeName = this.rs.getString("TYPE_NAME"); + assertTrue(typeName, types.contains(typeName)); + } + this.rs.close(); + this.rs = dbmd.getColumns("mysql", null, "proc", "%"); + while (this.rs.next()) { + String typeName = this.rs.getString("TYPE_NAME"); + assertTrue(typeName, types.contains(typeName)); + } + this.rs.close(); + PropConn.close(); + props.clear(); + } finally { + if (PropConn != null) { + PropConn.close(); + } + } + } + + public void testBug20491() throws Exception { + String[] fields = { "field1_ae_\u00e4", "field2_ue_\u00fc", "field3_oe_\u00f6", "field4_sz_\u00df" }; + + createTable("tst", "(`" + fields[0] + "` int(10) unsigned NOT NULL default '0', `" + fields[1] + "` varchar(45) default '', `" + fields[2] + + "` varchar(45) default '', `" + fields[3] + "` varchar(45) default '', PRIMARY KEY (`" + fields[0] + "`))"); + + // demonstrate that these are all in the Cp1252 encoding + + for (int i = 0; i < fields.length; i++) { + try { + assertEquals(fields[i], new String(fields[i].getBytes("Cp1252"), "Cp1252")); + } catch (ComparisonFailure cfEx) { + if (i == 3) { + // If we're on a mac, we're out of luck + // we can't store this in the filesystem... + + if (!System.getProperty("os.name").startsWith("Mac")) { + throw cfEx; + } + } + } + } + + @SuppressWarnings("unused") + byte[] asBytes = fields[0].getBytes("utf-8"); + + DatabaseMetaData md = this.conn.getMetaData(); + + this.rs = md.getColumns(null, "%", "tst", "%"); + + int j = 0; + + while (this.rs.next()) { + try { + assertEquals("Wrong column name:" + this.rs.getString(4), fields[j++], this.rs.getString(4)); + } catch (ComparisonFailure cfEx) { + if (j == 3) { + // If we're on a mac, we're out of luck + // we can't store this in the filesystem... + + if (!System.getProperty("os.name").startsWith("Mac")) { + throw cfEx; + } + } + } + } + + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT * FROM tst"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + try { + assertEquals("Wrong column name:" + rsmd.getColumnName(i), fields[i - 1], rsmd.getColumnName(i)); + } catch (ComparisonFailure cfEx) { + if (i - 1 == 3) { + // If we're on a mac, we're out of luck + // we can't store this in the filesystem... + + if (!System.getProperty("os.name").startsWith("Mac")) { + throw cfEx; + } + } + } + } + } + + /** + * Tests fix for Bug#33594 - When cursor fetch is enabled, wrong metadata is + * returned from DBMD. + * + * The fix is two parts. + * + * First, when asking for the first column value twice from a cursor-fetched + * row, the driver didn't re-position, and thus the "next" column was + * returned. + * + * Second, metadata statements and internal statements the driver uses + * shouldn't use cursor-based fetching at all, so we've ensured that + * internal statements have their fetch size set to "0". + */ + public void testBug33594() throws Exception { + if (!versionMeetsMinimum(5, 0, 7)) { + return; + } + boolean max_key_l_bug = false; + + try { + createTable("bug33594", "(fid varchar(255) not null primary key, id INT, geom linestring, name varchar(255))"); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max key length") != -1) { + createTable("bug33594", "(fid varchar(180) not null primary key, id INT, geom linestring, name varchar(255))"); + max_key_l_bug = true; + } + } + + Properties props = new Properties(); + props.put("useInformationSchema", "false"); + props.put("useCursorFetch", "false"); + props.put("defaultFetchSize", "100"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getColumns(null, null, "bug33594", null); + this.rs.next(); + assertEquals("bug33594", this.rs.getString("TABLE_NAME")); + assertEquals("fid", this.rs.getString("COLUMN_NAME")); + assertEquals("VARCHAR", this.rs.getString("TYPE_NAME")); + if (!max_key_l_bug) { + assertEquals("255", this.rs.getString("COLUMN_SIZE")); + } else { + assertEquals("180", this.rs.getString("COLUMN_SIZE")); + } + + Properties props2 = new Properties(); + props2.put("useInformationSchema", "false"); + props2.put("useCursorFetch", "true"); + props2.put("defaultFetchSize", "100"); + + Connection conn2 = null; + + try { + conn2 = getConnectionWithProps(props2); + DatabaseMetaData metaData2 = conn2.getMetaData(); + this.rs = metaData2.getColumns(null, null, "bug33594", null); + this.rs.next(); + assertEquals("bug33594", this.rs.getString("TABLE_NAME")); + assertEquals("fid", this.rs.getString("COLUMN_NAME")); + assertEquals("VARCHAR", this.rs.getString("TYPE_NAME")); + if (!max_key_l_bug) { + assertEquals("255", this.rs.getString("COLUMN_SIZE")); + } else { + assertEquals("180", this.rs.getString("COLUMN_SIZE")); + } + + // we should only see one server-side prepared statement, and + // that's + // caused by us going off to ask about the count! + assertEquals("1", getSingleIndexedValueWithQuery(conn2, 2, "SHOW SESSION STATUS LIKE 'Com_stmt_prepare'").toString()); + } finally { + if (conn2 != null) { + conn2.close(); + } + } + } finally { + if (conn1 != null) { + conn1.close(); + } + } + + } + + public void testBug34194() throws Exception { + createTable("bug34194", "(id integer,geom geometry)"); + + if (!versionMeetsMinimum(5, 6)) { + this.stmt.execute("insert into bug34194 values('1', GeomFromText('POINT(622572.881 5156121.034)'))"); + } else { + this.stmt.execute("insert into bug34194 values('1', ST_GeomFromText('POINT(622572.881 5156121.034)'))"); + } + this.rs = this.stmt.executeQuery("select * from bug34194"); + ResultSetMetaData RSMD = this.rs.getMetaData(); + assertEquals("GEOMETRY", RSMD.getColumnTypeName(2)); + } + + public void testNoSystemTablesReturned() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; // no information schema + } + + this.rs = this.conn.getMetaData().getTables("information_schema", "null", "%", new String[] { "SYSTEM VIEW" }); + assertTrue(this.rs.next()); + this.rs = this.conn.getMetaData().getTables("information_schema", "null", "%", new String[] { "SYSTEM TABLE" }); + assertFalse(this.rs.next()); + this.rs = this.conn.getMetaData().getTables("information_schema", "null", "%", new String[] { "TABLE" }); + assertFalse(this.rs.next()); + this.rs = this.conn.getMetaData().getTables("information_schema", "null", "%", new String[] { "VIEW" }); + assertFalse(this.rs.next()); + this.rs = this.conn.getMetaData().getTables("information_schema", "null", "%", new String[] { "SYSTEM TABLE", "SYSTEM VIEW", "TABLE", "VIEW" }); + assertTrue(this.rs.next()); + this.rs = this.conn.getMetaData().getColumns("information_schema", null, "TABLES", "%"); + assertTrue(this.rs.next()); + } + + public void testABunchOfReturnTypes() throws Exception { + checkABunchOfReturnTypesForConnection(this.conn); + + if (versionMeetsMinimum(5, 0)) { + checkABunchOfReturnTypesForConnection(getConnectionWithProps("useInformationSchema=true")); + } + } + + private void checkABunchOfReturnTypesForConnection(Connection mdConn) throws Exception { + + DatabaseMetaData md = mdConn.getMetaData(); + + // Bug#44862 - getBestRowIdentifier does not return resultset as per JDBC API specifications + this.rs = md.getBestRowIdentifier(this.conn.getCatalog(), null, "returnTypesTest", DatabaseMetaData.bestRowSession, false); + + int[] types = new int[] { Types.SMALLINT, // 1. SCOPE short => actual scope of result + Types.CHAR, // 2. COLUMN_NAME String => column name + Types.INTEGER, // 3. DATA_TYPE int => SQL data type from java.sql.Types + Types.CHAR, // 4. TYPE_NAME String => Data source dependent type name, for a UDT the type name is fully qualified + Types.INTEGER, // 5. COLUMN_SIZE int => precision + Types.INTEGER, // 6. BUFFER_LENGTH int => not used + Types.SMALLINT, // 7. DECIMAL_DIGITS short => scale + Types.SMALLINT, // 8. PSEUDO_COLUMN short => is this a pseudo column like an Oracle ROWID + }; + + checkTypes(this.rs, types); + + // Bug#44683 - getVersionColumns does not return resultset as per JDBC API specifications + this.rs = md.getVersionColumns(this.conn.getCatalog(), null, "returnTypesTest"); + + types = new int[] { Types.SMALLINT, // SCOPE short => is not used + Types.CHAR, // COLUMN_NAME String => column name + Types.INTEGER, // DATA_TYPE int => SQL data type from java.sql.Types + Types.CHAR, // TYPE_NAME String => Data source-dependent type name + Types.INTEGER, // COLUMN_SIZE int => precision + Types.INTEGER, // BUFFER_LENGTH int => length of column value in bytes + Types.SMALLINT, // DECIMAL_DIGITS short => scale + Types.SMALLINT // PSEUDO_COLUMN short => whether this is pseudo column like an Oracle ROWID + }; + + checkTypes(this.rs, types); + + // Bug#44865 - getColumns does not return resultset as per JDBC API specifications + this.rs = md.getColumns(this.conn.getCatalog(), null, "returnTypesTest", "foo"); + + types = new int[] { Types.CHAR, // 1. TABLE_CAT String => table catalog (may be null) + Types.CHAR, // 2. TABLE_SCHEM String => table schema (may be null) + Types.CHAR, // 3. TABLE_NAME String => table name + Types.CHAR, // 4. COLUMN_NAME String => column name + Types.INTEGER, // 5. DATA_TYPE int => SQL type from java.sql.Types + Types.CHAR, // 6. TYPE_NAME String => Data source dependent type name, for a UDT the type name is fully qualified + Types.INTEGER, // 7. COLUMN_SIZE int => column size. For char or date types this is the maximum number of characters, for numeric or decimal + // types this is precision. + Types.INTEGER, // 8. BUFFER_LENGTH is not used. + Types.INTEGER, // 9. DECIMAL_DIGITS int => the number of fractional digits + Types.INTEGER, // 10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + Types.INTEGER, // 11. NULLABLE int => is NULL allowed. + Types.CHAR, // 12. REMARKS String => comment describing column (may be null) + Types.CHAR, // 13. COLUMN_DEF String => default value (may be null) + Types.INTEGER, // 14. SQL_DATA_TYPE int => unused + Types.INTEGER, // 15. SQL_DATETIME_SUB int => unused + Types.INTEGER, // 16. CHAR_OCTET_LENGTH int => for char types the maximum number of bytes in the column + Types.INTEGER, // 17. ORDINAL_POSITION int => index of column in table (starting at 1) + Types.CHAR, // 18. IS_NULLABLE String => "NO" means column definitely does not allow NULL values; "YES" means the column might allow NULL + // values. An empty string means nobody knows. + Types.CHAR, // 19. SCOPE_CATLOG String => catalog of table that is the scope of a reference attribute (null if DATA_TYPE isn't REF) + Types.CHAR, // 20. SCOPE_SCHEMA String => schema of table that is the scope of a reference attribute (null if the DATA_TYPE isn't REF) + Types.CHAR, // 21. SCOPE_TABLE String => table name that this the scope of a reference attribute (null if the DATA_TYPE isn't REF) + Types.SMALLINT, // 22. SOURCE_DATA_TYPE short => source type of a distinct type or user-generated Ref type, SQL type from java.sql.Types (null + // if DATA_TYPE isn't DISTINCT or user-generated REF) + Types.CHAR, // 23. IS_AUTOINCREMENT String => Indicates whether this column is auto incremented + Types.CHAR // 24. IS_GENERATEDCOLUMN String => Indicates whether this is a generated column + }; + + checkTypes(this.rs, types); + + // Bug#44868 - getTypeInfo does not return resultset as per JDBC API specifications + this.rs = md.getTypeInfo(); + + types = new int[] { Types.CHAR, // 1. TYPE_NAME String => Type name + Types.INTEGER, // 2. DATA_TYPE int => SQL data type from java.sql.Types + Types.INTEGER, // 3. PRECISION int => maximum precision + Types.CHAR, // 4. LITERAL_PREFIX String => prefix used to quote a literal (may be null) + Types.CHAR, // 5. LITERAL_SUFFIX String => suffix used to quote a literal (may be null) + Types.CHAR, // 6. CREATE_PARAMS String => parameters used in creating the type (may be null) + Types.SMALLINT, // 7. NULLABLE short => can you use NULL for this type. + Types.BOOLEAN, // 8. CASE_SENSITIVE boolean=> is it case sensitive. + Types.SMALLINT, // 9. SEARCHABLE short => can you use "WHERE" based on this type: + Types.BOOLEAN, // 10. UNSIGNED_ATTRIBUTE boolean => is it unsigned. + Types.BOOLEAN, // 11. FIXED_PREC_SCALE boolean => can it be a money value. + Types.BOOLEAN, // 12. AUTO_INCREMENT boolean => can it be used for an auto-increment value. + Types.CHAR, // 13. LOCAL_TYPE_NAME String => localized version of type name (may be null) + Types.SMALLINT, // 14. MINIMUM_SCALE short => minimum scale supported + Types.SMALLINT, // 15. MAXIMUM_SCALE short => maximum scale supported + Types.INTEGER, // 16. SQL_DATA_TYPE int => unused + Types.INTEGER, // 17. SQL_DATETIME_SUB int => unused + Types.INTEGER // 18. NUM_PREC_RADIX int => usually 2 or 10 + }; + + checkTypes(this.rs, types); + + // Bug#44869 - getIndexInfo does not return resultset as per JDBC API specifications + this.rs = md.getIndexInfo(this.conn.getCatalog(), null, "returnTypesTest", false, false); + + types = new int[] { Types.CHAR, // 1. TABLE_CAT String => table catalog (may be null) + Types.CHAR, // 2. TABLE_SCHEM String => table schema (may be null) + Types.CHAR, // 3. TABLE_NAME String => table name + Types.BOOLEAN, // 4. NON_UNIQUE boolean => Can index values be non-unique. false when TYPE is tableIndexStatistic + Types.CHAR, // 5. INDEX_QUALIFIER String => index catalog (may be null); null when TYPE is tableIndexStatistic + Types.CHAR, // 6. INDEX_NAME String => index name; null when TYPE is tableIndexStatistic + Types.SMALLINT, // 7. TYPE short => index type: + Types.SMALLINT, // 8. ORDINAL_POSITION short => column sequence number within index; zero when TYPE is tableIndexStatistic + Types.CHAR, // 9. COLUMN_NAME String => column name; null when TYPE is tableIndexStatistic + Types.CHAR, // 10. ASC_OR_DESC String => column sort sequence, "A" => ascending, "D" => descending, may be null if sort sequence is not + // supported; null when TYPE is tableIndexStatistic + Util.isJdbc42() ? Types.BIGINT : Types.INTEGER, // 11. CARDINALITY int/long => When TYPE is tableIndexStatistic, then this is the number of rows + // in the table; otherwise, it is the number of unique values in the index. + Util.isJdbc42() ? Types.BIGINT : Types.INTEGER, // 12. PAGES int/long => When TYPE is tableIndexStatisic then this is the number of pages used + // for the table, otherwise it is the number of pages used for the current index. + Types.CHAR // 13. FILTER_CONDITION String => Filter condition, if any. (may be null) + }; + + checkTypes(this.rs, types); + + // Bug#44867 - getImportedKeys/exportedKeys/crossReference doesn't have correct type for DEFERRABILITY + this.rs = md.getImportedKeys(this.conn.getCatalog(), null, "returnTypesTest"); + + types = new int[] { Types.CHAR, // PKTABLE_CAT String => primary key table catalog being imported (may be null) + Types.CHAR, // PKTABLE_SCHEM String => primary key table schema being imported (may be null) + Types.CHAR, // PKTABLE_NAME String => primary key table name being imported + Types.CHAR, // PKCOLUMN_NAME String => primary key column name being imported + Types.CHAR, // FKTABLE_CAT String => foreign key table catalog (may be null) + Types.CHAR, // FKTABLE_SCHEM String => foreign key table schema (may be null) + Types.CHAR, // FKTABLE_NAME String => foreign key table name + Types.CHAR, // FKCOLUMN_NAME String => foreign key column name + Types.SMALLINT, // KEY_SEQ short => sequence number within a foreign key + Types.SMALLINT, // UPDATE_RULE short => What happens to a foreign key when the primary key is updated: + Types.SMALLINT, // DELETE_RULE short => What happens to the foreign key when primary is deleted + Types.CHAR, // FK_NAME String => foreign key name (may be null) + Types.CHAR, // PK_NAME String => primary key name (may be null) + Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit + }; + + checkTypes(this.rs, types); + + this.rs = md.getExportedKeys(this.conn.getCatalog(), null, "returnTypesTest"); + + types = new int[] { Types.CHAR, // PKTABLE_CAT String => primary key table catalog being imported (may be null) + Types.CHAR, // PKTABLE_SCHEM String => primary key table schema being imported (may be null) + Types.CHAR, // PKTABLE_NAME String => primary key table name being imported + Types.CHAR, // PKCOLUMN_NAME String => primary key column name being imported + Types.CHAR, // FKTABLE_CAT String => foreign key table catalog (may be null) + Types.CHAR, // FKTABLE_SCHEM String => foreign key table schema (may be null) + Types.CHAR, // FKTABLE_NAME String => foreign key table name + Types.CHAR, // FKCOLUMN_NAME String => foreign key column name + Types.SMALLINT, // KEY_SEQ short => sequence number within a foreign key + Types.SMALLINT, // UPDATE_RULE short => What happens to a foreign key when the primary key is updated: + Types.SMALLINT, // DELETE_RULE short => What happens to the foreign key when primary is deleted + Types.CHAR, // FK_NAME String => foreign key name (may be null) + Types.CHAR, // PK_NAME String => primary key name (may be null) + Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit + }; + + checkTypes(this.rs, types); + + this.rs = md.getCrossReference(this.conn.getCatalog(), null, "returnTypesTest", this.conn.getCatalog(), null, "bar"); + + types = new int[] { Types.CHAR, // PKTABLE_CAT String => primary key table catalog being imported (may be null) + Types.CHAR, // PKTABLE_SCHEM String => primary key table schema being imported (may be null) + Types.CHAR, // PKTABLE_NAME String => primary key table name being imported + Types.CHAR, // PKCOLUMN_NAME String => primary key column name being imported + Types.CHAR, // FKTABLE_CAT String => foreign key table catalog (may be null) + Types.CHAR, // FKTABLE_SCHEM String => foreign key table schema (may be null) + Types.CHAR, // FKTABLE_NAME String => foreign key table name + Types.CHAR, // FKCOLUMN_NAME String => foreign key column name + Types.SMALLINT, // KEY_SEQ short => sequence number within a foreign key + Types.SMALLINT, // UPDATE_RULE short => What happens to a foreign key when the primary key is updated: + Types.SMALLINT, // DELETE_RULE short => What happens to the foreign key when primary is deleted + Types.CHAR, // FK_NAME String => foreign key name (may be null) + Types.CHAR, // PK_NAME String => primary key name (may be null) + Types.SMALLINT // DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit + }; + + checkTypes(this.rs, types); + } + + private final static Map TYPES_MAP = new HashMap(); + + static { + Field[] typeFields = Types.class.getFields(); + + for (int i = 0; i < typeFields.length; i++) { + System.out.println(typeFields[i].getName() + " -> " + typeFields[i].getType().getClass()); + + if (Modifier.isStatic(typeFields[i].getModifiers())) { + try { + TYPES_MAP.put(new Integer(typeFields[i].getInt(null)), "java.sql.Types." + typeFields[i].getName()); + } catch (IllegalArgumentException e) { + // ignore + } catch (IllegalAccessException e) { + // ignore + } + } + } + } + + private void checkTypes(ResultSet rsToCheck, int[] types) throws Exception { + ResultSetMetaData rsmd = rsToCheck.getMetaData(); + assertEquals(types.length, rsmd.getColumnCount()); + for (int i = 0; i < types.length; i++) { + String expectedType = TYPES_MAP.get(new Integer(types[i])); + String actualType = TYPES_MAP.get(new Integer(rsmd.getColumnType(i + 1))); + assertNotNull(expectedType); + assertNotNull(actualType); + assertEquals("Unexpected type in column " + (i + 1), expectedType, actualType); + } + } + + /** + * Bug #43714 - useInformationSchema with DatabaseMetaData.getExportedKeys() + * throws exception + */ + public void testBug43714() throws Exception { + Connection c_IS = null; + try { + c_IS = getConnectionWithProps("useInformationSchema=true"); + DatabaseMetaData dbmd = c_IS.getMetaData(); + this.rs = dbmd.getExportedKeys("x", "y", "z"); + } finally { + try { + if (c_IS != null) { + c_IS.close(); + } + } catch (SQLException ex) { + } + } + } + + /** + * Bug #41269 - DatabaseMetadata.getProcedureColumns() returns wrong value + * for column length + */ + public void testBug41269() throws Exception { + createProcedure("bug41269", "(in param1 int, out result varchar(197)) BEGIN select 1, ''; END"); + + ResultSet procMD = this.conn.getMetaData().getProcedureColumns(null, null, "bug41269", "%"); + assertTrue(procMD.next()); + assertEquals("Int param length", 10, procMD.getInt(9)); + assertTrue(procMD.next()); + assertEquals("String param length", 197, procMD.getInt(9)); + assertFalse(procMD.next()); + + } + + public void testBug31187() throws Exception { + createTable("testBug31187", "(field1 int)"); + + Connection nullCatConn = getConnectionWithProps("nullCatalogMeansCurrent=false"); + DatabaseMetaData dbmd = nullCatConn.getMetaData(); + ResultSet dbTblCols = dbmd.getColumns(null, null, "testBug31187", "%"); + + boolean found = false; + + while (dbTblCols.next()) { + String catalog = dbTblCols.getString("TABLE_CAT"); + String table = dbTblCols.getString("TABLE_NAME"); + boolean useLowerCaseTableNames = dbmd.storesLowerCaseIdentifiers(); + + if (catalog.equals(nullCatConn.getCatalog()) + && (((useLowerCaseTableNames && "testBug31187".equalsIgnoreCase(table)) || "testBug31187".equals(table)))) { + found = true; + } + } + + assertTrue("Didn't find any columns for table named 'testBug31187' in database " + this.conn.getCatalog(), found); + } + + public void testBug44508() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getSuperTypes("", "", ""); + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertEquals("TYPE_CAT", rsmd.getColumnName(1)); // Gives TABLE_CAT + assertEquals("TYPE_SCHEM", rsmd.getColumnName(2)); // Gives TABLE_SCHEM + } + + /** + * Tests fix for BUG#52167 - Can't parse parameter list with special + * characters inside + * + * @throws Exception + */ + public void testBug52167() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + // DatabaseMetaData.java (~LN 1730) + // + //Bug#52167, tokenizer will break if declaration contains special + // characters like \n + // + declaration = declaration.replaceAll("[\\t\\n\\x0B\\f\\r]", " "); + // StringTokenizer declarationTok = new StringTokenizer( + // declaration, " \t"); + createProcedure("testBug52167", "(in _par1 decimal( 10 , 2 ) , in _par2\n varchar( 4 )) BEGIN select 1; END"); + + this.conn.prepareCall("{call testBug52167(?,?)}").close(); + } + + /** + * Tests fix for BUG#51912 - Passing NULL as cat. param to + * getProcedureColumns with nullCatalogMeansCurrent = false + * + * @throws Exception + * if the test fails. + */ + public void testBug51912() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection overrideConn = null; + try { + Properties props = new Properties(); + props.setProperty("nullCatalogMeansCurrent", "false"); + overrideConn = getConnectionWithProps(props); + + DatabaseMetaData dbmd = overrideConn.getMetaData(); + this.rs = dbmd.getProcedureColumns(null, null, "%", null); + this.rs.close(); + + } finally { + if (overrideConn != null) { + overrideConn.close(); + } + } + } + + /** + * Tests fix for BUG#38367 - DatabaseMetaData dbMeta = this.conn.getMetaData(); + * this.rs = dbMeta.getProcedureColumns("test", null, "nullableParameterTest", null); + * ... + * Short columnNullable = new Short(this.rs.getShort(12)); + * assertTrue("Parameter " + columnName + " do not allow null arguments", + * columnNullable.intValue() == java.sql.DatabaseMetaData.procedureNullable); + * was failing for no good reason. + * + * @throws Exception + * if the test fails. + */ + + public void testBug38367() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + try { + createProcedure("sptestBug38367", + "(OUT nfact VARCHAR(100), IN ccuenta VARCHAR(100),\nOUT ffact VARCHAR(100),\nOUT fdoc VARCHAR(100))" + "\nBEGIN\nEND"); + + DatabaseMetaData dbMeta = this.conn.getMetaData(); + this.rs = dbMeta.getProcedureColumns(this.conn.getCatalog(), null, "sptestBug38367", null); + while (this.rs.next()) { + String columnName = this.rs.getString(4); + Short columnNullable = new Short(this.rs.getShort(12)); + assertTrue("Parameter " + columnName + " is not java.sql.DatabaseMetaData.procedureNullable.", + columnNullable.intValue() == java.sql.DatabaseMetaData.procedureNullable); + } + } finally { + } + } + + /** + * Tests fix for BUG#57808 - wasNull not set + * for DATE field with value 0000-00-00 + * in getDate() although + * zeroDateTimeBehavior is convertToNull. + * + * @throws Exception + * if the test fails. + */ + public void testBug57808() throws Exception { + try { + createTable("bug57808", "(ID INT(3) NOT NULL PRIMARY KEY, ADate DATE NOT NULL)"); + Properties props = new Properties(); + if (versionMeetsMinimum(5, 7, 4)) { + props.put("jdbcCompliantTruncation", "false"); + } + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + props.put("zeroDateTimeBehavior", "convertToNull"); + Connection conn1 = null; + + conn1 = getConnectionWithProps(props); + this.stmt = conn1.createStatement(); + this.stmt.executeUpdate("INSERT INTO bug57808(ID, ADate) VALUES(1, 0000-00-00)"); + + this.rs = this.stmt.executeQuery("SELECT ID, ADate FROM bug57808 WHERE ID = 1"); + if (this.rs.first()) { + Date theDate = this.rs.getDate("ADate"); + if (theDate == null) { + assertTrue("wasNull is FALSE", this.rs.wasNull()); + } else { + fail("Original date was not NULL!"); + } + } + } finally { + } + } + + /** + * Tests fix for BUG#61150 - First call to SP + * fails with "No Database Selected" + * The workaround introduced in DatabaseMetaData.getCallStmtParameterTypes + * to fix the bug in server where SHOW CREATE PROCEDURE was not respecting + * lower-case table names is misbehaving when connection is not attached to + * database and on non-casesensitive OS. + * + * @throws Exception + * if the test fails. + */ + public void testBug61150() throws Exception { + NonRegisteringDriver driver = new NonRegisteringDriver(); + Properties oldProps = driver.parseURL(BaseTestCase.dbUrl, null); + + String host = driver.host(oldProps); + int port = driver.port(oldProps); + StringBuilder newUrlToTestNoDB = new StringBuilder("jdbc:mysql://"); + if (host != null) { + newUrlToTestNoDB.append(host); + } + newUrlToTestNoDB.append(":").append(port).append("/"); + + Statement savedSt = this.stmt; + + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + Connection conn1 = DriverManager.getConnection(newUrlToTestNoDB.toString(), props); + + this.stmt = conn1.createStatement(); + createDatabase("TST1"); + createProcedure("TST1.PROC", "(x int, out y int)\nbegin\ndeclare z int;\nset z = x+1, y = z;\nend\n"); + + CallableStatement cStmt = null; + cStmt = conn1.prepareCall("{call `TST1`.`PROC`(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + cStmt.clearParameters(); + cStmt.close(); + + conn1.setCatalog("TST1"); + cStmt = null; + cStmt = conn1.prepareCall("{call TST1.PROC(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + cStmt.clearParameters(); + cStmt.close(); + + conn1.setCatalog("mysql"); + cStmt = null; + cStmt = conn1.prepareCall("{call `TST1`.`PROC`(?, ?)}"); + cStmt.setInt(1, 5); + cStmt.registerOutParameter(2, Types.INTEGER); + + cStmt.execute(); + assertEquals(6, cStmt.getInt(2)); + cStmt.clearParameters(); + cStmt.close(); + + this.stmt = savedSt; + } + + /** + * Tests fix for BUG#61332 - Check if "LIKE" or "=" is sent + * to server in I__S query when no wildcards are supplied + * for schema parameter. + * + * @throws Exception + * if the test fails. + */ + public void testBug61332() throws Exception { + Properties props = new Properties(); + props.setProperty("useInformationSchema", "true"); + props.setProperty("statementInterceptors", StatementInterceptorBug61332.class.getName()); + + createDatabase("dbbug61332"); + Connection testConn = getConnectionWithProps(props); + + if (versionMeetsMinimum(5, 0, 7)) { + try { + createTable("dbbug61332.bug61332", "(c1 char(1))"); + DatabaseMetaData metaData = testConn.getMetaData(); + + this.rs = metaData.getColumns("dbbug61332", null, "bug61332", null); + this.rs.next(); + } finally { + } + } + } + + public static class StatementInterceptorBug61332 extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection conn) + throws SQLException { + if (interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + sql = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).getPreparedSql(); + assertTrue("Assereet failed on: " + sql, + StringUtils.indexOfIgnoreCase(0, sql, "WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME LIKE ?") > -1); + } + return null; + } + } + + public void testQuotedGunk() throws Exception { + createTable("testQuotedGunk", "(field1 int)"); + + String quotedCatalog = "`" + this.conn.getCatalog() + "`"; + String unquotedCatalog = this.conn.getCatalog(); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getTables(quotedCatalog, null, "testQuotedGunk", new String[] { "TABLE" }); + assertTrue(this.rs.next()); + this.rs = dbmd.getTables(unquotedCatalog, null, "testQuotedGunk", new String[] { "TABLE" }); + assertTrue(this.rs.next()); + this.rs = dbmd.getColumns(quotedCatalog, null, "testQuotedGunk", "field1"); + assertTrue(this.rs.next()); + this.rs = dbmd.getColumns(unquotedCatalog, null, "testQuotedGunk", "field1"); + assertTrue(this.rs.next()); + + } + + /** + * Tests fix for BUG#61203 - noAccessToProcedureBodies does not work anymore. + * + * @throws Exception + * if the test fails. + */ + public void testBug61203() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; // no stored procedures + } + + Connection rootConn = null; + Connection userConn = null; + CallableStatement cStmt = null; + + try { + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + createUser("'bug61203user'@'%'", "identified by 'foo'"); + this.stmt.executeUpdate("delete from mysql.db where user='bug61203user'"); + this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ('%', '" + dbname + + "', 'bug61203user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N')"); + this.stmt.executeUpdate("insert into mysql.db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv,Drop_priv, " + + "Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv," + + "Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES " + + "('%', 'information\\_schema', 'bug61203user', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', " + + "'Y', 'N', 'N')"); + this.stmt.executeUpdate("flush privileges"); + + // 1. underprivileged user is the creator + this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testbug61203fn;"); + this.stmt.executeUpdate("CREATE DEFINER='bug61203user'@'%' FUNCTION testbug61203fn(a float) RETURNS INT NO SQL BEGIN RETURN a; END"); + this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testbug61203pr;"); + this.stmt.executeUpdate( + "CREATE DEFINER='bug61203user'@'%' PROCEDURE testbug61203pr(INOUT a float, b bigint, c int) " + "NO SQL BEGIN SET @a = b + c; END"); + testBug61203checks(rootConn, userConn); + this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testbug61203fn;"); + this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testbug61203pr;"); + + // 2. root user is the creator + createFunction("testbug61203fn", "(a float) RETURNS INT NO SQL BEGIN RETURN a; END"); + createProcedure("testbug61203pr", "(INOUT a float, b bigint, c int) NO SQL BEGIN SET @a = b + c; END"); + testBug61203checks(rootConn, userConn); + + } finally { + dropFunction("testbug61203fn"); + dropProcedure("testbug61203pr"); + + if (cStmt != null) { + cStmt.close(); + } + if (rootConn != null) { + rootConn.close(); + } + if (userConn != null) { + userConn.close(); + } + } + } + + private void testBug61203checks(Connection rootConn, Connection userConn) throws SQLException { + CallableStatement cStmt = null; + // 1.1. with information schema + rootConn = getConnectionWithProps("noAccessToProcedureBodies=true,useInformationSchema=true"); + userConn = getConnectionWithProps("noAccessToProcedureBodies=true,useInformationSchema=true,user=bug61203user,password=foo"); + // 1.1.1. root call; + callFunction(cStmt, rootConn); + callProcedure(cStmt, rootConn); + // 1.1.2. underprivileged user call; + callFunction(cStmt, userConn); + callProcedure(cStmt, userConn); + + // 1.2. no information schema + rootConn = getConnectionWithProps("noAccessToProcedureBodies=true,useInformationSchema=false"); + userConn = getConnectionWithProps("noAccessToProcedureBodies=true,useInformationSchema=false,user=bug61203user,password=foo"); + // 1.2.1. root call; + callFunction(cStmt, rootConn); + callProcedure(cStmt, rootConn); + // 1.2.2. underprivileged user call; + callFunction(cStmt, userConn); + callProcedure(cStmt, userConn); + } + + private void callFunction(CallableStatement cStmt, Connection c) throws SQLException { + cStmt = c.prepareCall("{? = CALL testbug61203fn(?)}"); + cStmt.registerOutParameter(1, Types.INTEGER); + cStmt.setFloat(2, 2); + cStmt.execute(); + assertEquals(2f, cStmt.getInt(1), .001); + } + + private void callProcedure(CallableStatement cStmt, Connection c) throws SQLException { + cStmt = c.prepareCall("{CALL testbug61203pr(?,?,?)}"); + cStmt.setFloat(1, 2); + cStmt.setInt(2, 1); + cStmt.setInt(3, 1); + cStmt.registerOutParameter(1, Types.INTEGER); + cStmt.execute(); + assertEquals(2f, cStmt.getInt(1), .001); + } + + /** + * Tests fix for BUG#63456 - MetaData precision is different when using UTF8 or Latin1 tables + * + * @throws Exception + * if the test fails. + */ + public void testBug63456() throws Exception { + + //createTable("testBug63456_custom1", "(TEST VARCHAR(10)) ENGINE = MyISAM CHARACTER SET custom1 COLLATE custom1_general_ci"); + createTable("testBug63456_latin1", "(TEST VARCHAR(10)) DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci"); + createTable("testBug63456_utf8", "(TEST VARCHAR(10)) DEFAULT CHARACTER SET utf8"); + createTable("testBug63456_utf8_bin", "(TEST VARCHAR(10)) DEFAULT CHARACTER SET utf8 COLLATE utf8_bin"); + + //this.rs = this.stmt.executeQuery("select * from testBug63456_custom1"); + //int precision_custom1 = this.rs.getMetaData().getPrecision(1); + //assertEquals(10, precision_custom1); + + this.rs = this.stmt.executeQuery("select * from testBug63456_latin1"); + int precision_latin1 = this.rs.getMetaData().getPrecision(1); + + this.rs = this.stmt.executeQuery("select * from testBug63456_utf8"); + int precision_utf8 = this.rs.getMetaData().getPrecision(1); + + this.rs = this.stmt.executeQuery("select * from testBug63456_utf8_bin"); + int precision_utf8bin = this.rs.getMetaData().getPrecision(1); + + assertEquals(precision_latin1, precision_utf8); + assertEquals(precision_utf8, precision_utf8bin); + + } + + /** + * Tests fix for BUG#63800 - getVersionColumns() does not return timestamp fields; always empty. + * + * @throws Exception + * if the test fails. + */ + public void testBug63800() throws Exception { + try { + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + fail("No database selected"); + } + + for (String prop : new String[] { "dummyProp", "useInformationSchema" }) { + props = new Properties(); + if (versionMeetsMinimum(5, 7, 4)) { + props.put("jdbcCompliantTruncation", "false"); + } + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + props.setProperty(prop, "true"); + Connection conn2 = getConnectionWithProps(props); + Statement stmt2 = null; + + try { + stmt2 = conn2.createStatement(); + testTimestamp(conn2, stmt2, dbname); + if (versionMeetsMinimum(5, 6, 5)) { + testDatetime(conn2, stmt2, dbname); + } + } finally { + if (stmt2 != null) { + stmt2.close(); + } + if (conn2 != null) { + conn2.close(); + } + } + } + } finally { + dropTable("testBug63800"); + } + } + + private void testTimestamp(Connection con, Statement st, String dbname) throws SQLException { + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)"); + DatabaseMetaData dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertFalse("0 column must be found", this.rs.next()); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP DEFAULT 0)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertFalse("0 column must be found", this.rs.next()); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP NULL, f2 TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f2"); + + // ALTER test + st.execute("ALTER TABLE testBug63800 CHANGE COLUMN `f2` `f2` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', " + + "ADD COLUMN `f3` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP AFTER `f2`"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f3"); + } + + private void testDatetime(Connection con, Statement st, String dbname) throws SQLException { + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)"); + DatabaseMetaData dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertFalse("0 column must be found", this.rs.next()); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME DEFAULT CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertFalse("0 column must be found", this.rs.next()); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME DEFAULT 0)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertFalse("0 column must be found", this.rs.next()); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + assertTrue("1 column must be found", this.rs.next()); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f1"); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 DATETIME NULL, f2 DATETIME ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + int cnt = 0; + while (this.rs.next()) { + cnt++; + assertEquals("1 column must be found", cnt, 1); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f2"); + } + + // ALTER 1 test + st.execute("ALTER TABLE testBug63800 CHANGE COLUMN `f2` `f2` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', " + + "ADD COLUMN `f3` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP AFTER `f2`"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + cnt = 0; + while (this.rs.next()) { + cnt++; + assertEquals("1 column must be found", cnt, 1); + assertEquals("Wrong column or single column not found", this.rs.getString(2), "f3"); + } + + // ALTER 2 test + st.execute("ALTER TABLE testBug63800 CHANGE COLUMN `f2` `f2` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + cnt = 0; + while (this.rs.next()) { + cnt++; + } + assertEquals("2 column must be found", cnt, 2); + + st.execute("DROP TABLE IF EXISTS testBug63800"); + st.execute("CREATE TABLE testBug63800(f1 TIMESTAMP, f2 DATETIME ON UPDATE CURRENT_TIMESTAMP, f3 TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP)"); + dmd = con.getMetaData(); + this.rs = dmd.getVersionColumns(dbname, dbname, "testBug63800"); + cnt = 0; + while (this.rs.next()) { + cnt++; + } + assertEquals("3 column must be found", cnt, 3); + + } + + /** + * Tests fix for BUG#16436511 - getDriverName() returns a string with company name "MySQL-AB" + * + * @throws Exception + * if the test fails. + */ + public void testBug16436511() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + assertEquals("MySQL Connector Java", dbmd.getDriverName()); + } + + /** + * Test fix for BUG#68098 - DatabaseMetaData.getIndexInfo sorts results incorrectly. + * + * @throws Exception + * if the test fails. + */ + public void testBug68098() throws Exception { + String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + String[] expectedIndexesOrder = new String[] { "index_1", "index_1", "index_3", "PRIMARY", "index_2", "index_2", "index_4" }; + + this.stmt.execute("DROP TABLE IF EXISTS testBug68098"); + + createTable("testBug68098", "(column_1 INT NOT NULL, column_2 INT NOT NULL, column_3 INT NOT NULL, PRIMARY KEY (column_1))"); + + this.stmt.execute("CREATE INDEX index_4 ON testBug68098 (column_2)"); + this.stmt.execute("CREATE UNIQUE INDEX index_3 ON testBug68098 (column_3)"); + this.stmt.execute("CREATE INDEX index_2 ON testBug68098 (column_2, column_1)"); + this.stmt.execute("CREATE UNIQUE INDEX index_1 ON testBug68098 (column_3, column_2)"); + + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getIndexInfo(null, null, "testBug68098", false, false); + int ind = 0; + while (this.rs.next()) { + assertEquals(testStepDescription[i] + ", sort order is wrong", expectedIndexesOrder[ind++], this.rs.getString("INDEX_NAME")); + } + this.rs.close(); + } + + connUseIS.close(); + } + + /** + * Tests fix for BUG#68307 - getFunctionColumns() returns incorrect "COLUMN_TYPE" information. This JDBC4 + * feature required some changes in method getProcedureColumns(). + * + * @throws Exception + * if the test fails. + */ + public void testBug68307() throws Exception { + String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + + createFunction("testBug68307_func", "(func_param_in INT) RETURNS INT DETERMINISTIC RETURN 1"); + + createProcedure("testBug68307_proc", "(IN proc_param_in INT, OUT proc_param_out INT, INOUT proc_param_inout INT) SELECT 1"); + + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getProcedureColumns(null, null, "testBug68307_%", "%"); + + while (this.rs.next()) { + String message = testStepDescription[i] + ", procedure/function <" + this.rs.getString("PROCEDURE_NAME") + "." + + this.rs.getString("COLUMN_NAME") + ">"; + if (this.rs.getString("COLUMN_NAME") == null || this.rs.getString("COLUMN_NAME").length() == 0) { + assertEquals(message, DatabaseMetaData.procedureColumnReturn, this.rs.getShort("COLUMN_TYPE")); + } else if (this.rs.getString("COLUMN_NAME").endsWith("_in")) { + assertEquals(message, DatabaseMetaData.procedureColumnIn, this.rs.getShort("COLUMN_TYPE")); + } else if (this.rs.getString("COLUMN_NAME").endsWith("_inout")) { + assertEquals(message, DatabaseMetaData.procedureColumnInOut, this.rs.getShort("COLUMN_TYPE")); + } else if (this.rs.getString("COLUMN_NAME").endsWith("_out")) { + assertEquals(message, DatabaseMetaData.procedureColumnOut, this.rs.getShort("COLUMN_TYPE")); + } else { + fail(testStepDescription[i] + ", column '" + this.rs.getString("FUNCTION_NAME") + "." + this.rs.getString("COLUMN_NAME") + + "' not expected within test case."); + } + } + + this.rs.close(); + } + } + + /** + * Tests fix for BUG#44451 - getTables does not return resultset with expected columns. + * + * @throws Exception + * if the test fails. + */ + public void testBug44451() throws Exception { + String methodName; + List expectedFields; + String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + + methodName = "getColumns()"; + expectedFields = Arrays.asList("TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME", "DATA_TYPE", "TYPE_NAME", "COLUMN_SIZE", "BUFFER_LENGTH", + "DECIMAL_DIGITS", "NUM_PREC_RADIX", "NULLABLE", "REMARKS", "COLUMN_DEF", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH", + "ORDINAL_POSITION", "IS_NULLABLE", "SCOPE_CATALOG", "SCOPE_SCHEMA", "SCOPE_TABLE", "SOURCE_DATA_TYPE", "IS_AUTOINCREMENT", + "IS_GENERATEDCOLUMN"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getColumns(null, null, "%", "%"); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + this.rs.close(); + } + + methodName = "getProcedureColumns()"; + expectedFields = Arrays.asList("PROCEDURE_CAT", "PROCEDURE_SCHEM", "PROCEDURE_NAME", "COLUMN_NAME", "COLUMN_TYPE", "DATA_TYPE", "TYPE_NAME", + "PRECISION", "LENGTH", "SCALE", "RADIX", "NULLABLE", "REMARKS", "COLUMN_DEF", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH", + "ORDINAL_POSITION", "IS_NULLABLE", "SPECIFIC_NAME"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getProcedureColumns(null, null, "%", "%"); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + this.rs.close(); + } + + methodName = "getTables()"; + expectedFields = Arrays.asList("TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "TABLE_TYPE", "REMARKS", "TYPE_CAT", "TYPE_SCHEM", "TYPE_NAME", + "SELF_REFERENCING_COL_NAME", "REF_GENERATION"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getTables(null, null, "%", null); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + this.rs.close(); + } + + methodName = "getUDTs()"; + expectedFields = Arrays.asList("TYPE_CAT", "TYPE_SCHEM", "TYPE_NAME", "CLASS_NAME", "DATA_TYPE", "REMARKS", "BASE_TYPE"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getUDTs(null, null, "%", null); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, this.rs); + this.rs.close(); + } + + connUseIS.close(); + } + + private void checkReturnedColumnsForBug44451(String stepDescription, String methodName, List expectedFields, ResultSet resultSetToCheck) + throws Exception { + ResultSetMetaData rsMetaData = resultSetToCheck.getMetaData(); + int numberOfColumns = rsMetaData.getColumnCount(); + + assertEquals(stepDescription + ", wrong column count in method '" + methodName + "'.", expectedFields.size(), numberOfColumns); + for (int i = 0; i < numberOfColumns; i++) { + int position = i + 1; + assertEquals(stepDescription + ", wrong column at position '" + position + "' in method '" + methodName + "'.", expectedFields.get(i), + rsMetaData.getColumnName(position)); + } + this.rs.close(); + } + + /** + * Tests fix for BUG#65871 - DatabaseMetaData.getColumns() thows an MySQLSyntaxErrorException. + * Delimited names of databases and tables are handled correctly now. The edge case is ANSI quoted + * identifiers with leading and trailing "`" symbols, for example CREATE DATABASE "`dbname`". Methods + * like DatabaseMetaData.getColumns() allow parameters passed both in unquoted and quoted form, + * quoted form is not JDBC-compliant but used by third party tools. So when you pass the indentifier + * "`dbname`" in unquoted form (`dbname`) driver handles it as quoted by "`" symbol. To handle such + * identifiers correctly a new behavior was added to pedantic mode (connection property pedantic=true), + * now if it set to true methods like DatabaseMetaData.getColumns() treat all parameters as unquoted. + * + * @throws Exception + * if the test fails. + */ + public void testBug65871() throws Exception { + createTable("testbug65871_foreign", + "(cpd_foreign_1_id int(8) not null, cpd_foreign_2_id int(8) not null," + "primary key (cpd_foreign_1_id, cpd_foreign_2_id)) ", "InnoDB"); + + Connection pedanticConn = null; + Connection pedanticConn_IS = null; + Connection nonPedanticConn = null; + Connection nonPedanticConn_IS = null; + + try { + Properties props = new Properties(); + props.setProperty("sessionVariables", "sql_mode=ansi"); + nonPedanticConn = getConnectionWithProps(props); + + props.setProperty("useInformationSchema", "true"); + nonPedanticConn_IS = getConnectionWithProps(props); + + props.setProperty("pedantic", "true"); + pedanticConn_IS = getConnectionWithProps(props); + + props.setProperty("useInformationSchema", "false"); + pedanticConn = getConnectionWithProps(props); + + System.out.println("1. Non-pedantic, without I_S."); + testBug65871_testCatalogs(nonPedanticConn); + + System.out.println("2. Pedantic, without I_S."); + testBug65871_testCatalogs(pedanticConn); + + System.out.println("3. Non-pedantic, with I_S."); + testBug65871_testCatalogs(nonPedanticConn_IS); + + System.out.println("4. Pedantic, with I_S."); + testBug65871_testCatalogs(pedanticConn_IS); + + } finally { + if (pedanticConn != null) { + pedanticConn.close(); + } + if (nonPedanticConn != null) { + nonPedanticConn.close(); + } + } + } + + private void testBug65871_testCatalogs(Connection conn1) throws Exception { + testBug65871_testCatalog("db1`testbug65871", StringUtils.quoteIdentifier("db1`testbug65871", ((ConnectionProperties) conn1).getPedantic()), conn1); + + testBug65871_testCatalog("db2`testbug65871", StringUtils.quoteIdentifier("db2`testbug65871", "\"", ((ConnectionProperties) conn1).getPedantic()), + conn1); + + testBug65871_testCatalog("`db3`testbug65871`", StringUtils.quoteIdentifier("`db3`testbug65871`", "\"", ((ConnectionProperties) conn1).getPedantic()), + conn1); + } + + private void testBug65871_testCatalog(String unquotedDbName, String quotedDbName, Connection conn1) throws Exception { + + Statement st1 = null; + + try { + st1 = conn1.createStatement(); + + // 1. catalog + st1.executeUpdate("DROP DATABASE IF EXISTS " + quotedDbName); + st1.executeUpdate("CREATE DATABASE " + quotedDbName); + this.rs = st1.executeQuery("show databases like '" + unquotedDbName + "'"); + if (this.rs.next()) { + assertEquals(unquotedDbName, this.rs.getString(1)); + } else { + fail("Database " + unquotedDbName + " (quoted " + quotedDbName + ") not found."); + } + + testBug65871_testTable(unquotedDbName, quotedDbName, "table1`testbug65871", + StringUtils.quoteIdentifier("table1`testbug65871", ((ConnectionProperties) conn1).getPedantic()), conn1, st1); + + testBug65871_testTable(unquotedDbName, quotedDbName, "table2`testbug65871", + StringUtils.quoteIdentifier("table2`testbug65871", "\"", ((ConnectionProperties) conn1).getPedantic()), conn1, st1); + + testBug65871_testTable(unquotedDbName, quotedDbName, "table3\"testbug65871", + StringUtils.quoteIdentifier("table3\"testbug65871", "\"", ((ConnectionProperties) conn1).getPedantic()), conn1, st1); + + testBug65871_testTable(unquotedDbName, quotedDbName, "`table4`testbug65871`", + StringUtils.quoteIdentifier("`table4`testbug65871`", "\"", ((ConnectionProperties) conn1).getPedantic()), conn1, st1); + + } finally { + if (st1 != null) { + st1.executeUpdate("DROP DATABASE IF EXISTS " + quotedDbName); + st1.close(); + } + } + + } + + private void testBug65871_testTable(String unquotedDbName, String quotedDbName, String unquotedTableName, String quotedTableName, Connection conn1, + Statement st1) throws Exception { + + StringBuilder failedTests = new StringBuilder(); + try { + + String sql = "CREATE TABLE " + quotedDbName + "." + quotedTableName + "(\"`B`EST`\" INT NOT NULL PRIMARY KEY, `C\"1` int(11) DEFAULT NULL," + + " TS TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, \"cpd_f\"\"oreign_1_id\" int(8) not null," + + " \"`cpd_f\"\"oreign_2_id`\" int(8) not null, KEY `NEWINX` (`C\"1`), KEY `NEWINX2` (`C\"1`, `TS`)," + + " foreign key (\"cpd_f\"\"oreign_1_id\", \"`cpd_f\"\"oreign_2_id`\") references " + this.conn.getCatalog() + + ".testbug65871_foreign(cpd_foreign_1_id, cpd_foreign_2_id), CONSTRAINT `APPFK` FOREIGN KEY (`C\"1`) REFERENCES " + quotedDbName + "." + + quotedTableName + " (`C\"1`)) ENGINE=InnoDB"; + st1.executeUpdate(sql); + + // 1. Create table + try { + this.rs = st1.executeQuery("SHOW TABLES FROM " + quotedDbName + " LIKE '" + unquotedTableName + "'"); + if (!this.rs.next() || !unquotedTableName.equals(this.rs.getString(1))) { + failedTests.append(sql + "\n"); + } + } catch (Exception e) { + failedTests.append(sql + "\n"); + } + + // 2. extractForeignKeyFromCreateTable(...) + if (!(versionMeetsMinimum(5, 1) && !versionMeetsMinimum(5, 2))) { + try { + this.rs = ((com.mysql.jdbc.DatabaseMetaData) conn1.getMetaData()).extractForeignKeyFromCreateTable(unquotedDbName, unquotedTableName); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.extractForeignKeyFromCreateTable(unquotedDbName, unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.extractForeignKeyFromCreateTable(unquotedDbName, unquotedTableName);\n"); + } + } + + // 3. getColumns(...) + try { + boolean found = false; + this.rs = conn1.getMetaData().getColumns(unquotedDbName, null, unquotedTableName, "`B`EST`"); + while (this.rs.next()) { + if ("`B`EST`".equals(this.rs.getString("COLUMN_NAME"))) { + found = true; + } + } + if (!found) { + failedTests.append("conn.getMetaData.getColumns(unquotedDbName, null, unquotedTableName, null);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getColumns(unquotedDbName, null, unquotedTableName, null);\n"); + } + + // 4. getBestRowIdentifier(...) + try { + this.rs = conn1.getMetaData().getBestRowIdentifier(unquotedDbName, null, unquotedTableName, DatabaseMetaData.bestRowNotPseudo, true); + if (!this.rs.next() || !"`B`EST`".equals(this.rs.getString("COLUMN_NAME"))) { + failedTests.append( + "conn.getMetaData.getBestRowIdentifier(unquotedDbName, null, unquotedTableName, DatabaseMetaData.bestRowNotPseudo, " + "true);\n"); + } + } catch (Exception e) { + failedTests + .append("conn.getMetaData.getBestRowIdentifier(unquotedDbName, null, unquotedTableName, DatabaseMetaData.bestRowNotPseudo, true);\n"); + } + + // 5. getCrossReference(...) + try { + this.rs = conn1.getMetaData().getCrossReference(this.conn.getCatalog(), null, "testbug65871_foreign", unquotedDbName, null, unquotedTableName); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getCrossReference(this.conn.getCatalog(), null, \"testbug65871_foreign\", unquotedDbName, null, " + + "unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getCrossReference(this.conn.getCatalog(), null, \"testbug65871_foreign\", unquotedDbName, null, " + + "unquotedTableName);\n"); + } + + // 6.getExportedKeys(...) + try { + this.rs = conn1.getMetaData().getExportedKeys(unquotedDbName, null, unquotedTableName); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getExportedKeys(unquotedDbName, null, unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getExportedKeys(unquotedDbName, null, unquotedTableName);\n"); + } + + // 7. getImportedKeys(...) + try { + this.rs = conn1.getMetaData().getImportedKeys(unquotedDbName, null, unquotedTableName); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getImportedKeys(unquotedDbName, null, unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getImportedKeys(unquotedDbName, null, unquotedTableName);\n"); + } + + // 8. getIndexInfo(...) + try { + this.rs = conn1.getMetaData().getIndexInfo(unquotedDbName, null, unquotedTableName, true, false); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getIndexInfo(unquotedDbName, null, unquotedTableName, true, false);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getIndexInfo(unquotedDbName, null, unquotedTableName, true, false);\n"); + } + + // 9. getPrimaryKeys(...) + try { + this.rs = conn1.getMetaData().getPrimaryKeys(unquotedDbName, null, unquotedTableName); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getPrimaryKeys(unquotedDbName, null, unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getPrimaryKeys(unquotedDbName, null, unquotedTableName);\n"); + } + + // 10. getTables(...) + try { + this.rs = conn1.getMetaData().getTables(unquotedDbName, null, unquotedTableName, new String[] { "TABLE" }); + if (!this.rs.next()) { + failedTests.append("conn.getMetaData.getTables(unquotedDbName, null, unquotedTableName, new String[] {\"TABLE\"});\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getTables(unquotedDbName, null, unquotedTableName, new String[] {\"TABLE\"});\n"); + } + + // 11. getVersionColumns(...) + try { + this.rs = conn1.getMetaData().getVersionColumns(unquotedDbName, null, unquotedTableName); + if (!this.rs.next() || !"TS".equals(this.rs.getString(2))) { + failedTests.append("conn.getMetaData.getVersionColumns(unquotedDbName, null, unquotedTableName);\n"); + } + } catch (Exception e) { + failedTests.append("conn.getMetaData.getVersionColumns(unquotedDbName, null, unquotedTableName);\n"); + } + + } finally { + try { + st1.executeUpdate("DROP TABLE IF EXISTS " + quotedDbName + "." + quotedTableName); + } catch (Exception e) { + failedTests.append("DROP TABLE IF EXISTS " + quotedDbName + "." + quotedTableName + "\n"); + } + } + + if (failedTests.length() > 0) { + throw new Exception("Failed tests for catalog " + quotedDbName + " and table " + quotedTableName + " (" + + (((ConnectionProperties) conn1).getPedantic() ? "pedantic mode" : "non-pedantic mode") + "):\n" + failedTests.toString()); + } + } + + /** + * Tests fix for BUG#69298 - Different outcome from DatabaseMetaData.getFunctions() when using I__S. + * + * @throws Exception + * if the test fails. + */ + public void testBug69298() throws Exception { + if (Util.isJdbc4()) { + return; + } + + Connection testConn; + + createFunction("testBug69298_func", "(param_func INT) RETURNS INT COMMENT 'testBug69298_func comment' DETERMINISTIC RETURN 1"); + createProcedure("testBug69298_proc", "(IN param_proc INT) COMMENT 'testBug69298_proc comment' SELECT 1"); + + // test with standard connection + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) this.conn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) this.conn).getGetProceduresReturnsFunctions()); + checkGetProceduresForBug69298("Std. Connection MetaData", this.conn); + checkGetProcedureColumnsForBug69298("Std. Connection MetaData", this.conn); + + // test with property useInformationSchema=true + testConn = getConnectionWithProps("useInformationSchema=true"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetProceduresForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + testConn.close(); + + // test with property getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("getProceduresReturnsFunctions=false"); + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetProceduresForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + testConn.close(); + + // test with property useInformationSchema=true & getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("useInformationSchema=true,getProceduresReturnsFunctions=false"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetProceduresForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + testConn.close(); + } + + private void checkGetProceduresForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet proceduresMD = testDbMetaData.getProcedures(null, null, "testBug69298_%"); + String sd = stepDescription + " getProcedures() "; + + assertTrue(sd + "1st of 2 rows expected.", proceduresMD.next()); + + // function: testBug69298_func + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), proceduresMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, proceduresMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", proceduresMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> REMARKS", "testBug69298_func comment", proceduresMD.getString("REMARKS")); + assertEquals(sd + "-> PROCEDURE_TYPE", DatabaseMetaData.procedureReturnsResult, proceduresMD.getShort("PROCEDURE_TYPE")); + + assertTrue(sd + "2nd of 2 rows expected.", proceduresMD.next()); + + // procedure: testBug69298_proc + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), proceduresMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, proceduresMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_proc", proceduresMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> REMARKS", "testBug69298_proc comment", proceduresMD.getString("REMARKS")); + assertEquals(sd + "-> PROCEDURE_TYPE", DatabaseMetaData.procedureNoResult, proceduresMD.getShort("PROCEDURE_TYPE")); + + assertFalse(stepDescription + "no more rows expected.", proceduresMD.next()); + } + + private void checkGetProcedureColumnsForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet procColsMD = testDbMetaData.getProcedureColumns(null, null, "testBug69298_%", "%"); + String sd = stepDescription + " getProcedureColumns() "; + + assertTrue(sd + "1st of 3 rows expected.", procColsMD.next()); + + // function column: testBug69298_func return + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnReturn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + + assertTrue(sd + "2nd of 3 rows expected.", procColsMD.next()); + + // function column: testBug69298_func.param_func + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "param_func", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnIn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + + assertTrue(sd + "3rd of 3 rows expected.", procColsMD.next()); + + // procedure column: testBug69298_proc.param_proc + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_proc", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "param_proc", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnIn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + + assertFalse(sd + "no more rows expected.", procColsMD.next()); + } + + /** + * Tests fix for BUG#17248345 - GETFUNCTIONCOLUMNS() METHOD RETURNS COLUMNS OF PROCEDURE. (this happens when + * functions and procedures have a common name) + * + * @throws Exception + * if the test fails. + */ + public void testBug17248345() throws Exception { + if (Util.isJdbc4()) { + // there is a specific JCDB4 test for this + return; + } + + Connection testConn; + + // create one stored procedure and one function with same name + createFunction("testBug17248345", "(funccol INT) RETURNS INT DETERMINISTIC RETURN 1"); + createProcedure("testBug17248345", "(IN proccol INT) SELECT 1"); + + // test with standard connection (getProceduresReturnsFunctions=true & useInformationSchema=false) + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) this.conn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) this.conn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(this.conn); + + // test with property useInformationSchema=true (getProceduresReturnsFunctions=true) + testConn = getConnectionWithProps("useInformationSchema=true"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + + // test with property getProceduresReturnsFunctions=false (useInformationSchema=false) + testConn = getConnectionWithProps("getProceduresReturnsFunctions=false"); + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + + // test with property useInformationSchema=true & getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("useInformationSchema=true,getProceduresReturnsFunctions=false"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + } + + private void checkMetaDataInfoForBug17248345(Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet rsMD; + boolean useInfoSchema = ((ConnectionProperties) testConn).getUseInformationSchema(); + boolean getProcRetFunc = ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions(); + String stepDescription = "Prop. useInfoSchema(" + (useInfoSchema ? 1 : 0) + ") + getProcRetFunc(" + (getProcRetFunc ? 1 : 0) + "):"; + String sd; + + // getProcedures() must return 2 records, even if getProceduresReturnsFunctions is false once this flag only + // applies to JDBC4. When exists a procedure and a function with same name, function is returned first. + sd = stepDescription + " getProcedures() "; + rsMD = testDbMetaData.getProcedures(null, null, "testBug17248345"); + assertTrue(sd + "1st of 2 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertTrue(sd + "2nd of 2 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + + // getProcedureColumns() must return 3 records, even if getProceduresReturnsFunctions is false once this flag + // only applies to JDBC4. When exists a procedure and a function with same name, function is returned first. + sd = stepDescription + " getProcedureColumns() "; + rsMD = testDbMetaData.getProcedureColumns(null, null, "testBug17248345", "%"); + assertTrue(sd + "1st of 3 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "", rsMD.getString("COLUMN_NAME")); + assertTrue(sd + "2nd of 3 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "funccol", rsMD.getString("COLUMN_NAME")); + assertTrue(sd + "3rd of 3 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "proccol", rsMD.getString("COLUMN_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + } + + /** + * Tests fix for BUG#69290 - JDBC Table type "SYSTEM TABLE" is used inconsistently. + * + * Tests DatabaseMetaData.getTableTypes() and DatabaseMetaData.getTables() against schemas: mysql, + * information_schema, performance_schema, test. + * + * @throws Exception + * if the test fails. + */ + public void testBug69290() throws Exception { + String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + Connection connNullAll = getConnectionWithProps("nullCatalogMeansCurrent=false"); + Connection connUseISAndNullAll = getConnectionWithProps("useInformationSchema=true,nullCatalogMeansCurrent=false"); + final String testCatalog = this.conn.getCatalog(); + + Connection[] testConnections = new Connection[] { this.conn, connUseIS }; + + // check table types returned in getTableTypes() + final List tableTypes = Arrays.asList(new String[] { "LOCAL TEMPORARY", "SYSTEM TABLE", "SYSTEM VIEW", "TABLE", "VIEW" }); + + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + this.rs = testDbMetaData.getTableTypes(); + + int idx = 0; + while (this.rs.next()) { + String message = testStepDescription[i] + ", table type '" + this.rs.getString("TABLE_TYPE") + "'"; + if (idx >= tableTypes.size()) { + fail(message + " not expected."); + } + assertEquals(message, tableTypes.get(idx++), this.rs.getString("TABLE_TYPE")); + } + } + + // create table and view in '(test)' schema + createTable("testBug69290_table", "(c1 INT)"); + createView("testBug69290_view", "AS SELECT * FROM testBug69290_table WHERE c1 > 1"); + + int[][] countResults = new int[][] { { 0, 0, 0 }, { 0, 0, 0 } }; + + // check table types returned in getTables() for each catalog/schema + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + + // check catalog/schema 'information_schema' + this.rs = testDbMetaData.getTables("information_schema", null, "%", null); + while (this.rs.next()) { + assertEquals(testStepDescription[i] + ", 'information_schema' catalog/schema, wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + "SYSTEM VIEW", this.rs.getString("TABLE_TYPE")); + countResults[i][0]++; + } + + // check catalog/schema 'mysql' + this.rs = testDbMetaData.getTables("mysql", null, "%", null); + while (this.rs.next()) { + assertEquals(testStepDescription[i] + ", 'mysql' catalog/schema, wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + "SYSTEM TABLE", this.rs.getString("TABLE_TYPE")); + countResults[i][1]++; + } + + // check catalog/schema 'performance_schema' + this.rs = testDbMetaData.getTables("performance_schema", null, "%", null); + while (this.rs.next()) { + assertEquals(testStepDescription[i] + ", 'performance_schema' catalog/schema, wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + "SYSTEM TABLE", this.rs.getString("TABLE_TYPE")); + countResults[i][2]++; + } + + // check catalog/schema '(test)' + this.rs = testDbMetaData.getTables(testCatalog, null, "testBug69290_%", null); + assertTrue(testStepDescription[i] + ", '" + testCatalog + "' catalog/schema, expected row from getTables().", this.rs.next()); + assertEquals(testStepDescription[i] + ", '" + testCatalog + "' catalog/schema, wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + "TABLE", this.rs.getString("TABLE_TYPE")); + assertTrue(testStepDescription[i] + ", '" + testCatalog + "' catalog/schema, expected row from getTables().", this.rs.next()); + assertEquals(testStepDescription[i] + ", '" + testCatalog + "' catalog/schema, wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + "VIEW", this.rs.getString("TABLE_TYPE")); + } + + // compare results count + assertTrue("The number of results from getTables() MySQl(" + countResults[0][0] + ") and I__S(" + countResults[1][0] + + ") should be the same for 'information_schema' catalog/schema.", countResults[0][0] == countResults[1][0]); + assertTrue("The number of results from getTables() MySQl(" + countResults[0][1] + ") and I__S(" + countResults[1][1] + + ") should be the same for 'mysql' catalog/schema.", countResults[0][1] == countResults[1][1]); + assertTrue("The number of results from getTables() MySQl(" + countResults[0][2] + ") and I__S(" + countResults[1][2] + + ") should be the same for 'performance_schema' catalog/schema.", countResults[0][2] == countResults[1][2]); + + testConnections = new Connection[] { connNullAll, connUseISAndNullAll }; + countResults = new int[][] { { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; + + // check table types returned in getTables() for all catalogs/schemas and filter by table type (tested with property nullCatalogMeansCurrent=false) + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + int j = 0; + + // check table type filters + for (String tableType : tableTypes) { + this.rs = testDbMetaData.getTables(null, null, "%", new String[] { tableType }); + while (this.rs.next()) { + assertEquals( + testStepDescription[i] + ", table type filter '" + tableType + "', wrong table type for '" + this.rs.getString("TABLE_NAME") + "'.", + tableType, this.rs.getString("TABLE_TYPE")); + countResults[i][j]++; + } + j++; + } + } + + // compare results count + int i = 0; + for (String tableType : tableTypes) { + assertTrue("The number of results from getTables() MySQl(" + countResults[0][i] + ") and I__S(" + countResults[1][i] + ") should be the same for '" + + tableType + "' table type filter.", countResults[0][i] == countResults[1][i]); + i++; + } + } + + /** + * Tests fix for BUG#35115 - yearIsDateType=false has no effect on result's column type and class. + * + * @throws Exception + * if the test fails. + */ + public void testBug35115() throws Exception { + Connection testConnection = null; + ResultSetMetaData rsMetaData = null; + + createTable("testBug35115", "(year YEAR)"); + + this.stmt = this.conn.createStatement(); + this.stmt.executeUpdate("INSERT INTO testBug35115 VALUES ('2002'), ('2013')"); + + /* + * test connection with property 'yearIsDateType=false' + */ + testConnection = getConnectionWithProps("yearIsDateType=false"); + this.stmt = testConnection.createStatement(); + this.rs = this.stmt.executeQuery("SELECT * FROM testBug35115"); + rsMetaData = this.rs.getMetaData(); + + assertTrue(this.rs.next()); + assertEquals("YEAR columns should be treated as java.sql.Types.DATE", Types.DATE, rsMetaData.getColumnType(1)); + assertEquals("YEAR columns should be identified as 'YEAR'", "YEAR", rsMetaData.getColumnTypeName(1)); + assertEquals("YEAR columns should be mapped to java.lang.Short", java.lang.Short.class.getName(), rsMetaData.getColumnClassName(1)); + assertEquals("YEAR columns should be returned as java.lang.Short", java.lang.Short.class.getName(), this.rs.getObject(1).getClass().getName()); + + testConnection.close(); + + /* + * test connection with property 'yearIsDateType=true' + */ + testConnection = getConnectionWithProps("yearIsDateType=true"); + this.stmt = testConnection.createStatement(); + this.rs = this.stmt.executeQuery("SELECT * FROM testBug35115"); + rsMetaData = this.rs.getMetaData(); + + assertTrue(this.rs.next()); + assertEquals("YEAR columns should be treated as java.sql.Types.DATE", Types.DATE, rsMetaData.getColumnType(1)); + assertEquals("YEAR columns should be identified as 'YEAR'", "YEAR", rsMetaData.getColumnTypeName(1)); + assertEquals("YEAR columns should be mapped to java.sql.Date", java.sql.Date.class.getName(), rsMetaData.getColumnClassName(1)); + assertEquals("YEAR columns should be returned as java.sql.Date", java.sql.Date.class.getName(), this.rs.getObject(1).getClass().getName()); + + testConnection.close(); + } + + /* + * Tests DatabaseMetaData.getSQLKeywords(). + * (Related to BUG#70701 - DatabaseMetaData.getSQLKeywords() doesn't match MySQL 5.6 reserved words) + * + * The keywords list that this method returns depends on JDBC version. + * + * @throws Exception if the test fails. + */ + public void testReservedWords() throws Exception { + if (Util.isJdbc4()) { + // there is a specific JCDB4 test for this + return; + } + final String mysqlKeywords = "ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR," + + "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8," + + "FORCE,FULLTEXT,GENERATED,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,INDEX,INFILE,INOUT,INT1,INT2,INT3,INT4,INT8," + + "IO_AFTER_GTIDS,IO_BEFORE_GTIDS,ITERATE,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP," + + "LOW_PRIORITY,MASTER_BIND,MASTER_SSL_VERIFY_SERVER_CERT,MAXVALUE,MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND," + + "MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIMIZER_COSTS,OPTIONALLY,OUT,OUTFILE,PARTITION,PURGE,RANGE,READS,READ_WRITE,REGEXP,RELEASE," + + "RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SIGNAL,SPATIAL,SPECIFIC," + + "SQLEXCEPTION,SQLWARNING,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STORED,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT," + + "TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,VIRTUAL,WHILE,XOR,YEAR_MONTH,ZEROFILL"; + assertEquals("MySQL keywords don't match expected.", mysqlKeywords, this.conn.getMetaData().getSQLKeywords()); + } + + /** + * Tests fix for BUG#20504139 - GETFUNCTIONCOLUMNS() AND GETPROCEDURECOLUMNS() RETURNS ERROR FOR VALID INPUTS. + * + * Test duplicated in testsuite.regression.jdbc4.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug20504139() throws Exception { + if (Util.isJdbc4()) { + // there is a specific JCDB4 test for this + return; + } + + createFunction("testBug20504139f", "(namef CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT('Hello, ', namef, '!')"); + createFunction("`testBug20504139``f`", "(namef CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT('Hello, ', namef, '!')"); + createProcedure("testBug20504139p", "(INOUT namep CHAR(50)) SELECT CONCAT('Hello, ', namep, '!') INTO namep"); + createProcedure("`testBug20504139``p`", "(INOUT namep CHAR(50)) SELECT CONCAT('Hello, ', namep, '!') INTO namep"); + + for (int testCase = 0; testCase < 8; testCase++) { // 3 props, 8 combinations: 2^3 = 8 + boolean usePedantic = (testCase & 1) == 1; + boolean useInformationSchema = (testCase & 2) == 2; + boolean useFuncsInProcs = (testCase & 4) == 4; + + String connProps = String.format("pedantic=%s,useInformationSchema=%s,getProceduresReturnsFunctions=%s", usePedantic, useInformationSchema, + useFuncsInProcs); + System.out.printf("testBug20504139_%d: %s%n", testCase, connProps); + + Connection testConn = getConnectionWithProps(connProps); + DatabaseMetaData dbmd = testConn.getMetaData(); + + ResultSet testRs = null; + + try { + /* + * test DatabaseMetadata.getProcedureColumns for function + */ + int i = 1; + try { + for (String name : new String[] { "testBug20504139f", "testBug20504139`f" }) { + testRs = dbmd.getProcedureColumns(null, "", name, "%"); + + assertTrue(testRs.next()); + assertEquals(testCase + "." + i + ". expected function column name (empty)", "", testRs.getString(4)); + assertEquals(testCase + "." + i + ". expected function column type (empty)", DatabaseMetaData.procedureColumnReturn, testRs.getInt(5)); + assertTrue(testRs.next()); + assertEquals(testCase + "." + i + ". expected function column name", "namef", testRs.getString(4)); + assertEquals(testCase + "." + i + ". expected function column type (empty)", DatabaseMetaData.procedureColumnIn, testRs.getInt(5)); + assertFalse(testRs.next()); + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("FUNCTION `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve function columns from database meta data."); + } + throw e; + } + + /* + * test DatabaseMetadata.getProcedureColumns for procedure + */ + i = 1; + try { + for (String name : new String[] { "testBug20504139p", "testBug20504139`p" }) { + testRs = dbmd.getProcedureColumns(null, "", name, "%"); + + assertTrue(testRs.next()); + assertEquals(testCase + "." + i + ". expected procedure column name", "namep", testRs.getString(4)); + assertEquals(testCase + "." + i + ". expected procedure column type (empty)", DatabaseMetaData.procedureColumnInOut, testRs.getInt(5)); + assertFalse(testRs.next()); + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("PROCEDURE `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve procedure columns from database meta data."); + } + throw e; + } + } finally { + testConn.close(); + } + } + } + + /** + * Tests fix for BUG#21215151 - DATABASEMETADATA.GETCATALOGS() FAILS TO SORT RESULTS. + * + * DatabaseMetaData.GetCatalogs() relies on the results of 'SHOW DATABASES' which deliver a sorted list of databases except for 'information_schema' which + * is always returned in the first position. + * This test creates set of databases around the relative position of 'information_schema' and checks the ordering of the final ResultSet. + * + * @throws Exception + * if the test fails. + */ + public void testBug21215151() throws Exception { + createDatabase("z_testBug21215151"); + createDatabase("j_testBug21215151"); + createDatabase("h_testBug21215151"); + createDatabase("i_testBug21215151"); + createDatabase("a_testBug21215151"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getCatalogs(); + + System.out.println("Catalogs:"); + System.out.println("--------------------------------------------------"); + while (this.rs.next()) { + System.out.println("\t" + this.rs.getString(1)); + } + this.rs.beforeFirst(); + + // check the relative position of each element in the result set compared to the previous element. + String previousDb = ""; + while (this.rs.next()) { + assertTrue("'" + this.rs.getString(1) + "' is lexicographically lower than the previous catalog. Check the system output to see the catalogs list.", + previousDb.compareTo(this.rs.getString(1)) < 0); + previousDb = this.rs.getString(1); + } + } + + /** + * Tests fix for BUG#19803348 - GETPROCEDURES() RETURNS INCORRECT O/P WHEN USEINFORMATIONSCHEMA=FALSE. + * + * Composed by two parts: + * 1. Confirm that getProcedures() and getProcedureColumns() aren't returning more results than expected (as per reported bug). + * 2. Confirm that the results from getProcedures() and getProcedureColumns() are in the right order (secondary bug). + * + * Test duplicated in testsuite.regression.jdbc4.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug19803348() throws Exception { + Connection testConn = null; + try { + testConn = getConnectionWithProps("useInformationSchema=false,nullCatalogMeansCurrent=false"); + DatabaseMetaData dbmd = testConn.getMetaData(); + + String testDb1 = "testBug19803348_db1"; + String testDb2 = "testBug19803348_db2"; + + if (!dbmd.supportsMixedCaseIdentifiers()) { + testDb1 = testDb1.toLowerCase(); + testDb2 = testDb2.toLowerCase(); + } + + createDatabase(testDb1); + createDatabase(testDb2); + + // 1. Check if getProcedures() and getProcedureColumns() aren't returning more results than expected (as per reported bug). + createFunction(testDb1 + ".testBug19803348_f", "(d INT) RETURNS INT DETERMINISTIC BEGIN RETURN d; END"); + createProcedure(testDb1 + ".testBug19803348_p", "(d int) BEGIN SELECT d; END"); + + this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_p", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + dropFunction(testDb1 + ".testBug19803348_f"); + dropProcedure(testDb1 + ".testBug19803348_p"); + + // 2. Check if the results from getProcedures() and getProcedureColumns() are in the right order (secondary bug). + createFunction(testDb1 + ".testBug19803348_B_f", "(d INT) RETURNS INT DETERMINISTIC BEGIN RETURN d; END"); + createProcedure(testDb1 + ".testBug19803348_B_p", "(d int) BEGIN SELECT d; END"); + createFunction(testDb2 + ".testBug19803348_A_f", "(d INT) RETURNS INT DETERMINISTIC BEGIN RETURN d; END"); + createProcedure(testDb2 + ".testBug19803348_A_p", "(d int) BEGIN SELECT d; END"); + + this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_p", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_p", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + + /** + * Tests fix for BUG#20727196 - GETPROCEDURECOLUMNS() RETURNS EXCEPTION FOR FUNCTION WHICH RETURNS ENUM/SET TYPE. + * + * Test duplicated in testsuite.regression.jdbc4.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug20727196() throws Exception { + createFunction("testBug20727196_f1", + "(p ENUM ('Yes', 'No')) RETURNS VARCHAR(10) DETERMINISTIC BEGIN RETURN IF(p='Yes', 'Yay!', if(p='No', 'Ney!', 'What?')); END"); + createFunction("testBug20727196_f2", "(p CHAR(1)) RETURNS ENUM ('Yes', 'No') DETERMINISTIC BEGIN RETURN IF(p='y', 'Yes', if(p='n', 'No', '?')); END"); + createFunction("testBug20727196_f3", + "(p ENUM ('Yes', 'No')) RETURNS ENUM ('Yes', 'No') DETERMINISTIC BEGIN RETURN IF(p='Yes', 'Yes', if(p='No', 'No', '?')); END"); + createProcedure("testBug20727196_p1", "(p ENUM ('Yes', 'No')) BEGIN SELECT IF(p='Yes', 'Yay!', if(p='No', 'Ney!', 'What?')); END"); + + for (String connProps : new String[] { "useInformationSchema=false", "useInformationSchema=true" }) { + + Connection testConn = null; + try { + testConn = getConnectionWithProps(connProps); + DatabaseMetaData dbmd = testConn.getMetaData(); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug20727196_%", "%"); + + // testBug20727196_f1 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f1", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("VARCHAR", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f1", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + // testBug20727196_f2 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f2", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f2", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("CHAR", this.rs.getString(7)); + + // testBug20727196_f3 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f3", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f3", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + // testBug20727196_p1 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_p1", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + assertFalse(this.rs.next()); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#76187 (20675539), getTypeInfo report maximum precision of 255 for varchar. + * + * @throws Exception + * if the test fails. + */ + public void testBug76187() throws Exception { + + DatabaseMetaData meta = this.conn.getMetaData(); + this.rs = meta.getTypeInfo(); + while (this.rs.next()) { + if (this.rs.getString("TYPE_NAME").equals("VARCHAR")) { + if (versionMeetsMinimum(5, 0, 3)) { + assertEquals(65535, this.rs.getInt("PRECISION")); + } else { + assertEquals(255, this.rs.getInt("PRECISION")); + } + } + } + + } + + /** + * Tests fix for BUG#21978216, GETTYPEINFO REPORT MAXIMUM PRECISION OF 255 FOR VARBINARY + * + * @throws Exception + * if the test fails. + */ + public void testBug21978216() throws Exception { + + DatabaseMetaData meta = this.conn.getMetaData(); + this.rs = meta.getTypeInfo(); + while (this.rs.next()) { + if (this.rs.getString("TYPE_NAME").equals("VARBINARY")) { + if (versionMeetsMinimum(5, 0, 3)) { + assertEquals(65535, this.rs.getInt("PRECISION")); + } else { + assertEquals(255, this.rs.getInt("PRECISION")); + } + } + } + + } + + /** + * Tests fix for Bug#23212347, ALL API CALLS ON RESULTSET METADATA RESULTS IN NPE WHEN USESERVERPREPSTMTS=TRUE. + */ + public void testBug23212347() throws Exception { + boolean useSPS = false; + do { + String testCase = String.format("Case [SPS: %s]", useSPS ? "Y" : "N"); + createTable("testBug23212347", "(id INT)"); + + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", Boolean.toString(useSPS)); + + Connection testConn = getConnectionWithProps(props); + Statement testStmt = testConn.createStatement(); + testStmt.execute("INSERT INTO testBug23212347 VALUES (1)"); + + this.pstmt = testConn.prepareStatement("SELECT * FROM testBug23212347 WHERE id = 1"); + this.rs = this.pstmt.executeQuery(); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, 1, this.rs.getInt(1)); + assertFalse(testCase, this.rs.next()); + ResultSetMetaData rsmd = this.pstmt.getMetaData(); + assertEquals(testCase, "id", rsmd.getColumnName(1)); + + this.pstmt = testConn.prepareStatement("SELECT * FROM testBug23212347 WHERE id = ?"); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, 1, this.rs.getInt(1)); + assertFalse(this.rs.next()); + rsmd = this.pstmt.getMetaData(); + assertEquals(testCase, "id", rsmd.getColumnName(1)); + } while (useSPS = !useSPS); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/MicroPerformanceRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/MicroPerformanceRegressionTest.java new file mode 100644 index 0000000..7f4ce0f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/MicroPerformanceRegressionTest.java @@ -0,0 +1,498 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import com.mysql.jdbc.Util; + +import testsuite.BaseTestCase; + +/** + * Microperformance benchmarks to track increase/decrease in performance of core methods in the driver over time. + */ +public class MicroPerformanceRegressionTest extends BaseTestCase { + private static double[] scaleFactorSamples = new double[5]; + + private static double scaleFactor = 0.0; + + private final static double ORIGINAL_LOOP_TIME_MS = 2300.0; + + // (Used to be 10.0 for all but since HW and VMs are much faster now a minimal disruption can cause significant deviations) + private final static double LEEWAY = Util.getJVMVersion() < 7 ? 10.0 : 50.0; // account for VMs + + private final static Map BASELINE_TIMES = new HashMap(); + + static { + BASELINE_TIMES.put("ResultSet.getInt()", new Double(0.00661)); + BASELINE_TIMES.put("ResultSet.getDouble()", new Double(0.00671)); + BASELINE_TIMES.put("ResultSet.getTime()", new Double(0.02033)); + BASELINE_TIMES.put("ResultSet.getTimestamp()", new Double(0.02363)); + BASELINE_TIMES.put("ResultSet.getDate()", new Double(0.02223)); + BASELINE_TIMES.put("ResultSet.getString()", new Double(0.00982)); + BASELINE_TIMES.put("ResultSet.getObject() on a string", new Double(0.00861)); + BASELINE_TIMES.put("Connection.prepareStatement()", new Double(0.18547)); + BASELINE_TIMES.put("single selects", new Double(46)); + BASELINE_TIMES.put("5 standalone queries", new Double(146)); + BASELINE_TIMES.put("total time all queries", new Double(190)); + if (com.mysql.jdbc.Util.isJdbc4()) { + BASELINE_TIMES.put("PreparedStatement.setInt()", new Double(0.0014)); + BASELINE_TIMES.put("PreparedStatement.setTime()", new Double(0.0107)); + BASELINE_TIMES.put("PreparedStatement.setTimestamp()", new Double(0.0182)); + BASELINE_TIMES.put("PreparedStatement.setDate()", new Double(0.0819)); + BASELINE_TIMES.put("PreparedStatement.setString()", new Double(0.0081)); + BASELINE_TIMES.put("PreparedStatement.setObject() on a string", new Double(0.00793)); + BASELINE_TIMES.put("PreparedStatement.setDouble()", new Double(0.0246)); + } else { + BASELINE_TIMES.put("PreparedStatement.setInt()", new Double(0.0011)); + BASELINE_TIMES.put("PreparedStatement.setTime()", new Double(0.0642)); + BASELINE_TIMES.put("PreparedStatement.setTimestamp()", new Double(0.03184)); + BASELINE_TIMES.put("PreparedStatement.setDate()", new Double(0.12248)); + BASELINE_TIMES.put("PreparedStatement.setString()", new Double(0.01512)); + BASELINE_TIMES.put("PreparedStatement.setObject() on a string", new Double(0.01923)); + BASELINE_TIMES.put("PreparedStatement.setDouble()", new Double(0.00671)); + } + + System.out.println("Calculating global performance scaling factor..."); + for (int i = 0; i < scaleFactorSamples.length; i++) { + scaleFactorSamples[i] = calculateScaleFactor(); + scaleFactor += scaleFactorSamples[i]; + } + scaleFactor /= scaleFactorSamples.length; + System.out.println("Global performance scaling factor is: " + scaleFactor); + } + + public MicroPerformanceRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MicroPerformanceRegressionTest.class); + } + + /** + * Tests result set accessors performance. + * + * @throws Exception + * if the performance of these methods does not meet + * expectations. + */ + public void testResultSetAccessors() throws Exception { + if (Util.getJVMVersion() == 6 && System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1) { + /* + * Skip this test if running with Java 6 in Windows. + * This particular combination delivers an unreliable scale factor value: the performance ratio between the scale factor calculation and the code + * being tested is too divergent. + */ + return; + } + createTable("marktest", "(intField INT, floatField DOUBLE, timeField TIME, datetimeField DATETIME, stringField VARCHAR(64))"); + this.stmt.executeUpdate( + "INSERT INTO marktest VALUES (123456789, 12345.6789, NOW(), NOW(), 'abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@')"); + + this.rs = this.stmt.executeQuery("SELECT intField, floatField, timeField, datetimeField, stringField FROM marktest"); + + this.rs.next(); + + int numLoops = 100000; + + long start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getInt(1); + } + + double getIntAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getInt()", getIntAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getDouble(2); + } + + double getDoubleAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getDouble()", getDoubleAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getTime(3); + } + + double getTimeAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getTime()", getTimeAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getTimestamp(4); + } + + double getTimestampAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getTimestamp()", getTimestampAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getDate(4); + } + + double getDateAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getDate()", getDateAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getString(5); + } + + double getStringAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getString()", getStringAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + this.rs.getObject(5); + } + + double getStringObjAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("ResultSet.getObject() on a string", getStringObjAvgMs); + } + + public void testPreparedStatementTimes() throws Exception { + if (Util.getJVMVersion() == 6 && System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1) { + /* + * Skip this test if running with Java 6 in Windows. + * This particular combination delivers an unreliable scale factor value: the performance ratio between the scale factor calculation and the code + * being tested is too divergent. + */ + return; + } + createTable("marktest", "(intField INT, floatField DOUBLE, timeField TIME, datetimeField DATETIME, stringField VARCHAR(64))"); + this.stmt.executeUpdate( + "INSERT INTO marktest VALUES (123456789, 12345.6789, NOW(), NOW(), 'abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@')"); + + long start = currentTimeMillis(); + + long blockStart = currentTimeMillis(); + long lastBlock = 0; + + int numLoops = 100000; + + int numPrepares = 100000; + + if (versionMeetsMinimum(4, 1)) { + numPrepares = 10000; // we don't need to do so many for + // server-side prep statements... + } + + for (int i = 0; i < numPrepares; i++) { + if (i % 1000 == 0) { + + long blockEnd = currentTimeMillis(); + + long totalTime = blockEnd - blockStart; + + blockStart = blockEnd; + + StringBuilder messageBuf = new StringBuilder(); + + messageBuf.append(i + " prepares, the last 1000 prepares took " + totalTime + " ms"); + + if (lastBlock == 0) { + lastBlock = totalTime; + messageBuf.append("."); + } else { + double diff = (double) totalTime / (double) lastBlock; + + messageBuf.append(", difference is " + diff + " x"); + + lastBlock = totalTime; + } + + System.out.println(messageBuf.toString()); + + } + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO test.marktest VALUES (?, ?, ?, ?, ?)"); + pStmt.close(); + } + + @SuppressWarnings("unused") + double getPrepareStmtAvgMs = (double) (currentTimeMillis() - start) / numPrepares; + + // checkTime("Connection.prepareStatement()", getPrepareStmtAvgMs); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO marktest VALUES (?, ?, ?, ?, ?)"); + + System.out.println(pStmt.toString()); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + pStmt.setInt(1, 1); + } + + System.out.println(pStmt.toString()); + + double setIntAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setInt()", setIntAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + pStmt.setDouble(2, 1234567890.1234); + } + + double setDoubleAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setDouble()", setDoubleAvgMs); + + start = currentTimeMillis(); + + Time tm = new Time(start); + + for (int i = 0; i < numLoops; i++) { + pStmt.setTime(3, tm); + } + + double setTimeAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setTime()", setTimeAvgMs); + + start = currentTimeMillis(); + + Timestamp ts = new Timestamp(start); + + for (int i = 0; i < numLoops; i++) { + pStmt.setTimestamp(4, ts); + } + + double setTimestampAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setTimestamp()", setTimestampAvgMs); + + start = currentTimeMillis(); + + Date dt = new Date(start); + + for (int i = 0; i < numLoops; i++) { + pStmt.setDate(4, dt); + } + + double setDateAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setDate()", setDateAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + pStmt.setString(5, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@"); + } + + double setStringAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setString()", setStringAvgMs); + + start = currentTimeMillis(); + + for (int i = 0; i < numLoops; i++) { + pStmt.setObject(5, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@"); + } + + double setStringObjAvgMs = (double) (currentTimeMillis() - start) / numLoops; + + checkTime("PreparedStatement.setObject() on a string", setStringObjAvgMs); + + start = currentTimeMillis(); + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#setUp() + */ + @Override + public synchronized void setUp() throws Exception { + super.setUp(); + + System.out.println("Adjusting global performance scaling factor..."); + System.out.println("Gobal performance scaling factor adjusted from: " + scaleFactor + " to: " + adjustScaleFactor()); + } + + private static final double adjustScaleFactor() { + double newScaleFactor = calculateScaleFactor(); + double maxDeviation = Math.abs(newScaleFactor - scaleFactor); + + // discard the farthest value from previous mean (scaleFactor); + for (int i = 0; i < scaleFactorSamples.length; i++) { + double deviation = Math.abs(scaleFactorSamples[i] - scaleFactor); + if (deviation > maxDeviation) { + Double swapValue = scaleFactorSamples[i]; + scaleFactorSamples[i] = newScaleFactor; + newScaleFactor = swapValue; + maxDeviation = deviation; + } + } + + // calculate new mean (scaleFactor) + newScaleFactor = 0.0; + for (double d : scaleFactorSamples) { + newScaleFactor += d; + } + scaleFactor = newScaleFactor / scaleFactorSamples.length; + + return scaleFactor; + } + + private static final double calculateScaleFactor() { + // Run this simple test to get some sort of performance scaling factor, compared to the development environment. This should help reduce false-positives + // on this test. + int numLoops = 10000; + + long start = BaseTestCase.currentTimeMillis(); + + for (int j = 0; j < 2000; j++) { + // StringBuffer below is used for measuring and can't be changed to StringBuilder. + StringBuffer buf = new StringBuffer(numLoops); + + for (int i = 0; i < numLoops; i++) { + buf.append('a'); + } + } + + long elapsedTime = BaseTestCase.currentTimeMillis() - start; + return elapsedTime / ORIGINAL_LOOP_TIME_MS; + } + + private synchronized void checkTime(String testType, double avgExecTimeMs) throws Exception { + + double adjustForVendor = 1.0D; + + if (isRunningOnJRockit()) { + adjustForVendor = 4.0D; + } + + Double baselineExecTimeMs = BASELINE_TIMES.get(testType); + + if (baselineExecTimeMs == null) { + throw new Exception("No baseline time recorded for test '" + testType + "'"); + } + + double acceptableTime = LEEWAY * baselineExecTimeMs.doubleValue() * scaleFactor * adjustForVendor; + + assertTrue("Average execution time of " + avgExecTimeMs + " ms. exceeded baseline * leeway of " + acceptableTime + " ms.", + (avgExecTimeMs <= acceptableTime)); + } + + public void testBug6359() throws Exception { + if (runLongTests()) { + int numRows = 550000; + int numSelects = 100000; + + createTable("testBug6359", + "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT, field2 INT, field3 INT, field4 INT, field5 INT, field6 INT, field7 INT, field8 INT, field9 INT, INDEX (field1))"); + + PreparedStatement pStmt = this.conn.prepareStatement( + "INSERT INTO testBug6359 (field1, field2, field3, field4, field5, field6, field7, field8, field9) VALUES (?, 1, 2, 3, 4, 5, 6, 7, 8)"); + + logDebug("Loading " + numRows + " rows..."); + + for (int i = 0; i < numRows; i++) { + pStmt.setInt(1, i); + pStmt.executeUpdate(); + + if ((i % 10000) == 0) { + logDebug(i + " rows loaded so far"); + } + } + + logDebug("Finished loading rows"); + + long begin = currentTimeMillis(); + + long beginSingleQuery = currentTimeMillis(); + + for (int i = 0; i < numSelects; i++) { + this.rs = this.stmt.executeQuery("SELECT pk_field FROM testBug6359 WHERE field1 BETWEEN 1 AND 5"); + } + + long endSingleQuery = currentTimeMillis(); + + double secondsSingleQuery = ((double) endSingleQuery - (double) beginSingleQuery) / 1000; + + logDebug("time to execute " + numSelects + " single queries: " + secondsSingleQuery + " seconds"); + + checkTime("single selects", secondsSingleQuery); + + PreparedStatement pStmt2 = this.conn.prepareStatement("SELECT field2, field3, field4, field5 FROM testBug6359 WHERE pk_field=?"); + + long beginFiveQueries = currentTimeMillis(); + + for (int i = 0; i < numSelects; i++) { + + for (int j = 0; j < 5; j++) { + pStmt2.setInt(1, j); + this.rs = pStmt2.executeQuery(); + } + } + + long endFiveQueries = currentTimeMillis(); + + double secondsFiveQueries = ((double) endFiveQueries - (double) beginFiveQueries) / 1000; + + logDebug("time to execute " + numSelects + " 5 standalone queries: " + secondsFiveQueries + " seconds"); + + checkTime("5 standalone queries", secondsFiveQueries); + + long end = currentTimeMillis(); + + double seconds = ((double) end - (double) begin) / 1000; + + logDebug("time to execute " + numSelects + " selects: " + seconds + " seconds"); + + checkTime("total time all queries", seconds); + } + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/NumbersRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/NumbersRegressionTest.java new file mode 100644 index 0000000..f41ca2e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/NumbersRegressionTest.java @@ -0,0 +1,242 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.sql.ResultSetMetaData; + +import testsuite.BaseTestCase; + +/** + * Tests various number-handling issues that have arisen in the JDBC driver at one time or another. + */ +public class NumbersRegressionTest extends BaseTestCase { + /** + * Constructor for NumbersRegressionTest. + * + * @param name + * the test name + */ + public NumbersRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases + * + * @param args + * command-line args + * + * @throws Exception + * if any errors occur + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(NumbersRegressionTest.class); + } + + /** + * Tests that BIGINT retrieval works correctly + * + * @throws Exception + * if any errors occur + */ + public void testBigInt() throws Exception { + createTable("bigIntRegression", "(val BIGINT NOT NULL)"); + this.stmt.executeUpdate("INSERT INTO bigIntRegression VALUES (6692730313872877584)"); + this.rs = this.stmt.executeQuery("SELECT val FROM bigIntRegression"); + + while (this.rs.next()) { + // check retrieval + long retrieveAsLong = this.rs.getLong(1); + assertTrue(retrieveAsLong == 6692730313872877584L); + } + + this.rs.close(); + this.stmt.executeUpdate("DROP TABLE IF EXISTS bigIntRegression"); + + String bigIntAsString = "6692730313872877584"; + + long parsedBigIntAsLong = Long.parseLong(bigIntAsString); + + // check JDK parsing + assertTrue(bigIntAsString.equals(String.valueOf(parsedBigIntAsLong))); + } + + /** + * Tests correct type assignment for MySQL FLOAT and REAL datatypes. + * + * @throws Exception + * if the test fails. + */ + public void testFloatsAndReals() throws Exception { + createTable("floatsAndReals", "(floatCol FLOAT, realCol REAL, doubleCol DOUBLE)"); + this.stmt.executeUpdate("INSERT INTO floatsAndReals VALUES (0, 0, 0)"); + + this.rs = this.stmt.executeQuery("SELECT floatCol, realCol, doubleCol FROM floatsAndReals"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + this.rs.next(); + + assertTrue(rsmd.getColumnClassName(1).equals("java.lang.Float")); + assertTrue(this.rs.getObject(1).getClass().getName().equals("java.lang.Float")); + + assertTrue(rsmd.getColumnClassName(2).equals("java.lang.Double")); + assertTrue(this.rs.getObject(2).getClass().getName().equals("java.lang.Double")); + + assertTrue(rsmd.getColumnClassName(3).equals("java.lang.Double")); + assertTrue(this.rs.getObject(3).getClass().getName().equals("java.lang.Double")); + + } + + /** + * Tests that ResultSetMetaData precision and scale methods work correctly + * for all numeric types. + * + * @throws Exception + * if any errors occur + */ + public void testPrecisionAndScale() throws Exception { + testPrecisionForType("TINYINT", 8, -1, false); + testPrecisionForType("TINYINT", 8, -1, true); + testPrecisionForType("SMALLINT", 8, -1, false); + testPrecisionForType("SMALLINT", 8, -1, true); + testPrecisionForType("MEDIUMINT", 8, -1, false); + testPrecisionForType("MEDIUMINT", 8, -1, true); + testPrecisionForType("INT", 8, -1, false); + testPrecisionForType("INT", 8, -1, true); + testPrecisionForType("BIGINT", 8, -1, false); + testPrecisionForType("BIGINT", 8, -1, true); + + testPrecisionForType("FLOAT", 8, 4, false); + testPrecisionForType("FLOAT", 8, 4, true); + testPrecisionForType("DOUBLE", 8, 4, false); + testPrecisionForType("DOUBLE", 8, 4, true); + + testPrecisionForType("DECIMAL", 8, 4, false); + testPrecisionForType("DECIMAL", 8, 4, true); + + testPrecisionForType("DECIMAL", 9, 0, false); + testPrecisionForType("DECIMAL", 9, 0, true); + } + + private void testPrecisionForType(String typeName, int m, int d, boolean unsigned) throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS precisionAndScaleRegression"); + + StringBuilder createStatement = new StringBuilder("CREATE TABLE precisionAndScaleRegression ( val "); + createStatement.append(typeName); + createStatement.append("("); + createStatement.append(m); + + if (d != -1) { + createStatement.append(","); + createStatement.append(d); + } + + createStatement.append(")"); + + if (unsigned) { + createStatement.append(" UNSIGNED "); + } + + createStatement.append(" NOT NULL)"); + + this.stmt.executeUpdate(createStatement.toString()); + + this.rs = this.stmt.executeQuery("SELECT val FROM precisionAndScaleRegression"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertTrue("Precision returned incorrectly for type " + typeName + ", " + m + " != rsmd.getPrecision() = " + rsmd.getPrecision(1), + rsmd.getPrecision(1) == m); + + if (d != -1) { + assertTrue("Scale returned incorrectly for type " + typeName + ", " + d + " != rsmd.getScale() = " + rsmd.getScale(1), rsmd.getScale(1) == d); + } + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (Exception ex) { + // ignore + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS precisionAndScaleRegression"); + } + } + + public void testIntShouldReturnLong() throws Exception { + createTable("testIntRetLong", "(field1 INT)"); + this.stmt.executeUpdate("INSERT INTO testIntRetLong VALUES (1)"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testIntRetLong"); + this.rs.next(); + + assertTrue(this.rs.getObject(1).getClass().equals(java.lang.Integer.class)); + } + + /** + * Tests fix for BUG#5729, UNSIGNED BIGINT returned incorrectly + * + * @throws Exception + * if the test fails + */ + public void testBug5729() throws Exception { + if (versionMeetsMinimum(4, 1)) { + String valueAsString = "1095923280000"; + + createTable("testBug5729", "(field1 BIGINT UNSIGNED)"); + this.stmt.executeUpdate("INSERT INTO testBug5729 VALUES (" + valueAsString + ")"); + + this.rs = this.conn.prepareStatement("SELECT * FROM testBug5729").executeQuery(); + this.rs.next(); + + assertTrue(this.rs.getObject(1).toString().equals(valueAsString)); + } + } + + /** + * Tests fix for BUG#8484 - ResultSet.getBigDecimal() throws exception when + * rounding would need to occur to set scale. + * + * @throws Exception + * if the test fails + * @deprecated + */ + @Deprecated + public void testBug8484() throws Exception { + createTable("testBug8484", "(field1 DECIMAL(16, 8), field2 varchar(32))"); + this.stmt.executeUpdate("INSERT INTO testBug8484 VALUES (12345678.12345678, '')"); + this.rs = this.stmt.executeQuery("SELECT field1, field2 FROM testBug8484"); + this.rs.next(); + assertEquals("12345678.123", this.rs.getBigDecimal(1, 3).toString()); + assertEquals("0.000", this.rs.getBigDecimal(2, 3).toString()); + + this.pstmt = this.conn.prepareStatement("SELECT field1, field2 FROM testBug8484"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + assertEquals("12345678.123", this.rs.getBigDecimal(1, 3).toString()); + assertEquals("0.000", this.rs.getBigDecimal(2, 3).toString()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/PooledConnectionRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/PooledConnectionRegressionTest.java new file mode 100644 index 0000000..d7c832f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/PooledConnectionRegressionTest.java @@ -0,0 +1,397 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.sql.ConnectionEvent; +import javax.sql.ConnectionEventListener; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +import com.mysql.jdbc.PacketTooBigException; +import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper; +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; + +import testsuite.BaseTestCase; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Tests a PooledConnection implementation provided by a JDBC driver. Test case provided by Johnny Macchione from bug database record BUG#884. According to + * the JDBC 2.0 specification: + * + *

    + * "Each call to PooledConnection.getConnection() must return a newly constructed Connection object that exhibits the default Connection behavior. Only the most + * recent Connection object produced from a particular PooledConnection is open. An existing Connection object is automatically closed, if the getConnection() + * method of its associated Pooled-Connection is called again, before it has been explicitly closed by the application. This gives the application server a way + * to �take away� a Connection from the application if it wishes, and give it out to someone else. This capability will not likely be used frequently in + * practice." + *

    + * + *

    + * "When the application calls Connection.close(), an event is triggered that tells the connection pool it can recycle the physical database connection. In + * other words, the event signals the connection pool that the PooledConnection object which originally produced the Connection object generating the event can + * be put back in the connection pool." + *

    + * + *

    + * "A Connection-EventListener will also be notified when a fatal error occurs, so that it can make a note not to put a bad PooledConnection object back in the + * cache when the application finishes using it. When an error occurs, the ConnectionEventListener is notified by the JDBC driver, just before the driver throws + * an SQLException to the application to notify it of the same error. Note that automatic closing of a Connection object as discussed in the previous section + * does not generate a connection close event." + *

    + * The JDBC 3.0 specification states the same in other words: + * + *

    + * "The Connection.close method closes the logical handle, but the physical connection is maintained. The connection pool manager is notified that the + * underlying PooledConnection object is now available for reuse. If the application attempts to reuse the logical handle, the Connection implementation throws + * an SQLException." + *

    + * + *

    + * "For a given PooledConnection object, only the most recently produced logical Connection object will be valid. Any previously existing Connection object is + * automatically closed when the associated PooledConnection.getConnection method is called. Listeners (connection pool managers) are not notified in this case. + * This gives the application server a way to take a connection away from a client. This is an unlikely scenario but may be useful if the application server is + * trying to force an orderly shutdown." + *

    + * + *

    + * "A connection pool manager shuts down a physical connection by calling the method PooledConnection.close. This method is typically called only in certain + * circumstances: when the application server is undergoing an orderly shutdown, when the connection cache is being reinitialized, or when the application + * server receives an event indicating that an unrecoverable error has occurred on the connection." + *

    + * Even though the specification isn't clear about it, I think it is no use + * generating a close event when calling the method PooledConnection.close(), + * even if a logical Connection is open for this PooledConnection, bc the + * PooledConnection will obviously not be returned to the pool. + */ +public final class PooledConnectionRegressionTest extends BaseTestCase { + private ConnectionPoolDataSource cpds; + + // Count nb of closeEvent. + protected int closeEventCount; + + // Count nb of connectionErrorEvent + protected int connectionErrorEventCount; + + /** + * Creates a new instance of ProgressPooledConnectionTest + * + * @param testname + */ + public PooledConnectionRegressionTest(String testname) { + super(testname); + } + + /** + * Set up test case before a test is run. + * + * @throws Exception + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + // Reset event count. + this.closeEventCount = 0; + this.connectionErrorEventCount = 0; + + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + + ds.setURL(BaseTestCase.dbUrl); + + this.cpds = ds; + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(PooledConnectionRegressionTest.class); + } + + /** + * @return a test suite composed of this test case. + */ + public static Test suite() { + TestSuite suite = new TestSuite(PooledConnectionRegressionTest.class); + + return suite; + } + + /** + * After the test is run. + */ + @Override + public void tearDown() throws Exception { + this.cpds = null; + super.tearDown(); + } + + /** + * Tests fix for BUG#7136 ... Statement.getConnection() returning physical + * connection instead of logical connection. + */ + public void testBug7136() { + final ConnectionEventListener conListener = new ConnectionListener(); + PooledConnection pc = null; + this.closeEventCount = 0; + + try { + pc = this.cpds.getPooledConnection(); + + pc.addConnectionEventListener(conListener); + + Connection _conn = pc.getConnection(); + + Connection connFromStatement = _conn.createStatement().getConnection(); + + // This should generate a close event. + + connFromStatement.close(); + + assertEquals("One close event should've been registered", 1, this.closeEventCount); + + this.closeEventCount = 0; + + _conn = pc.getConnection(); + + Connection connFromPreparedStatement = _conn.prepareStatement("SELECT 1").getConnection(); + + // This should generate a close event. + + connFromPreparedStatement.close(); + + assertEquals("One close event should've been registered", 1, this.closeEventCount); + + } catch (SQLException ex) { + fail(ex.toString()); + } finally { + if (pc != null) { + try { + pc.close(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + } + } + + /** + * Test the nb of closeEvents generated when a Connection is reclaimed. No + * event should be generated in that case. + */ + public void testConnectionReclaim() { + final ConnectionEventListener conListener = new ConnectionListener(); + PooledConnection pc = null; + final int NB_TESTS = 5; + + try { + pc = this.cpds.getPooledConnection(); + + pc.addConnectionEventListener(conListener); + + for (int i = 0; i < NB_TESTS; i++) { + Connection _conn = pc.getConnection(); + + try { + // Try to reclaim connection. + System.out.println("Before connection reclaim."); + + _conn = pc.getConnection(); + + System.out.println("After connection reclaim."); + } finally { + if (_conn != null) { + System.out.println("Before connection.close()."); + + // This should generate a close event. + _conn.close(); + + System.out.println("After connection.close()."); + } + } + } + } catch (SQLException ex) { + ex.printStackTrace(); + fail(ex.toString()); + } finally { + if (pc != null) { + try { + System.out.println("Before pooledConnection.close()."); + + // This should not generate a close event. + pc.close(); + + System.out.println("After pooledConnection.close()."); + } catch (SQLException ex) { + ex.printStackTrace(); + fail(ex.toString()); + } + } + } + + assertEquals("Wrong nb of CloseEvents: ", NB_TESTS, this.closeEventCount); + } + + /** + * Tests that PacketTooLargeException doesn't clober the connection. + * + * @throws Exception + * if the test fails. + */ + public void testPacketTooLargeException() throws Exception { + final ConnectionEventListener conListener = new ConnectionListener(); + PooledConnection pc = null; + + pc = this.cpds.getPooledConnection(); + + pc.addConnectionEventListener(conListener); + + createTable("testPacketTooLarge", "(field1 LONGBLOB)"); + + Connection connFromPool = pc.getConnection(); + PreparedStatement pstmtFromPool = ((ConnectionWrapper) connFromPool).clientPrepare("INSERT INTO testPacketTooLarge VALUES (?)"); + + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'"); + this.rs.next(); + + int maxAllowedPacket = this.rs.getInt(2); + + int numChars = (int) (maxAllowedPacket * 1.2); + + pstmtFromPool.setBinaryStream(1, new BufferedInputStream(new FileInputStream(newTempBinaryFile("testPacketTooLargeException", numChars))), numChars); + + try { + pstmtFromPool.executeUpdate(); + fail("Expecting PacketTooLargeException"); + } catch (PacketTooBigException ptbe) { + // We're expecting this one... + } + + // This should still work okay, even though the last query on the same connection didn't... + this.rs = connFromPool.createStatement().executeQuery("SELECT 1"); + + assertTrue(this.connectionErrorEventCount == 0); + assertTrue(this.closeEventCount == 0); + } + + /** + * Test the nb of closeEvents generated by a PooledConnection. A + * JDBC-compliant driver should only generate 1 closeEvent each time + * connection.close() is called. + */ + public void testCloseEvent() { + final ConnectionEventListener conListener = new ConnectionListener(); + PooledConnection pc = null; + final int NB_TESTS = 5; + + try { + pc = this.cpds.getPooledConnection(); + + pc.addConnectionEventListener(conListener); + + for (int i = 0; i < NB_TESTS; i++) { + Connection pConn = pc.getConnection(); + + System.out.println("Before connection.close()."); + + // This should generate a close event. + pConn.close(); + + System.out.println("After connection.close()."); + } + } catch (SQLException ex) { + fail(ex.toString()); + } finally { + if (pc != null) { + try { + System.out.println("Before pooledConnection.close()."); + + // This should not generate a close event. + pc.close(); + + System.out.println("After pooledConnection.close()."); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + } + assertEquals("Wrong nb of CloseEvents: ", NB_TESTS, this.closeEventCount); + } + + /** + * Listener for PooledConnection events. + */ + protected final class ConnectionListener implements ConnectionEventListener { + /** */ + public void connectionClosed(ConnectionEvent event) { + PooledConnectionRegressionTest.this.closeEventCount++; + System.out.println(PooledConnectionRegressionTest.this.closeEventCount + " - Connection closed."); + } + + /** */ + public void connectionErrorOccurred(ConnectionEvent event) { + PooledConnectionRegressionTest.this.connectionErrorEventCount++; + System.out.println("Connection error: " + event.getSQLException()); + } + } + + /** + * Tests fix for BUG#35489 - Prepared statements from pooled connections + * cause NPE when closed() under JDBC4 + * + * @throws Exception + * if the test fails + */ + public void testBug35489() throws Exception { + MysqlConnectionPoolDataSource pds = new MysqlConnectionPoolDataSource(); + pds.setUrl(dbUrl); + this.pstmt = pds.getPooledConnection().getConnection().prepareStatement("SELECT 1"); + this.pstmt.execute(); + this.pstmt.close(); + + MysqlXADataSource xads = new MysqlXADataSource(); + xads.setUrl(dbUrl); + this.pstmt = xads.getXAConnection().getConnection().prepareStatement("SELECT 1"); + this.pstmt.execute(); + this.pstmt.close(); + + xads = new MysqlXADataSource(); + xads.setUrl(dbUrl); + xads.setPinGlobalTxToPhysicalConnection(true); + this.pstmt = xads.getXAConnection().getConnection().prepareStatement("SELECT 1"); + this.pstmt.execute(); + this.pstmt.close(); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/ResultSetRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/ResultSetRegressionTest.java new file mode 100644 index 0000000..c894885 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/ResultSetRegressionTest.java @@ -0,0 +1,5275 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.sql.rowset.CachedRowSet; + +import com.mysql.jdbc.CommunicationsException; +import com.mysql.jdbc.ConnectionImpl.ExceptionInterceptorChain; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.Extension; +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.MysqlDataTruncation; +import com.mysql.jdbc.NotUpdatable; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.log.StandardLogger; + +import testsuite.BaseTestCase; + +/** + * Regression test cases for the ResultSet class. + */ +public class ResultSetRegressionTest extends BaseTestCase { + /** + * Creates a new ResultSetRegressionTest + * + * @param name + * the name of the test to run + */ + public ResultSetRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ResultSetRegressionTest.class); + } + + /** + * Tests fix for BUG#???? -- Numeric types and server-side prepared + * statements incorrectly detect nulls. + * + * @throws Exception + * if the test fails + */ + public void testBug2359() throws Exception { + /* + * this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2359"); + * this.stmt.executeUpdate("CREATE TABLE testBug2359 (field1 INT) + * TYPE=InnoDB"); this.stmt.executeUpdate("INSERT INTO testBug2359 + * VALUES (null), (1)"); + * + * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM + * testBug2359 WHERE field1 IS NULL"); this.rs = + * this.pstmt.executeQuery(); + * + * assertTrue(this.rs.next()); + * + * assertTrue(this.rs.getByte(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getShort(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getInt(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getLong(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getFloat(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getDouble(1) == 0); assertTrue(this.rs.wasNull()); + * + * assertTrue(this.rs.getBigDecimal(1) == null); + * assertTrue(this.rs.wasNull()); + * + * this.rs.close(); + * + * this.pstmt = this.conn.prepareStatement("SELECT max(field1) FROM + * testBug2359 WHERE field1 IS NOT NULL"); this.rs = + * this.pstmt.executeQuery(); assertTrue(this.rs.next()); + * + * assertTrue(this.rs.getByte(1) == 1); assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getShort(1) == 1); assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getInt(1) == 1); assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getLong(1) == 1); assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getFloat(1) == 1); assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getDouble(1) == 1); + * assertTrue(!this.rs.wasNull()); + * + * assertTrue(this.rs.getBigDecimal(1) != null); + * assertTrue(!this.rs.wasNull()); + */ + createTable("testBug2359_1", "(id INT)", "InnoDB"); + this.stmt.executeUpdate("INSERT INTO testBug2359_1 VALUES (1)"); + + this.pstmt = this.conn.prepareStatement("SELECT max(id) FROM testBug2359_1"); + this.rs = this.pstmt.executeQuery(); + + if (this.rs.next()) { + assertTrue(this.rs.getInt(1) != 0); + this.rs.close(); + } + + this.rs.close(); + } + + /** + * Tests fix for BUG#2643, ClassCastException when using this.rs.absolute() + * and server-side prepared statements. + * + * @throws Exception + */ + public void testBug2623() throws Exception { + PreparedStatement pStmt = null; + + try { + pStmt = this.conn.prepareStatement("SELECT NOW()", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); + + this.rs = pStmt.executeQuery(); + + this.rs.absolute(1); + } finally { + if (this.rs != null) { + this.rs.close(); + } + + this.rs = null; + + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests fix for BUG#2654, "Column 'column.table' not found" when "order by" + * in query" + * + * @throws Exception + * if the test fails + */ + public void testBug2654() throws Exception { + if (!this.DISABLED_testBug2654) { // this is currently a server-level bug + + createTable("foo", "(id tinyint(3) default NULL, data varchar(255) default NULL) DEFAULT CHARSET=latin1", "MyISAM "); + this.stmt.executeUpdate("INSERT INTO foo VALUES (1,'male'),(2,'female')"); + + createTable("bar", "(id tinyint(3) unsigned default NULL, data char(3) default '0') DEFAULT CHARSET=latin1", "MyISAM "); + + this.stmt.executeUpdate("INSERT INTO bar VALUES (1,'yes'),(2,'no')"); + + String statement = "select foo.id, foo.data, bar.data from foo, bar where foo.id = bar.id order by foo.id"; + + String column = "foo.data"; + + this.rs = this.stmt.executeQuery(statement); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + System.out.println(rsmd.getTableName(1)); + System.out.println(rsmd.getColumnName(1)); + + this.rs.next(); + + String fooData = this.rs.getString(column); + assertNotNull(fooData); + + } + } + + /** + * Tests for fix to BUG#1130 + * + * @throws Exception + * if the test fails + */ + public void testClobTruncate() throws Exception { + createTable("testClobTruncate", "(field1 TEXT)"); + this.stmt.executeUpdate("INSERT INTO testClobTruncate VALUES ('abcdefg')"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testClobTruncate"); + this.rs.next(); + + Clob clob = this.rs.getClob(1); + clob.truncate(3); + + Reader reader = clob.getCharacterStream(); + char[] buf = new char[8]; + int charsRead = reader.read(buf); + + String clobAsString = new String(buf, 0, charsRead); + + assertTrue(clobAsString.equals("abc")); + } + + /** + * Tests that streaming result sets are registered correctly. + * + * @throws Exception + * if any errors occur + */ + public void testClobberStreamingRS() throws Exception { + try { + Properties props = new Properties(); + props.setProperty("clobberStreamingResults", "true"); + + Connection clobberConn = getConnectionWithProps(props); + + Statement clobberStmt = clobberConn.createStatement(); + + clobberStmt.executeUpdate("DROP TABLE IF EXISTS StreamingClobber"); + clobberStmt.executeUpdate("CREATE TABLE StreamingClobber ( DUMMYID INTEGER NOT NULL, DUMMYNAME VARCHAR(32),PRIMARY KEY (DUMMYID) )"); + clobberStmt.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (0, NULL)"); + clobberStmt.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (1, 'nro 1')"); + clobberStmt.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (2, 'nro 2')"); + clobberStmt.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (3, 'nro 3')"); + + Statement streamStmt = null; + + try { + streamStmt = clobberConn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); + streamStmt.setFetchSize(Integer.MIN_VALUE); + + this.rs = streamStmt.executeQuery("SELECT DUMMYID, DUMMYNAME FROM StreamingClobber ORDER BY DUMMYID"); + + this.rs.next(); + + // This should proceed normally, after the driver clears the input stream + ResultSet rs2 = clobberStmt.executeQuery("SHOW VARIABLES"); + rs2.next(); + this.rs.close(); + } finally { + if (streamStmt != null) { + streamStmt.close(); + } + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS StreamingClobber"); + } + } + + public void testEmptyResultSetGet() throws Exception { + try { + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'foo'"); + System.out.println(this.rs.getInt(1)); + } catch (SQLException sqlEx) { + assertTrue("Correct exception not thrown", SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + } + + /** + * Checks fix for BUG#1592 -- cross-database updatable result sets are not + * checked for updatability correctly. + * + * @throws Exception + * if the test fails. + */ + public void testFixForBug1592() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Statement updatableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + try { + updatableStmt.execute("SELECT * FROM mysql.user"); + + this.rs = updatableStmt.getResultSet(); + } catch (SQLException sqlEx) { + String message = sqlEx.getMessage(); + + if ((message != null) && (message.indexOf("denied") != -1)) { + System.err.println("WARN: Can't complete testFixForBug1592(), access to 'mysql' database not allowed"); + } else { + throw sqlEx; + } + } + } + } + + /** + * Tests fix for BUG#2006, where 2 columns with same name in a result set + * are returned via findColumn() in the wrong order...The JDBC spec states, + * that the _first_ matching column should be returned. + * + * @throws Exception + * if the test fails + */ + public void testFixForBug2006() throws Exception { + + createTable("testFixForBug2006_1", "(key_field INT NOT NULL)"); + createTable("testFixForBug2006_2", "(key_field INT NULL)"); + this.stmt.executeUpdate("INSERT INTO testFixForBug2006_1 VALUES (1)"); + + this.rs = this.stmt.executeQuery( + "SELECT testFixForBug2006_1.key_field, testFixForBug2006_2.key_field FROM testFixForBug2006_1 LEFT JOIN testFixForBug2006_2 USING(key_field)"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertTrue(rsmd.getColumnName(1).equals(rsmd.getColumnName(2))); + assertTrue(rsmd.isNullable(this.rs.findColumn("key_field")) == ResultSetMetaData.columnNoNulls); + assertTrue(rsmd.isNullable(2) == ResultSetMetaData.columnNullable); + assertTrue(this.rs.next()); + assertTrue(this.rs.getObject(1) != null); + assertTrue(this.rs.getObject(2) == null); + + } + + /** + * Tests that ResultSet.getLong() does not truncate values. + * + * @throws Exception + * if any errors occur + */ + public void testGetLongBug() throws Exception { + createTable("getLongBug", "(int_col int, bigint_col bigint)"); + + int intVal = 123456; + long longVal1 = 123456789012345678L; + long longVal2 = -2079305757640172711L; + this.stmt.executeUpdate("INSERT INTO getLongBug (int_col, bigint_col) VALUES (" + intVal + ", " + longVal1 + "), (" + intVal + ", " + longVal2 + ")"); + + this.rs = this.stmt.executeQuery("SELECT int_col, bigint_col FROM getLongBug ORDER BY bigint_col DESC"); + this.rs.next(); + assertTrue("Values not decoded correctly", ((this.rs.getInt(1) == intVal) && (this.rs.getLong(2) == longVal1))); + this.rs.next(); + assertTrue("Values not decoded correctly", ((this.rs.getInt(1) == intVal) && (this.rs.getLong(2) == longVal2))); + + } + + public void testGetTimestampWithDate() throws Exception { + createTable("testGetTimestamp", "(d date)"); + this.stmt.executeUpdate("INSERT INTO testGetTimestamp values (now())"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testGetTimestamp"); + this.rs.next(); + System.out.println(this.rs.getTimestamp(1)); + } + + /** + * Tests a bug where ResultSet.isBefireFirst() would return true when the + * result set was empty (which is incorrect) + * + * @throws Exception + * if an error occurs. + */ + public void testIsBeforeFirstOnEmpty() throws Exception { + // Query with valid rows: isBeforeFirst() correctly returns True + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'version'"); + assertTrue("Non-empty search should return true", this.rs.isBeforeFirst()); + + // Query with empty result: isBeforeFirst() falsely returns True. Sun's documentation says it should return false + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'garbage'"); + assertTrue("Empty search should return false ", !this.rs.isBeforeFirst()); + } + + /** + * Tests a bug where ResultSet.isBefireFirst() would return true when the + * result set was empty (which is incorrect) + * + * @throws Exception + * if an error occurs. + */ + public void testMetaDataIsWritable() throws Exception { + // Query with valid rows + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'version'"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + int numColumns = rsmd.getColumnCount(); + + for (int i = 1; i <= numColumns; i++) { + assertTrue("rsmd.isWritable() should != rsmd.isReadOnly()", rsmd.isWritable(i) != rsmd.isReadOnly(i)); + } + } + + /** + * Tests fix for bug # 496 + * + * @throws Exception + * if an error happens. + */ + public void testNextAndPrevious() throws Exception { + createTable("testNextAndPrevious", "(field1 int)"); + this.stmt.executeUpdate("INSERT INTO testNextAndPrevious VALUES (1)"); + + this.rs = this.stmt.executeQuery("SELECT * from testNextAndPrevious"); + + System.out.println("Currently at row " + this.rs.getRow()); + this.rs.next(); + System.out.println("Value at row " + this.rs.getRow() + " is " + this.rs.getString(1)); + + this.rs.previous(); + + try { + System.out.println("Value at row " + this.rs.getRow() + " is " + this.rs.getString(1)); + fail("Should not be able to retrieve values with invalid cursor"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().startsWith("Before start")); + } + + this.rs.next(); + + this.rs.next(); + + try { + System.out.println("Value at row " + this.rs.getRow() + " is " + this.rs.getString(1)); + fail("Should not be able to retrieve values with invalid cursor"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().startsWith("After end")); + } + } + + /** + * Tests fix for BUG#1630 (not updatable exception turning into NPE on + * second updateFoo() method call. + * + * @throws Exception + * if an unexpected exception is thrown. + */ + public void testNotUpdatable() throws Exception { + this.rs = null; + + String sQuery = "SHOW VARIABLES"; + this.pstmt = this.conn.prepareStatement(sQuery, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.rs = this.pstmt.executeQuery(); + + if (this.rs.next()) { + this.rs.absolute(1); + + try { + this.rs.updateInt(1, 1); + } catch (SQLException sqlEx) { + assertTrue(sqlEx instanceof NotUpdatable); + } + + try { + this.rs.updateString(1, "1"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx instanceof NotUpdatable); + } + } + } + + /** + * Tests that streaming result sets are registered correctly. + * + * @throws Exception + * if any errors occur + */ + public void testStreamingRegBug() throws Exception { + createTable("StreamingRegBug", "( DUMMYID INTEGER NOT NULL, DUMMYNAME VARCHAR(32),PRIMARY KEY (DUMMYID) )"); + this.stmt.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (0, NULL)"); + this.stmt.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (1, 'nro 1')"); + this.stmt.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (2, 'nro 2')"); + this.stmt.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (3, 'nro 3')"); + + PreparedStatement streamStmt = null; + + try { + streamStmt = this.conn.prepareStatement("SELECT DUMMYID, DUMMYNAME FROM StreamingRegBug ORDER BY DUMMYID", java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY); + streamStmt.setFetchSize(Integer.MIN_VALUE); + + this.rs = streamStmt.executeQuery(); + + while (this.rs.next()) { + this.rs.getString(1); + } + + this.rs.close(); // error occurs here + } catch (SQLException sqlEx) { + + } finally { + if (streamStmt != null) { + try { + streamStmt.close(); + } catch (SQLException exWhileClose) { + exWhileClose.printStackTrace(); + } + } + } + } + + /** + * Tests that result sets can be updated when all parameters are correctly + * set. + * + * @throws Exception + * if any errors occur + */ + public void testUpdatability() throws Exception { + this.rs = null; + + createTable("updatabilityBug", + "(id int(10) unsigned NOT NULL auto_increment, field1 varchar(32) NOT NULL default ''," + + " field2 varchar(128) NOT NULL default '', field3 varchar(128) default NULL, field4 varchar(128) default NULL," + + " field5 varchar(64) default NULL, field6 int(10) unsigned default NULL, field7 varchar(64) default NULL, PRIMARY KEY (id)) ", + "InnoDB"); + this.stmt.executeUpdate("insert into updatabilityBug (id) values (1)"); + + String sQuery = " SELECT * FROM updatabilityBug WHERE id = ? "; + this.pstmt = this.conn.prepareStatement(sQuery, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.conn.setAutoCommit(false); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + + if (this.rs.next()) { + this.rs.absolute(1); + this.rs.updateInt("id", 1); + this.rs.updateString("field1", "1"); + this.rs.updateString("field2", "1"); + this.rs.updateString("field3", "1"); + this.rs.updateString("field4", "1"); + this.rs.updateString("field5", "1"); + this.rs.updateInt("field6", 1); + this.rs.updateString("field7", "1"); + this.rs.updateRow(); + } + + this.conn.commit(); + this.conn.setAutoCommit(true); + } + + /** + * Test fixes for BUG#1071 + * + * @throws Exception + * if the test fails. + */ + public void testUpdatabilityAndEscaping() throws Exception { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "big5"); + + Connection updConn = getConnectionWithProps(props); + Statement updStmt = updConn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + + try { + updStmt.executeUpdate("DROP TABLE IF EXISTS testUpdatesWithEscaping"); + updStmt.executeUpdate("CREATE TABLE testUpdatesWithEscaping (field1 INT PRIMARY KEY, field2 VARCHAR(64))"); + updStmt.executeUpdate("INSERT INTO testUpdatesWithEscaping VALUES (1, null)"); + + String stringToUpdate = "\" \\ '"; + + this.rs = updStmt.executeQuery("SELECT * from testUpdatesWithEscaping"); + + this.rs.next(); + this.rs.updateString(2, stringToUpdate); + this.rs.updateRow(); + + assertTrue(stringToUpdate.equals(this.rs.getString(2))); + } finally { + updStmt.executeUpdate("DROP TABLE IF EXISTS testUpdatesWithEscaping"); + updStmt.close(); + updConn.close(); + } + } + + /** + * Tests the fix for BUG#661 ... refreshRow() fails when primary key values + * have escaped data in them. + * + * @throws Exception + * if an error occurs + */ + public void testUpdatabilityWithQuotes() throws Exception { + Statement updStmt = null; + + try { + createTable("testUpdWithQuotes", "(keyField CHAR(32) PRIMARY KEY NOT NULL, field2 int)"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testUpdWithQuotes VALUES (?, ?)"); + pStmt.setString(1, "Abe's"); + pStmt.setInt(2, 1); + pStmt.executeUpdate(); + + updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.rs = updStmt.executeQuery("SELECT * FROM testUpdWithQuotes"); + this.rs.next(); + this.rs.updateInt(2, 2); + this.rs.updateRow(); + } finally { + if (updStmt != null) { + updStmt.close(); + } + + updStmt = null; + } + } + + /** + * Checks whether or not ResultSet.updateClob() is implemented + * + * @throws Exception + * if the test fails + */ + public void testUpdateClob() throws Exception { + Statement updatableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + createTable("testUpdateClob", "(intField INT NOT NULL PRIMARY KEY, clobField TEXT)"); + this.stmt.executeUpdate("INSERT INTO testUpdateClob VALUES (1, 'foo')"); + + this.rs = updatableStmt.executeQuery("SELECT intField, clobField FROM testUpdateClob"); + this.rs.next(); + + Clob clob = this.rs.getClob(2); + + clob.setString(1, "bar"); + + this.rs.updateClob(2, clob); + this.rs.updateRow(); + + this.rs.moveToInsertRow(); + + clob.setString(1, "baz"); + this.rs.updateInt(1, 2); + this.rs.updateClob(2, clob); + this.rs.insertRow(); + + clob.setString(1, "bat"); + this.rs.updateInt(1, 3); + this.rs.updateClob(2, clob); + this.rs.insertRow(); + + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT intField, clobField FROM testUpdateClob ORDER BY intField"); + + this.rs.next(); + assertTrue((this.rs.getInt(1) == 1) && this.rs.getString(2).equals("bar")); + + this.rs.next(); + assertTrue((this.rs.getInt(1) == 2) && this.rs.getString(2).equals("baz")); + + this.rs.next(); + assertTrue((this.rs.getInt(1) == 3) && this.rs.getString(2).equals("bat")); + } + + /** + * Tests fix for BUG#4482, ResultSet.getObject() returns wrong type for + * strings when using prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug4482() throws Exception { + this.rs = this.conn.prepareStatement("SELECT 'abcdef'").executeQuery(); + assertTrue(this.rs.next()); + assertTrue(this.rs.getObject(1) instanceof String); + } + + /** + * Test fix for BUG#4689 - WasNull not getting set correctly for binary + * result sets. + */ + public void testBug4689() throws Exception { + createTable("testBug4689", "(tinyintField tinyint, tinyintFieldNull tinyint, intField int, intFieldNull int, " + + "bigintField bigint, bigintFieldNull bigint, shortField smallint, shortFieldNull smallint, doubleField double, doubleFieldNull double)"); + + this.stmt.executeUpdate("INSERT INTO testBug4689 VALUES (1, null, 1, null, 1, null, 1, null, 1, null)"); + + PreparedStatement pStmt = this.conn.prepareStatement("SELECT tinyintField, tinyintFieldNull, intField, intFieldNull, " + + "bigintField, bigintFieldNull, shortField, shortFieldNull, doubleField, doubleFieldNull FROM testBug4689"); + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + + assertTrue(this.rs.getByte(1) == 1); + assertTrue(this.rs.wasNull() == false); + assertTrue(this.rs.getByte(2) == 0); + assertTrue(this.rs.wasNull() == true); + + assertTrue(this.rs.getInt(3) == 1); + assertTrue(this.rs.wasNull() == false); + assertTrue(this.rs.getInt(4) == 0); + assertTrue(this.rs.wasNull() == true); + + assertTrue(this.rs.getInt(5) == 1); + assertTrue(this.rs.wasNull() == false); + assertTrue(this.rs.getInt(6) == 0); + assertTrue(this.rs.wasNull() == true); + + assertTrue(this.rs.getShort(7) == 1); + assertTrue(this.rs.wasNull() == false); + assertTrue(this.rs.getShort(8) == 0); + assertTrue(this.rs.wasNull() == true); + + assertTrue(this.rs.getDouble(9) == 1); + assertTrue(this.rs.wasNull() == false); + assertTrue(this.rs.getDouble(10) == 0); + assertTrue(this.rs.wasNull() == true); + } + + /** + * Tests fix for BUG#5032 -- ResultSet.getObject() doesn't return type + * Boolean for pseudo-bit types from prepared statements on 4.1.x. + * + * @throws Exception + * if the test fails. + */ + public void testBug5032() throws Exception { + if (versionMeetsMinimum(4, 1)) { + + createTable("testBug5032", "(field1 BIT)"); + this.stmt.executeUpdate("INSERT INTO testBug5032 VALUES (1)"); + + this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug5032"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertTrue(this.rs.getObject(1) instanceof Boolean); + + } + } + + /** + * Tests fix for BUG#5069 -- ResultSet.getMetaData() should not return + * incorrectly-initialized metadata if the result set has been closed, but + * should instead throw a SQLException. Also tests fix for getRow() and + * getWarnings() and traversal methods. + * + * @throws Exception + * if the test fails. + */ + public void testBug5069() throws Exception { + + this.rs = this.stmt.executeQuery("SELECT 1"); + this.rs.close(); + + try { + @SuppressWarnings("unused") + ResultSetMetaData md = this.rs.getMetaData(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getRow(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getWarnings(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.first(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.beforeFirst(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.last(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.afterLast(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.relative(0); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.next(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.previous(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.isBeforeFirst(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.isFirst(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.isAfterLast(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + + try { + this.rs.isLast(); + } catch (NullPointerException npEx) { + fail("Should not catch NullPointerException here"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx.getSQLState())); + } + } + + /** + * Tests for BUG#5136, GEOMETRY types getting corrupted, turns out to be a + * server bug. + * + * @throws Exception + * if the test fails. + */ + public void testBug5136() throws Exception { + if (!this.DISABLED_testBug5136) { + PreparedStatement toGeom = this.conn.prepareStatement("select GeomFromText(?)"); + PreparedStatement toText = this.conn.prepareStatement("select AsText(?)"); + + String inText = "POINT(146.67596278 -36.54368233)"; + + // First assert that the problem is not at the server end + this.rs = this.stmt.executeQuery("select AsText(GeomFromText('" + inText + "'))"); + this.rs.next(); + + String outText = this.rs.getString(1); + this.rs.close(); + assertTrue("Server side only\n In: " + inText + "\nOut: " + outText, inText.equals(outText)); + + // Now bring a binary geometry object to the client and send it back + toGeom.setString(1, inText); + this.rs = toGeom.executeQuery(); + this.rs.next(); + + // Return a binary geometry object from the WKT + Object geom = this.rs.getObject(1); + this.rs.close(); + toText.setObject(1, geom); + this.rs = toText.executeQuery(); + this.rs.next(); + + // Return WKT from the binary geometry + outText = this.rs.getString(1); + this.rs.close(); + assertTrue("Server to client and back\n In: " + inText + "\nOut: " + outText, inText.equals(outText)); + } + } + + /** + * Tests fix for BUG#5664, ResultSet.updateByte() when on insert row throws + * ArrayOutOfBoundsException. + * + * @throws Exception + * if the test fails. + */ + public void testBug5664() throws Exception { + createTable("testBug5664", "(pkfield int PRIMARY KEY NOT NULL, field1 SMALLINT)"); + this.stmt.executeUpdate("INSERT INTO testBug5664 VALUES (1, 1)"); + + Statement updatableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.rs = updatableStmt.executeQuery("SELECT pkfield, field1 FROM testBug5664"); + this.rs.next(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 2); + this.rs.updateByte(2, (byte) 2); + + } + + public void testBogusTimestampAsString() throws Exception { + + this.rs = this.stmt.executeQuery("SELECT '2004-08-13 13:21:17.'"); + + this.rs.next(); + + // We're only checking for an exception being thrown here as the bug + this.rs.getTimestamp(1); + + } + + /** + * Tests our ability to reject NaN and +/- INF in + * PreparedStatement.setDouble(); + */ + public void testBug5717() throws Exception { + createTable("testBug5717", "(field1 DOUBLE)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug5717 VALUES (?)"); + + try { + this.pstmt.setDouble(1, Double.NEGATIVE_INFINITY); + fail("Exception should've been thrown"); + } catch (Exception ex) { + // expected + } + + try { + this.pstmt.setDouble(1, Double.POSITIVE_INFINITY); + fail("Exception should've been thrown"); + } catch (Exception ex) { + // expected + } + + try { + this.pstmt.setDouble(1, Double.NaN); + fail("Exception should've been thrown"); + } catch (Exception ex) { + // expected + } + } + + /** + * Tests fix for server issue that drops precision on aggregate operations + * on DECIMAL types, because they come back as DOUBLEs. + * + * @throws Exception + * if the test fails. + */ + @SuppressWarnings("deprecation") + public void testBug6537() throws Exception { + if (versionMeetsMinimum(4, 1, 0)) { + String tableName = "testBug6537"; + + createTable(tableName, "(`id` int(11) NOT NULL default '0', `value` decimal(10,2) NOT NULL default '0.00', `stringval` varchar(10)," + + "PRIMARY KEY (`id`)) DEFAULT CHARSET=latin1", "MyISAM"); + this.stmt.executeUpdate("INSERT INTO " + tableName + "(id, value, stringval) VALUES (1, 100.00, '100.00'), (2, 200, '200')"); + + String sql = "SELECT SUM(value) as total FROM " + tableName + " WHERE id = ? "; + PreparedStatement pStmt = this.conn.prepareStatement(sql); + pStmt.setInt(1, 1); + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + + assertTrue("100.00".equals(this.rs.getBigDecimal("total").toString())); + + sql = "SELECT stringval as total FROM " + tableName + " WHERE id = ? "; + pStmt = this.conn.prepareStatement(sql); + pStmt.setInt(1, 2); + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + + assertTrue("200.00".equals(this.rs.getBigDecimal("total", 2).toString())); + + } + } + + /** + * Tests fix for BUG#6231, ResultSet.getTimestamp() on a column with TIME in + * it fails. + * + * @throws Exception + * if the test fails. + */ + public void testBug6231() throws Exception { + + createTable("testBug6231", "(field1 TIME)"); + this.stmt.executeUpdate("INSERT INTO testBug6231 VALUES ('09:16:00')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug6231"); + this.rs.next(); + long asMillis = this.rs.getTimestamp(1).getTime(); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(asMillis); + + assertEquals(9, cal.get(Calendar.HOUR)); + assertEquals(16, cal.get(Calendar.MINUTE)); + assertEquals(0, cal.get(Calendar.SECOND)); + + } + + public void testBug6619() throws Exception { + + createTable("testBug6619", "(field1 int)"); + this.stmt.executeUpdate("INSERT INTO testBug6619 VALUES (1), (2)"); + + PreparedStatement pStmt = this.conn.prepareStatement("SELECT SUM(field1) FROM testBug6619"); + + this.rs = pStmt.executeQuery(); + this.rs.next(); + System.out.println(this.rs.getString(1)); + + } + + public void testBug6743() throws Exception { + // 0x835C U+30BD # KATAKANA LETTER SO + String katakanaStr = "\u30BD"; + + Properties props = new Properties(); + + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "SJIS"); + + Connection sjisConn = null; + Statement sjisStmt = null; + + try { + sjisConn = getConnectionWithProps(props); + sjisStmt = sjisConn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + + sjisStmt.executeUpdate("DROP TABLE IF EXISTS testBug6743"); + StringBuilder queryBuf = new StringBuilder("CREATE TABLE testBug6743 (pkField INT NOT NULL PRIMARY KEY, field1 VARCHAR(32)"); + + if (versionMeetsMinimum(4, 1)) { + queryBuf.append(" CHARACTER SET SJIS"); + } + + queryBuf.append(")"); + sjisStmt.executeUpdate(queryBuf.toString()); + sjisStmt.executeUpdate("INSERT INTO testBug6743 VALUES (1, 'abc')"); + + this.rs = sjisStmt.executeQuery("SELECT pkField, field1 FROM testBug6743"); + this.rs.next(); + this.rs.updateString(2, katakanaStr); + this.rs.updateRow(); + + String retrString = this.rs.getString(2); + assertTrue(katakanaStr.equals(retrString)); + + this.rs = sjisStmt.executeQuery("SELECT pkField, field1 FROM testBug6743"); + this.rs.next(); + + retrString = this.rs.getString(2); + assertTrue(katakanaStr.equals(retrString)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6743"); + + if (sjisStmt != null) { + sjisStmt.close(); + } + + if (sjisConn != null) { + sjisConn.close(); + } + } + } + + /** + * Tests for presence of BUG#6561, NPE thrown when dealing with 0 dates and + * non-unpacked result sets. + * + * @throws Exception + * if the test occurs. + */ + public void testBug6561() throws Exception { + Connection testConn = this.conn; + Connection zeroConn = getConnectionWithProps("zeroDateTimeBehavior=convertToNull"); + try { + if (versionMeetsMinimum(5, 7, 4)) { + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "false"); + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + testConn = getConnectionWithProps(props); + this.stmt = testConn.createStatement(); + } + + createTable("testBug6561", "(ofield int, field1 DATE, field2 integer, field3 integer)"); + this.stmt.executeUpdate("INSERT INTO testBug6561 (ofield, field1,field2,field3) VALUES (1, 0,NULL,0)"); + this.stmt.executeUpdate("INSERT INTO testBug6561 (ofield, field1,field2,field3) VALUES (2, '2004-11-20',NULL,0)"); + + PreparedStatement ps = zeroConn.prepareStatement("SELECT field1,field2,field3 FROM testBug6561 ORDER BY ofield"); + this.rs = ps.executeQuery(); + + assertTrue(this.rs.next()); + assertNull(this.rs.getObject("field1")); + assertNull(this.rs.getObject("field2")); + assertEquals(0, this.rs.getInt("field3")); + + assertTrue(this.rs.next()); + assertEquals("2004-11-20", this.rs.getString("field1")); + assertNull(this.rs.getObject("field2")); + assertEquals(0, this.rs.getInt("field3")); + + ps.close(); + } finally { + zeroConn.close(); + if (testConn != this.conn) { + testConn.close(); + } + } + } + + public void testBug7686() throws SQLException { + String tableName = "testBug7686"; + createTable(tableName, "(id1 int(10) unsigned NOT NULL, id2 DATETIME, field1 varchar(128) NOT NULL default '', PRIMARY KEY (id1, id2))", "InnoDB;"); + + this.stmt.executeUpdate("insert into " + tableName + " (id1, id2, field1) values (1, '2005-01-05 13:59:20', 'foo')"); + + String sQuery = " SELECT * FROM " + tableName + " WHERE id1 = ? AND id2 = ?"; + this.pstmt = this.conn.prepareStatement(sQuery, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.conn.setAutoCommit(false); + this.pstmt.setInt(1, 1); + GregorianCalendar cal = new GregorianCalendar(); + cal.clear(); + cal.set(2005, 00, 05, 13, 59, 20); + + Timestamp jan5before2pm = null; + jan5before2pm = new java.sql.Timestamp(cal.getTimeInMillis()); + + this.pstmt.setTimestamp(2, jan5before2pm); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + this.rs.absolute(1); + this.rs.updateString("field1", "bar"); + this.rs.updateRow(); + this.conn.commit(); + this.conn.setAutoCommit(true); + + } + + /** + * Tests fix for BUG#7715 - Timestamps converted incorrectly to strings with + * SSPS and Upd. Result Sets. + * + * @throws Exception + * if the test fails. + */ + public void testBug7715() throws Exception { + PreparedStatement pStmt = null; + + createTable("testConvertedBinaryTimestamp", "(field1 VARCHAR(32), field2 VARCHAR(32), field3 VARCHAR(32), field4 TIMESTAMP)"); + this.stmt.executeUpdate("INSERT INTO testConvertedBinaryTimestamp VALUES ('abc', 'def', 'ghi', NOW())"); + + pStmt = this.conn.prepareStatement("SELECT field1, field2, field3, field4 FROM testConvertedBinaryTimestamp", ResultSet.TYPE_SCROLL_SENSITIVE, + ResultSet.CONCUR_UPDATABLE); + + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + + this.rs.getObject(4); // fails if bug exists + } + + /** + * Tests fix for BUG#8428 - getString() doesn't maintain format stored on + * server. + * + * @throws Exception + * if the test fails. + */ + public void testBug8428() throws Exception { + Connection noSyncConn = null; + + createTable("testBug8428", "(field1 YEAR, field2 DATETIME)"); + this.stmt.executeUpdate("INSERT INTO testBug8428 VALUES ('1999', '2005-02-11 12:54:41')"); + + Properties props = new Properties(); + props.setProperty("noDatetimeStringSync", "true"); + props.setProperty("useUsageAdvisor", "true"); + props.setProperty("yearIsDateType", "false"); // for 3.1.9+ + + noSyncConn = getConnectionWithProps(props); + + this.rs = noSyncConn.createStatement().executeQuery("SELECT field1, field2 FROM testBug8428"); + this.rs.next(); + assertEquals("1999", this.rs.getString(1)); + assertEquals("2005-02-11 12:54:41", this.rs.getString(2)); + + this.rs = noSyncConn.prepareStatement("SELECT field1, field2 FROM testBug8428").executeQuery(); + this.rs.next(); + assertEquals("1999", this.rs.getString(1)); + assertEquals("2005-02-11 12:54:41", this.rs.getString(2)); + + } + + /** + * Tests fix for Bug#8868, DATE_FORMAT() queries returned as BLOBs from + * getObject(). + * + * @throws Exception + * if the test fails. + */ + public void testBug8868() throws Exception { + if (versionMeetsMinimum(4, 1)) { + createTable("testBug8868", "(field1 DATE, field2 VARCHAR(32) CHARACTER SET BINARY)"); + this.stmt.executeUpdate("INSERT INTO testBug8868 VALUES (NOW(), 'abcd')"); + this.rs = this.stmt.executeQuery("SELECT DATE_FORMAT(field1,'%b-%e %l:%i%p') as fmtddate, field2 FROM testBug8868"); + this.rs.next(); + assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName()); + } + } + + /** + * Tests fix for BUG#9098 - Server doesn't give us info to distinguish + * between CURRENT_TIMESTAMP and 'CURRENT_TIMESTAMP' for default values. + * + * @throws Exception + * if the test fails + */ + public void testBug9098() throws Exception { + if (versionMeetsMinimum(4, 1, 10)) { + Statement updatableStmt = null; + + createTable("testBug9098", "(pkfield INT PRIMARY KEY NOT NULL AUTO_INCREMENT, \n" + + "tsfield TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, tsfield2 TIMESTAMP NOT NULL DEFAULT '2005-12-25 12:20:52', charfield VARCHAR(4) NOT NULL DEFAULT 'abcd')"); + updatableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = updatableStmt.executeQuery("SELECT pkfield, tsfield, tsfield2, charfield FROM testBug9098"); + this.rs.moveToInsertRow(); + this.rs.insertRow(); + } + } + + /** + * Tests fix for BUG#9236, a continuation of BUG#8868, where functions used + * in queries that should return non-string types when resolved by temporary + * tables suddenly become opaque binary strings (work-around for server + * limitation) + * + * @throws Exception + * if the test fails. + */ + public void testBug9236() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Connection testConn = this.conn; + try { + if (versionMeetsMinimum(5, 7, 4)) { + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "false"); + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + testConn = getConnectionWithProps(props); + this.stmt = testConn.createStatement(); + } + + createTable("testBug9236", + "(field_1 int(18) NOT NULL auto_increment, field_2 varchar(50) NOT NULL default ''," + + "field_3 varchar(12) default NULL, field_4 int(18) default NULL, field_5 int(18) default NULL," + + "field_6 datetime default NULL, field_7 varchar(30) default NULL, field_8 varchar(50) default NULL," + + "field_9 datetime default NULL, field_10 int(18) NOT NULL default '0', field_11 int(18) default NULL," + + "field_12 datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (field_1), KEY (field_4), KEY (field_2)," + + "KEY (field_3), KEY (field_7,field_1), KEY (field_5), KEY (field_6,field_10,field_9), KEY (field_11,field_10)," + + "KEY (field_12,field_10)) DEFAULT CHARSET=latin1", + "InnoDB"); + + this.stmt.executeUpdate("INSERT INTO testBug9236 VALUES " + + "(1,'0',NULL,-1,0,'0000-00-00 00:00:00','123456789','-1','2004-03-13 14:21:38',0,NULL,'2004-03-13 14:21:38')," + + "(2,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'1',NULL,0,NULL,'2004-07-13 14:29:52')," + + "(3,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'2',NULL,0,NULL,'2004-07-16 13:20:51')," + + "(4,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'3','2004-07-16 13:43:39',0,NULL,'2004-07-16 13:22:01')," + + "(5,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'4','2004-07-16 13:23:48',0,NULL,'2004-07-16 13:23:01')," + + "(6,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'5',NULL,0,NULL,'2004-07-16 14:41:07')," + + "(7,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'6',NULL,0,NULL,'2004-07-16 14:41:34')," + + "(8,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'7',NULL,0,NULL,'2004-07-16 14:41:54')," + + "(9,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'8',NULL,0,NULL,'2004-07-16 14:42:42')," + + "(10,'0','PI',1,0,'0000-00-00 00:00:00',NULL,'9',NULL,0,NULL,'2004-07-18 10:51:30')," + + "(11,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'10','2004-07-23 17:23:06',0,NULL,'2004-07-23 17:18:19')," + + "(12,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'11','2004-07-23 17:24:45',0,NULL,'2004-07-23 17:23:57')," + + "(13,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'12','2004-07-23 17:30:51',0,NULL,'2004-07-23 17:30:15')," + + "(14,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'13','2004-07-26 17:50:19',0,NULL,'2004-07-26 17:49:38')," + + "(15,'0','FRL',1,0,'0000-00-00 00:00:00',NULL,'1',NULL,0,NULL,'2004-08-19 18:29:18')," + + "(16,'0','FRL',1,0,'0000-00-00 00:00:00',NULL,'15',NULL,0,NULL,'2005-03-16 12:08:28')"); + + createTable("testBug9236_1", "(field1 CHAR(2) CHARACTER SET BINARY)"); + this.stmt.executeUpdate("INSERT INTO testBug9236_1 VALUES ('ab')"); + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug9236_1"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals("[B", rsmd.getColumnClassName(1)); + assertTrue(this.rs.next()); + Object asObject = this.rs.getObject(1); + assertEquals("[B", asObject.getClass().getName()); + + this.rs = this.stmt.executeQuery( + "select DATE_FORMAT(field_12, '%Y-%m-%d') as date, count(*) as count from testBug9236 where field_10 = 0 and field_3 = 'FRL' and field_12 >= '2005-03-02 00:00:00' and field_12 <= '2005-03-17 00:00:00' group by date"); + rsmd = this.rs.getMetaData(); + assertEquals("java.lang.String", rsmd.getColumnClassName(1)); + this.rs.next(); + asObject = this.rs.getObject(1); + assertEquals("java.lang.String", asObject.getClass().getName()); + + this.rs.close(); + + createTable("testBug8868_2", "(field1 CHAR(4) CHARACTER SET BINARY)"); + this.stmt.executeUpdate("INSERT INTO testBug8868_2 VALUES ('abc')"); + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug8868_2"); + + rsmd = this.rs.getMetaData(); + assertEquals("[B", rsmd.getColumnClassName(1)); + this.rs.next(); + asObject = this.rs.getObject(1); + assertEquals("[B", asObject.getClass().getName()); + } finally { + if (testConn != this.conn) { + testConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#9437, IF() returns type of [B or java.lang.String + * depending on platform. Fixed earlier, but in here to catch if it ever + * regresses. + * + * @throws Exception + * if the test fails. + */ + public void testBug9437() throws Exception { + String tableName = "testBug9437"; + + if (versionMeetsMinimum(4, 1, 0)) { + createTable(tableName, + "(languageCode char(2) NOT NULL default '', countryCode char(2) NOT NULL default ''," + + "supported enum('no','yes') NOT NULL default 'no', ordering int(11) default NULL," + + "createDate datetime NOT NULL default '1000-01-01 00:00:03', modifyDate timestamp NOT NULL default CURRENT_TIMESTAMP on update" + + " CURRENT_TIMESTAMP, PRIMARY KEY (languageCode,countryCode), KEY languageCode (languageCode)," + + "KEY countryCode (countryCode), KEY ordering (ordering), KEY modifyDate (modifyDate)) DEFAULT CHARSET=utf8", + "InnoDB"); + + this.stmt.executeUpdate("INSERT INTO " + tableName + " (languageCode) VALUES ('en')"); + + String alias = "someLocale"; + String sql = "select if ( languageCode = ?, ?, ? ) as " + alias + " from " + tableName; + this.pstmt = this.conn.prepareStatement(sql); + + int count = 1; + this.pstmt.setObject(count++, "en"); + this.pstmt.setObject(count++, "en_US"); + this.pstmt.setObject(count++, "en_GB"); + + this.rs = this.pstmt.executeQuery(); + + assertTrue(this.rs.next()); + + Object object = this.rs.getObject(alias); + + if (object != null) { + assertEquals("java.lang.String", object.getClass().getName()); + assertEquals("en_US", object.toString()); + } + } + } + + public void testBug9684() throws Exception { + if (versionMeetsMinimum(4, 1, 9)) { + String tableName = "testBug9684"; + + createTable(tableName, "(sourceText text character set utf8 collate utf8_bin)"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES ('abc')"); + this.rs = this.stmt.executeQuery("SELECT sourceText FROM " + tableName); + assertTrue(this.rs.next()); + assertEquals("java.lang.String", this.rs.getString(1).getClass().getName()); + assertEquals("abc", this.rs.getString(1)); + } + } + + /** + * Tests fix for BUG#10156 - Unsigned SMALLINT treated as signed + * + * @throws Exception + * if the test fails. + */ + public void testBug10156() throws Exception { + String tableName = "testBug10156"; + createTable(tableName, "(field1 smallint(5) unsigned, field2 tinyint unsigned, field3 int unsigned)"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (32768, 255, 4294967295)"); + this.rs = this.conn.prepareStatement("SELECT field1, field2, field3 FROM " + tableName).executeQuery(); + assertTrue(this.rs.next()); + assertEquals(32768, this.rs.getInt(1)); + assertEquals(255, this.rs.getInt(2)); + assertEquals(4294967295L, this.rs.getLong(3)); + + assertEquals(String.valueOf(this.rs.getObject(1)), String.valueOf(this.rs.getInt(1))); + assertEquals(String.valueOf(this.rs.getObject(2)), String.valueOf(this.rs.getInt(2))); + assertEquals(String.valueOf(this.rs.getObject(3)), String.valueOf(this.rs.getLong(3))); + + } + + public void testBug10212() throws Exception { + String tableName = "testBug10212"; + createTable(tableName, "(field1 YEAR(4))"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (1974)"); + this.rs = this.conn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertTrue(this.rs.next()); + assertEquals("java.sql.Date", rsmd.getColumnClassName(1)); + assertEquals("java.sql.Date", this.rs.getObject(1).getClass().getName()); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM " + tableName); + + rsmd = this.rs.getMetaData(); + assertTrue(this.rs.next()); + assertEquals("java.sql.Date", rsmd.getColumnClassName(1)); + assertEquals("java.sql.Date", this.rs.getObject(1).getClass().getName()); + } + + /** + * Tests fix for BUG#11190 - ResultSet.moveToCurrentRow() fails to work when + * preceeded with .moveToInsertRow(). + * + * @throws Exception + * if the test fails. + */ + public void testBug11190() throws Exception { + + createTable("testBug11190", "(a CHAR(4) PRIMARY KEY, b VARCHAR(20))"); + this.stmt.executeUpdate("INSERT INTO testBug11190 VALUES('3000','L'),('3001','H'),('1050','B')"); + + Statement updStmt = null; + + try { + updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + + this.rs = updStmt.executeQuery("select * from testBug11190"); + assertTrue("must return a row", this.rs.next()); + String savedValue = this.rs.getString(1); + this.rs.moveToInsertRow(); + this.rs.updateString(1, "4000"); + this.rs.updateString(2, "C"); + this.rs.insertRow(); + + this.rs.moveToCurrentRow(); + assertEquals(savedValue, this.rs.getString(1)); + } finally { + + if (updStmt != null) { + updStmt.close(); + } + + } + } + + /** + * Tests fix for BUG#12104 - Geometry types not handled with server-side + * prepared statements. + * + * @throws Exception + * if the test fails + */ + public void testBug12104() throws Exception { + createTable("testBug12104", "(field1 GEOMETRY)", "MyISAM"); + + if (!versionMeetsMinimum(5, 6)) { + this.stmt.executeUpdate("INSERT INTO testBug12104 VALUES (GeomFromText('POINT(1 1)'))"); + } else { + this.stmt.executeUpdate("INSERT INTO testBug12104 VALUES (ST_GeomFromText('POINT(1 1)'))"); + } + this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug12104"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + System.out.println(this.rs.getObject(1)); + } + + /** + * Tests fix for BUG#13043 - when 'gatherPerfMetrics' is enabled for servers + * < 4.1.0, a NPE is thrown from the constructor of ResultSet if the query + * doesn't use any tables. + * + * @throws Exception + * if the test fails + */ + public void testBug13043() throws Exception { + if (!versionMeetsMinimum(4, 1)) { + Connection perfConn = null; + + try { + Properties props = new Properties(); + props.put("gatherPerfMetrics", "true"); // this property is reported as the cause of NullPointerException + props.put("reportMetricsIntervalMillis", "30000"); // this property is reported as the cause of NullPointerException + perfConn = getConnectionWithProps(props); + this.rs = perfConn.createStatement().executeQuery("SELECT 1"); + } finally { + if (perfConn != null) { + perfConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#13374 - ResultSet.getStatement() on closed result set + * returns NULL (as per JDBC 4.0 spec, but not backwards-compatible). + * + * @throws Exception + * if the test fails + */ + + public void testBug13374() throws Exception { + Statement retainStmt = null; + Connection retainConn = null; + + try { + Properties props = new Properties(); + + props.setProperty("retainStatementAfterResultSetClose", "true"); + + retainConn = getConnectionWithProps(props); + + retainStmt = retainConn.createStatement(); + + this.rs = retainStmt.executeQuery("SELECT 1"); + this.rs.close(); + assertNotNull(this.rs.getStatement()); + + this.rs = this.stmt.executeQuery("SELECT 1"); + this.rs.close(); + + try { + this.rs.getStatement(); + } catch (SQLException sqlEx) { + assertEquals(sqlEx.getSQLState(), SQLError.SQL_STATE_GENERAL_ERROR); + } + + } finally { + + if (retainStmt != null) { + retainStmt.close(); + } + + if (retainConn != null) { + retainConn.close(); + } + } + } + + /** + * Tests bugfix for BUG#14562 - metadata/type for MEDIUMINT UNSIGNED is + * incorrect. + * + * @throws Exception + * if the test fails. + */ + public void testBug14562() throws Exception { + createTable("testBug14562", "(row_order INT, signed_field MEDIUMINT, unsigned_field MEDIUMINT UNSIGNED)"); + + this.stmt.executeUpdate("INSERT INTO testBug14562 VALUES (1, -8388608, 0), (2, 8388607, 16777215)"); + + this.rs = this.stmt.executeQuery("SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order"); + traverseResultSetBug14562(); + + this.rs = this.conn.prepareStatement("SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order").executeQuery(); + traverseResultSetBug14562(); + + if (versionMeetsMinimum(5, 0)) { + CallableStatement storedProc = null; + + try { + createProcedure("sp_testBug14562", "() BEGIN SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order; END"); + storedProc = this.conn.prepareCall("{call sp_testBug14562()}"); + storedProc.execute(); + this.rs = storedProc.getResultSet(); + traverseResultSetBug14562(); + + createProcedure("sp_testBug14562_1", "(OUT param_1 MEDIUMINT, OUT param_2 MEDIUMINT UNSIGNED)" + + "BEGIN SELECT signed_field, unsigned_field INTO param_1, param_2 FROM testBug14562 WHERE row_order=1; END"); + storedProc = this.conn.prepareCall("{call sp_testBug14562_1(?, ?)}"); + storedProc.registerOutParameter(1, Types.INTEGER); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + assertEquals("java.lang.Integer", storedProc.getObject(1).getClass().getName()); + + if (versionMeetsMinimum(5, 1) || versionMeetsMinimum(5, 0, 67)) { + assertEquals("java.lang.Long", storedProc.getObject(2).getClass().getName()); + } else { + assertEquals("java.lang.Integer", storedProc.getObject(2).getClass().getName()); + } + + } finally { + if (storedProc != null) { + storedProc.close(); + } + } + } + + this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(), null, "testBug14562", "%field"); + + assertTrue(this.rs.next()); + + assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE")); + assertEquals("MEDIUMINT", this.rs.getString("TYPE_NAME").toUpperCase(Locale.US)); + + assertTrue(this.rs.next()); + + assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE")); + assertEquals("MEDIUMINT UNSIGNED", this.rs.getString("TYPE_NAME").toUpperCase(Locale.US)); + + // + // The following test is harmless in the 3.1 driver, but is needed for the 5.0 driver, so we'll leave it here + // + if (versionMeetsMinimum(5, 0, 14)) { + Connection infoSchemConn = null; + + try { + Properties props = new Properties(); + props.setProperty("useInformationSchema", "true"); + + infoSchemConn = getConnectionWithProps(props); + + this.rs = infoSchemConn.getMetaData().getColumns(infoSchemConn.getCatalog(), null, "testBug14562", "%field"); + + assertTrue(this.rs.next()); + + assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE")); + assertEquals("MEDIUMINT", this.rs.getString("TYPE_NAME").toUpperCase(Locale.US)); + + assertTrue(this.rs.next()); + + assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE")); + assertEquals("MEDIUMINT UNSIGNED", this.rs.getString("TYPE_NAME").toUpperCase(Locale.US)); + + } finally { + if (infoSchemConn != null) { + infoSchemConn.close(); + } + } + } + } + + public void testBug15604() throws Exception { + createTable("testBug15604_date_cal", "(field1 DATE)"); + Properties props = new Properties(); + props.setProperty("useLegacyDatetimeCode", "false"); + props.setProperty("sessionVariables", "time_zone='America/Chicago'"); + + Connection nonLegacyConn = getConnectionWithProps(props); + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + cal.set(Calendar.YEAR, 2005); + cal.set(Calendar.MONTH, 4); + cal.set(Calendar.DAY_OF_MONTH, 15); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + java.sql.Date sqlDate = new java.sql.Date(cal.getTime().getTime()); + + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(sqlDate); + System.out.println(new java.sql.Date(cal2.getTime().getTime())); + this.pstmt = nonLegacyConn.prepareStatement("INSERT INTO testBug15604_date_cal VALUES (?)"); + + this.pstmt.setDate(1, sqlDate, cal); + this.pstmt.executeUpdate(); + this.rs = nonLegacyConn.createStatement().executeQuery("SELECT field1 FROM testBug15604_date_cal"); + this.rs.next(); + + assertEquals(sqlDate.getTime(), this.rs.getDate(1, cal).getTime()); + } + + public void testBug14897() throws Exception { + createTable("table1", "(id int, name_id int)"); + createTable("table2", "(id int)"); + createTable("lang_table", "(id int, en varchar(255) CHARACTER SET utf8, cz varchar(255) CHARACTER SET utf8)"); + + this.stmt.executeUpdate("insert into table1 values (0, 0)"); + this.stmt.executeUpdate("insert into table2 values (0)"); + this.stmt.executeUpdate("insert into lang_table values (0, 'abcdef', 'ghijkl')"); + this.rs = this.stmt.executeQuery("select a.id, b.id, c.en, c.cz from table1 as a, table2 as b, lang_table as c where a.id = b.id and a.name_id = c.id"); + assertTrue(this.rs.next()); + this.rs.getString("c.cz"); + + this.rs = this.stmt.executeQuery("select table1.*, table2.* FROM table1, table2"); + this.rs.findColumn("table1.id"); + this.rs.findColumn("table2.id"); + } + + /** + * Tests fix for BUG#14609 - Exception thrown for new decimal type when + * using updatable result sets. + * + * @throws Exception + * if the test fails + */ + public void testBug14609() throws Exception { + if (versionMeetsMinimum(5, 0)) { + createTable("testBug14609", "(field1 int primary key, field2 decimal)"); + this.stmt.executeUpdate("INSERT INTO testBug14609 VALUES (1, 1)"); + + PreparedStatement updatableStmt = this.conn.prepareStatement("SELECT field1, field2 FROM testBug14609", ResultSet.TYPE_SCROLL_INSENSITIVE, + ResultSet.CONCUR_UPDATABLE); + + try { + this.rs = updatableStmt.executeQuery(); + } finally { + if (updatableStmt != null) { + updatableStmt.close(); + } + } + } + } + + /** + * Tests fix for BUG#16169 - ResultSet.getNativeShort() causes stack + * overflow error via recurisve calls. + * + * @throws Exception + * if the tests fails + */ + public void testBug16169() throws Exception { + createTable("testBug16169", "(field1 smallint)"); + + this.stmt.executeUpdate("INSERT INTO testBug16169 (field1) VALUES (0)"); + + this.pstmt = this.conn.prepareStatement("SELECT * FROM testBug16169"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + + assertEquals(0, ((Integer) this.rs.getObject("field1")).intValue()); + } + + /** + * Tests fix for BUG#16841 - updatable result set doesn't return + * AUTO_INCREMENT values for insertRow() when multiple column primary keys + * are used. + * + * @throws Exception + * if the test fails. + */ + public void testBug16841() throws Exception { + + createTable("testBug16841", "(CID int( 20 ) NOT NULL default '0', OID int( 20 ) NOT NULL AUTO_INCREMENT ," + + "PatientID int( 20 ) default NULL , PRIMARY KEY ( CID , OID ) , KEY OID ( OID ) , KEY Path ( CID, PatientID))", "MYISAM"); + + String sSQLQuery = "SELECT * FROM testBug16841 WHERE 1 = 0"; + Statement updStmt = null; + + try { + updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + + this.rs = updStmt.executeQuery(sSQLQuery); + + this.rs.moveToInsertRow(); + + this.rs.updateInt("CID", 1); + this.rs.updateInt("PatientID", 1); + + this.rs.insertRow(); + + this.rs.last(); + assertEquals(1, this.rs.getInt("OID")); + } finally { + + if (updStmt != null) { + updStmt.close(); + } + + } + } + + /** + * Tests fix for BUG#17450 - ResultSet.wasNull() not always reset correctly + * for booleans when done via conversion for server-side prepared + * statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug17450() throws Exception { + if (versionMeetsMinimum(4, 1, 0)) { + createTable("testBug17450", "(FOO VARCHAR(100), BAR CHAR NOT NULL)"); + + this.stmt.execute("insert into testBug17450 (foo,bar) values ('foo',true)"); + this.stmt.execute("insert into testBug17450 (foo,bar) values (null,true)"); + + this.pstmt = this.conn.prepareStatement("select * from testBug17450 where foo=?"); + this.pstmt.setString(1, "foo"); + this.rs = this.pstmt.executeQuery(); + checkResult17450(); + + this.pstmt = this.conn.prepareStatement("select * from testBug17450 where foo is null"); + this.rs = this.pstmt.executeQuery(); + checkResult17450(); + + this.rs = this.stmt.executeQuery("select * from testBug17450 where foo='foo'"); + checkResult17450(); + + this.rs = this.stmt.executeQuery("select * from testBug17450 where foo is null"); + checkResult17450(); + } + } + + /** + * Tests fix for BUG#19282 - ResultSet.wasNull() returns incorrect value + * when extracting native string from server-side prepared statement + * generated result set. + * + * @throws Exception + * if the test fails. + */ + public void testBug19282() throws Exception { + createTable("testBug19282", "(field1 VARCHAR(32))"); + this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug19282"); + this.stmt.executeUpdate("INSERT INTO testBug19282 VALUES ('abcdefg')"); + + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + assertEquals(false, this.rs.wasNull()); + this.rs.getString(1); + assertEquals(false, this.rs.wasNull()); + } + + private void checkResult17450() throws Exception { + this.rs.next(); + this.rs.getString(1); + boolean bar = this.rs.getBoolean(2); + + assertEquals("field 2 should be true", true, bar); + assertFalse("wasNull should return false", this.rs.wasNull()); + } + + /** + * Tests fix for BUG# + * + * @throws Exception + */ + public void testBug19568() throws Exception { + if (versionMeetsMinimum(4, 1, 0)) { + createTable("testBug19568", "(field1 BOOLEAN," + (versionMeetsMinimum(5, 0, 0) ? "field2 BIT" : "field2 BOOLEAN") + ")"); + + this.stmt.executeUpdate("INSERT INTO testBug19568 VALUES (1,0), (0, 1)"); + + this.pstmt = this.conn.prepareStatement("SELECT field1, field2 FROM testBug19568 ORDER BY field1 DESC"); + this.rs = this.pstmt.executeQuery(); + + checkResultsBug19568(); + + this.rs = this.stmt.executeQuery("SELECT field1, field2 FROM testBug19568 ORDER BY field1 DESC"); + checkResultsBug19568(); + } + } + + private void checkResultsBug19568() throws SQLException { + // Test all numerical getters, and make sure to alternate true/false across rows so we can catch false-positives if off-by-one errors exist in the + // column getters. + + for (int i = 0; i < 2; i++) { + assertTrue(this.rs.next()); + + for (int j = 0; j < 2; j++) { + assertEquals((i == 1 && j == 1) || (i == 0 && j == 0), this.rs.getBoolean(j + 1)); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getBigDecimal(j + 1).intValue()); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getByte(j + 1)); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getShort(j + 1)); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getInt(j + 1)); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getLong(j + 1)); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getFloat(j + 1), .1); + assertEquals(((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0), this.rs.getDouble(j + 1), .1); + } + } + } + + public void testBug19724() throws Exception { + if (versionMeetsMinimum(4, 1)) { + // can't set this via session on 4.0 :( + + createTable("test19724", "(col1 INTEGER NOT NULL, col2 VARCHAR(255) NULL, PRIMARY KEY (col1))"); + + this.stmt.execute("INSERT IGNORE INTO test19724 VALUES (0, 'Blah'),(1,'Boo')"); + + Connection ansiConn = null; + Statement updStmt = null; + + Properties props = new Properties(); + props.setProperty("sessionVariables", "sql_mode=ansi"); + + try { + ansiConn = getConnectionWithProps(props); + updStmt = ansiConn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery("SELECT * FROM test19724"); + + this.rs.beforeFirst(); + + this.rs.next(); + + this.rs.updateString("col2", "blah2"); + this.rs.updateRow(); + } finally { + if (ansiConn != null) { + ansiConn.close(); + } + } + } + } + + private void traverseResultSetBug14562() throws SQLException { + assertTrue(this.rs.next()); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals("MEDIUMINT", rsmd.getColumnTypeName(1)); + assertEquals("MEDIUMINT UNSIGNED", rsmd.getColumnTypeName(2)); + + assertEquals(Types.INTEGER, rsmd.getColumnType(1)); + assertEquals(Types.INTEGER, rsmd.getColumnType(2)); + + assertEquals("java.lang.Integer", rsmd.getColumnClassName(1)); + assertEquals("java.lang.Integer", rsmd.getColumnClassName(2)); + + assertEquals(-8388608, this.rs.getInt(1)); + assertEquals(0, this.rs.getInt(2)); + + assertEquals("java.lang.Integer", this.rs.getObject(1).getClass().getName()); + assertEquals("java.lang.Integer", this.rs.getObject(2).getClass().getName()); + + assertTrue(this.rs.next()); + + assertEquals(8388607, this.rs.getInt(1)); + assertEquals(16777215, this.rs.getInt(2)); + + assertEquals("java.lang.Integer", this.rs.getObject(1).getClass().getName()); + assertEquals("java.lang.Integer", this.rs.getObject(2).getClass().getName()); + } + + /* + * public void testBug16458() throws Exception { createTable("a", "(id + * INTEGER NOT NULL, primary key (id)) Type=InnoDB"); createTable("b", "(id + * INTEGER NOT NULL, primary key (id)) Type=InnoDB"); createTable("c", "(id + * INTEGER NOT NULL, primary key (id)) Type=InnoDB"); + * + * createTable( "problem_table", "(id int(11) NOT NULL auto_increment," + + * "a_id int(11) NOT NULL default '0'," + "b_id int(11) NOT NULL default + * '0'," + "c_id int(11) default NULL," + "order_num int(2) NOT NULL default + * '0'," + "PRIMARY KEY (id)," + "KEY idx_problem_table__b_id (b_id)," + + * "KEY idx_problem_table__a_id (a_id)," + "KEY idx_problem_table__c_id + * (c_id)," + "CONSTRAINT fk_problem_table__c FOREIGN KEY (c_id) REFERENCES + * c (id)," + "CONSTRAINT fk_problem_table__a FOREIGN KEY (a_id) REFERENCES + * a (id)," + "CONSTRAINT fk_problem_table__b FOREIGN KEY (b_id) REFERENCES + * b (id)" + ")" + "Type=InnoDB"); + * + * this.stmt .executeUpdate("INSERT INTO `a` VALUES " + + * "(1),(4),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23" + * + + * "),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39" + * + "),(40),(41),(42),(43),(45),(46),(47),(48),(49),(50)"); + * + * this.stmt .executeUpdate("INSERT INTO `b` VALUES " + + * "(1),(2),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19" + * + "),(20)"); + * + * this.stmt .executeUpdate("INSERT INTO `c` VALUES " + + * "(1),(2),(3),(13),(15),(16),(22),(30),(31),(32),(33),(34),(35),(36),(37),(148),(1" + * + + * "59),(167),(174),(176),(177),(178),(179),(180),(187),(188),(189),(190),(191),(192" + * + + * "),(193),(194),(195),(196),(197),(198),(199),(200),(201),(202),(203),(204),(205)," + * + "(206),(207),(208)"); + * + * this.stmt .executeUpdate("INSERT INTO `problem_table` VALUES " + + * "(1,1,1,NULL,1),(2,1,4,NULL,1),(3,1,5,NULL,1),(4,1,8,NULL,1),(5,23,1,NULL,1),(6,2" + * + + * "3,4,NULL,1),(7,24,1,NULL,1),(8,24,2,NULL,1),(9,24,4,NULL,1),(10,25,1,NULL,1),(11" + * + + * ",25,2,NULL,1),(12,25,4,NULL,1),(13,27,1,NULL,1),(14,28,1,NULL,1),(15,29,1,NULL,1" + * + + * "),(16,15,2,NULL,1),(17,15,5,NULL,1),(18,15,8,NULL,1),(19,30,1,NULL,1),(20,31,1,N" + * + + * "ULL,1),(21,31,4,NULL,1),(22,32,2,NULL,1),(23,32,4,NULL,1),(24,32,6,NULL,1),(25,3" + * + + * "2,8,NULL,1),(26,32,10,NULL,1),(27,32,11,NULL,1),(28,32,13,NULL,1),(29,32,16,NULL" + * + + * ",1),(30,32,17,NULL,1),(31,32,18,NULL,1),(32,32,19,NULL,1),(33,32,20,NULL,1),(34," + * + + * "33,15,NULL,1),(35,33,15,NULL,1),(36,32,20,206,1),(96,32,9,NULL,1),(100,47,6,NULL" + * + ",1),(101,47,10,NULL,1),(102,47,5,NULL,1),(105,47,19,NULL,1)"); + * PreparedStatement ps = null; + * + * try { ps = conn.prepareStatement("SELECT DISTINCT id,order_num FROM + * problem_table WHERE a_id=? FOR UPDATE", ResultSet.TYPE_FORWARD_ONLY, + * ResultSet.CONCUR_UPDATABLE); + * + * ps.setInt(1, 32); + * + * this.rs = ps.executeQuery(); + * + * while(this.rs.next()) { this.rs.updateInt(3, 51); + * + * this.rs.updateRow(); } } finally { if (this.rs != null) { ResultSet + * toCloseRs = this.rs; this.rs = null; toCloseRs.close(); } + * + * if (ps != null) { PreparedStatement toClosePs = ps; ps = null; + * toClosePs.close(); } } } + */ + + public void testNPEWithUsageAdvisor() throws Exception { + Connection advisorConn = null; + + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + + advisorConn = getConnectionWithProps(props); + this.pstmt = advisorConn.prepareStatement("SELECT 1"); + this.rs = this.pstmt.executeQuery(); + this.rs.close(); + this.rs = this.pstmt.executeQuery(); + } + + public void testAllTypesForNull() throws Exception { + Properties props = new Properties(); + props.setProperty("jdbcCompliantTruncation", "false"); + props.setProperty("zeroDateTimeBehavior", "round"); + Connection conn2 = getConnectionWithProps(props); + Statement stmt2 = conn2.createStatement(); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getTypeInfo(); + + boolean firstColumn = true; + int numCols = 1; + StringBuilder createStatement = new StringBuilder("CREATE TABLE testAllTypes ("); + List wasDatetimeTypeList = new ArrayList(); + + while (this.rs.next()) { + String dataType = this.rs.getString("TYPE_NAME").toUpperCase(); + + boolean wasDateTime = false; + + if (dataType.indexOf("DATE") != -1 || dataType.indexOf("TIME") != -1) { + wasDateTime = true; + } + + if (!"BOOL".equalsIgnoreCase(dataType) && !"LONG VARCHAR".equalsIgnoreCase(dataType) && !"LONG VARBINARY".equalsIgnoreCase(dataType) + && !"ENUM".equalsIgnoreCase(dataType) && !"SET".equalsIgnoreCase(dataType)) { + wasDatetimeTypeList.add(new Boolean(wasDateTime)); + createStatement.append("\n\t"); + if (!firstColumn) { + createStatement.append(","); + } else { + firstColumn = false; + } + + createStatement.append("field_"); + createStatement.append(numCols++); + createStatement.append(" "); + + createStatement.append(dataType); + + if ("VARCHAR".equalsIgnoreCase(dataType) || "VARBINARY".equalsIgnoreCase(dataType)) { + // we can't use max varchar precision because it is equal to max row length + createStatement.append("(255)"); + + } else if (dataType.indexOf("CHAR") != -1 + || dataType.indexOf("BINARY") != -1 && dataType.indexOf("BLOB") == -1 && dataType.indexOf("TEXT") == -1) { + createStatement.append("("); + createStatement.append(this.rs.getString("PRECISION")); + createStatement.append(")"); + } + + createStatement.append(" NULL DEFAULT NULL"); + } + } + + createStatement.append("\n)"); + + stmt2.executeUpdate("DROP TABLE IF EXISTS testAllTypes"); + + stmt2.executeUpdate(createStatement.toString()); + StringBuilder insertStatement = new StringBuilder("INSERT INTO testAllTypes VALUES (NULL"); + for (int i = 1; i < numCols - 1; i++) { + insertStatement.append(", NULL"); + } + insertStatement.append(")"); + stmt2.executeUpdate(insertStatement.toString()); + + this.rs = stmt2.executeQuery("SELECT * FROM testAllTypes"); + + testAllFieldsForNull(this.rs); + this.rs.close(); + + this.rs = this.conn.prepareStatement("SELECT * FROM testAllTypes").executeQuery(); + testAllFieldsForNull(this.rs); + + stmt2.executeUpdate("DELETE FROM testAllTypes"); + + insertStatement = new StringBuilder("INSERT INTO testAllTypes VALUES ("); + + boolean needsNow = wasDatetimeTypeList.get(0).booleanValue(); + + if (needsNow) { + insertStatement.append("NOW()"); + } else { + insertStatement.append("0"); + } + + for (int i = 1; i < numCols - 1; i++) { + needsNow = wasDatetimeTypeList.get(i).booleanValue(); + insertStatement.append(","); + if (needsNow) { + insertStatement.append("NOW()"); + } else { + insertStatement.append("0"); + } + } + + insertStatement.append(")"); + + stmt2.executeUpdate(insertStatement.toString()); + + this.rs = stmt2.executeQuery("SELECT * FROM testAllTypes"); + + testAllFieldsForNotNull(this.rs, wasDatetimeTypeList); + this.rs.close(); + + this.rs = conn2.prepareStatement("SELECT * FROM testAllTypes").executeQuery(); + testAllFieldsForNotNull(this.rs, wasDatetimeTypeList); + + stmt2.executeUpdate("DROP TABLE IF EXISTS testAllTypes"); + } + + @SuppressWarnings("deprecation") + private void testAllFieldsForNull(ResultSet rsToTest) throws Exception { + ResultSetMetaData rsmd = this.rs.getMetaData(); + int numCols = rsmd.getColumnCount(); + + while (rsToTest.next()) { + for (int i = 0; i < numCols - 1; i++) { + String typeName = rsmd.getColumnTypeName(i + 1); + + if ("VARBINARY".equalsIgnoreCase(typeName)) { + System.out.println(); + } + + if (!"BIT".equalsIgnoreCase(typeName)) { + assertEquals(false, rsToTest.getBoolean(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + + assertEquals(0, rsToTest.getDouble(i + 1), 0 /* delta */); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(0, rsToTest.getFloat(i + 1), 0 /* delta */); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(0, rsToTest.getInt(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(0, rsToTest.getLong(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getObject(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getString(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getAsciiStream(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getBigDecimal(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getBinaryStream(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getBlob(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(0, rsToTest.getByte(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getBytes(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getCharacterStream(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getClob(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getDate(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(0, rsToTest.getShort(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getTime(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getTimestamp(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getUnicodeStream(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + assertEquals(null, rsToTest.getURL(i + 1)); + assertTrue("for type " + typeName, rsToTest.wasNull()); + } + } + } + } + + @SuppressWarnings("deprecation") + private void testAllFieldsForNotNull(ResultSet rsToTest, List wasDatetimeTypeList) throws Exception { + ResultSetMetaData rsmd = this.rs.getMetaData(); + int numCols = rsmd.getColumnCount(); + + while (rsToTest.next()) { + for (int i = 0; i < numCols - 1; i++) { + boolean wasDatetimeType = wasDatetimeTypeList.get(i).booleanValue(); + String typeName = rsmd.getColumnTypeName(i + 1); + int sqlType = rsmd.getColumnType(i + 1); + + if (!"BIT".equalsIgnoreCase(typeName) && sqlType != Types.BINARY && sqlType != Types.VARBINARY && sqlType != Types.LONGVARBINARY) { + if (!wasDatetimeType) { + + assertEquals(false, rsToTest.getBoolean(i + 1)); + + assertTrue(!rsToTest.wasNull()); + + assertEquals(0, rsToTest.getDouble(i + 1), 0 /* delta */); + assertTrue(!rsToTest.wasNull()); + assertEquals(0, rsToTest.getFloat(i + 1), 0 /* delta */); + assertTrue(!rsToTest.wasNull()); + assertEquals(0, rsToTest.getInt(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertEquals(0, rsToTest.getLong(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertEquals(0, rsToTest.getByte(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertEquals(0, rsToTest.getShort(i + 1)); + assertTrue(!rsToTest.wasNull()); + } + + assertNotNull(rsToTest.getObject(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getString(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getAsciiStream(i + 1)); + assertTrue(!rsToTest.wasNull()); + + assertNotNull(rsToTest.getBinaryStream(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getBlob(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getBytes(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getCharacterStream(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getClob(i + 1)); + assertTrue(!rsToTest.wasNull()); + + String columnClassName = rsmd.getColumnClassName(i + 1); + + boolean canBeUsedAsDate = !("java.lang.Boolean".equals(columnClassName) || "java.lang.Double".equals(columnClassName) + || "java.lang.Float".equals(columnClassName) || "java.lang.Real".equals(columnClassName) + || "java.math.BigDecimal".equals(columnClassName)); + + if (canBeUsedAsDate) { + assertNotNull(rsToTest.getDate(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getTime(i + 1)); + assertTrue(!rsToTest.wasNull()); + assertNotNull(rsToTest.getTimestamp(i + 1)); + assertTrue(!rsToTest.wasNull()); + } + + assertNotNull(rsToTest.getUnicodeStream(i + 1)); + assertTrue(!rsToTest.wasNull()); + + try { + assertNotNull(rsToTest.getURL(i + 1)); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf("URL") != -1); + } + + assertTrue(!rsToTest.wasNull()); + } + } + } + } + + public void testNPEWithStatementsAndTime() throws Exception { + createTable("testNPETime", "(field1 TIME NULL, field2 DATETIME NULL, field3 DATE NULL)"); + this.stmt.executeUpdate("INSERT INTO testNPETime VALUES (null, null, null)"); + this.pstmt = this.conn.prepareStatement("SELECT field1, field2, field3 FROM testNPETime"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + + for (int i = 0; i < 3; i++) { + assertEquals(null, this.rs.getTime(i + 1)); + assertEquals(true, this.rs.wasNull()); + } + + for (int i = 0; i < 3; i++) { + assertEquals(null, this.rs.getTimestamp(i + 1)); + assertEquals(true, this.rs.wasNull()); + } + + for (int i = 0; i < 3; i++) { + assertEquals(null, this.rs.getDate(i + 1)); + assertEquals(true, this.rs.wasNull()); + } + } + + public void testEmptyStringsWithNumericGetters() throws Exception { + createTable("emptyStringTable", "(field1 char(32))"); + this.stmt.executeUpdate("INSERT INTO emptyStringTable VALUES ('')"); + this.rs = this.stmt.executeQuery("SELECT field1 FROM emptyStringTable"); + assertTrue(this.rs.next()); + createTable("emptyStringTable", "(field1 char(32))"); + this.stmt.executeUpdate("INSERT INTO emptyStringTable VALUES ('')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM emptyStringTable"); + assertTrue(this.rs.next()); + checkEmptyConvertToZero(); + + this.rs = this.conn.prepareStatement("SELECT field1 FROM emptyStringTable").executeQuery(); + assertTrue(this.rs.next()); + checkEmptyConvertToZero(); + + Properties props = new Properties(); + props.setProperty("useFastIntParsing", "false"); + + Connection noFastIntParseConn = getConnectionWithProps(props); + Statement noFastIntStmt = noFastIntParseConn.createStatement(); + + this.rs = noFastIntStmt.executeQuery("SELECT field1 FROM emptyStringTable"); + assertTrue(this.rs.next()); + checkEmptyConvertToZero(); + + this.rs = noFastIntParseConn.prepareStatement("SELECT field1 FROM emptyStringTable").executeQuery(); + assertTrue(this.rs.next()); + checkEmptyConvertToZero(); + + // + // Now, be more pedantic.... + // + + props = new Properties(); + props.setProperty("emptyStringsConvertToZero", "false"); + + Connection pedanticConn = getConnectionWithProps(props); + Statement pedanticStmt = pedanticConn.createStatement(); + + this.rs = pedanticStmt.executeQuery("SELECT field1 FROM emptyStringTable"); + assertTrue(this.rs.next()); + + checkEmptyConvertToZeroException(); + + this.rs = pedanticConn.prepareStatement("SELECT field1 FROM emptyStringTable").executeQuery(); + assertTrue(this.rs.next()); + checkEmptyConvertToZeroException(); + + props = new Properties(); + props.setProperty("emptyStringsConvertToZero", "false"); + props.setProperty("useFastIntParsing", "false"); + + pedanticConn = getConnectionWithProps(props); + pedanticStmt = pedanticConn.createStatement(); + + this.rs = pedanticStmt.executeQuery("SELECT field1 FROM emptyStringTable"); + assertTrue(this.rs.next()); + + checkEmptyConvertToZeroException(); + + this.rs = pedanticConn.prepareStatement("SELECT field1 FROM emptyStringTable").executeQuery(); + assertTrue(this.rs.next()); + checkEmptyConvertToZeroException(); + } + + public void testNegativeOneIsTrue() throws Exception { + if (!versionMeetsMinimum(5, 0, 3)) { + String tableName = "testNegativeOneIsTrue"; + Connection tinyInt1IsBitConn = null; + + try { + createTable(tableName, "(field1 BIT)"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (-1)"); + + Properties props = new Properties(); + props.setProperty("tinyInt1isBit", "true"); + tinyInt1IsBitConn = getConnectionWithProps(props); + + this.rs = tinyInt1IsBitConn.createStatement().executeQuery("SELECT field1 FROM " + tableName); + assertTrue(this.rs.next()); + assertEquals(true, this.rs.getBoolean(1)); + + this.rs = tinyInt1IsBitConn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + assertTrue(this.rs.next()); + assertEquals(true, this.rs.getBoolean(1)); + + } finally { + if (tinyInt1IsBitConn != null) { + tinyInt1IsBitConn.close(); + } + } + } + } + + /** + * @throws SQLException + */ + private void checkEmptyConvertToZero() throws SQLException { + assertEquals(0, this.rs.getByte(1)); + assertEquals(0, this.rs.getShort(1)); + assertEquals(0, this.rs.getInt(1)); + assertEquals(0, this.rs.getLong(1)); + assertEquals(0, this.rs.getFloat(1), 0.1); + assertEquals(0, this.rs.getDouble(1), 0.1); + assertEquals(0, this.rs.getBigDecimal(1).intValue()); + } + + /** + */ + private void checkEmptyConvertToZeroException() { + try { + assertEquals(0, this.rs.getByte(1)); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getShort(1)); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getInt(1)); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getLong(1)); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getFloat(1), 0.1); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getDouble(1), 0.1); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + try { + assertEquals(0, this.rs.getBigDecimal(1).intValue()); + fail("Should've thrown an exception!"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, sqlEx.getSQLState()); + } + } + + /** + * Tests fix for BUG#10485, SQLException thrown when retrieving YEAR(2) with + * ResultSet.getString(). + * + * @throws Exception + * if the test fails. + */ + public void testBug10485() throws Exception { + + if (versionMeetsMinimum(5, 7, 5)) { + // Nothing to test, YEAR(2) is removed starting from 5.7.5 + return; + } + + String tableName = "testBug10485"; + + Calendar nydCal = null; + + if (((com.mysql.jdbc.Connection) this.conn).getUseGmtMillisForDatetimes()) { + nydCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + } else { + nydCal = Calendar.getInstance(); + } + + nydCal.set(2005, 0, 1, 0, 0, 0); + + Date newYears2005 = new Date(nydCal.getTime().getTime()); + + createTable(tableName, "(field1 YEAR(2))"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES ('05')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM " + tableName); + assertTrue(this.rs.next()); + + assertEquals(newYears2005.toString(), this.rs.getString(1)); + + this.rs = this.conn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + assertTrue(this.rs.next()); + assertEquals(newYears2005.toString(), this.rs.getString(1)); + + Properties props = new Properties(); + props.setProperty("yearIsDateType", "false"); + + Connection yearShortConn = getConnectionWithProps(props); + this.rs = yearShortConn.createStatement().executeQuery("SELECT field1 FROM " + tableName); + assertTrue(this.rs.next()); + + String expectedShort = versionMeetsMinimum(5, 6, 6) ? "2005" : "05"; + + assertEquals(expectedShort, this.rs.getString(1)); + + this.rs = yearShortConn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + assertTrue(this.rs.next()); + assertEquals(expectedShort, this.rs.getString(1)); + + if (versionMeetsMinimum(5, 0)) { + + createProcedure("testBug10485", "()\nBEGIN\nSELECT field1 FROM " + tableName + ";\nEND"); + + this.rs = this.conn.prepareCall("{CALL testBug10485()}").executeQuery(); + assertTrue(this.rs.next()); + assertEquals(newYears2005.toString(), this.rs.getString(1)); + + this.rs = yearShortConn.prepareCall("{CALL testBug10485()}").executeQuery(); + assertTrue(this.rs.next()); + assertEquals(expectedShort, this.rs.getString(1)); + + } + } + + /** + * Tests fix for BUG#11552, wrong values returned from server-side prepared + * statements if values are unsigned. + * + * @throws Exception + * if the test fails. + */ + public void testBug11552() throws Exception { + createTable("testBug11552", "(field1 INT UNSIGNED, field2 TINYINT UNSIGNED, field3 SMALLINT UNSIGNED, field4 BIGINT UNSIGNED)"); + this.stmt.executeUpdate("INSERT INTO testBug11552 VALUES (2, 2, 2, 2), (4294967294, 255, 32768, 18446744073709551615 )"); + this.rs = this.conn.prepareStatement("SELECT field1, field2, field3, field4 FROM testBug11552 ORDER BY field1 ASC").executeQuery(); + this.rs.next(); + assertEquals("2", this.rs.getString(1)); + assertEquals("2", this.rs.getObject(1).toString()); + assertEquals("2", String.valueOf(this.rs.getLong(1))); + + assertEquals("2", this.rs.getString(2)); + assertEquals("2", this.rs.getObject(2).toString()); + assertEquals("2", String.valueOf(this.rs.getLong(2))); + + assertEquals("2", this.rs.getString(3)); + assertEquals("2", this.rs.getObject(3).toString()); + assertEquals("2", String.valueOf(this.rs.getLong(3))); + + assertEquals("2", this.rs.getString(4)); + assertEquals("2", this.rs.getObject(4).toString()); + assertEquals("2", String.valueOf(this.rs.getLong(4))); + + this.rs.next(); + + assertEquals("4294967294", this.rs.getString(1)); + assertEquals("4294967294", this.rs.getObject(1).toString()); + assertEquals("4294967294", String.valueOf(this.rs.getLong(1))); + + assertEquals("255", this.rs.getString(2)); + assertEquals("255", this.rs.getObject(2).toString()); + assertEquals("255", String.valueOf(this.rs.getLong(2))); + + assertEquals("32768", this.rs.getString(3)); + assertEquals("32768", this.rs.getObject(3).toString()); + assertEquals("32768", String.valueOf(this.rs.getLong(3))); + + assertEquals("18446744073709551615", this.rs.getString(4)); + assertEquals("18446744073709551615", this.rs.getObject(4).toString()); + } + + /** + * Tests correct detection of truncation of non-sig digits. + * + * @throws Exception + * if the test fails. + */ + public void testTruncationOfNonSigDigits() throws Exception { + if (versionMeetsMinimum(4, 1, 0)) { + createTable("testTruncationOfNonSigDigits", "(field1 decimal(12,2), field2 varchar(2))", "Innodb"); + + this.stmt.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (123456.2345, 'ab')"); + + try { + this.stmt.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (1234561234561.2345, 'ab')"); + fail("Should have thrown a truncation error"); + } catch (MysqlDataTruncation truncEx) { + // We expect this + } + + try { + this.stmt.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (1234.2345, 'abcd')"); + fail("Should have thrown a truncation error"); + } catch (MysqlDataTruncation truncEx) { + // We expect this + } + } + } + + /** + * Tests fix for BUG#20479 - Updatable result set throws ClassCastException + * when there is row data and moveToInsertRow() is called. + * + * @throws Exception + * if the test fails. + */ + public void testBug20479() throws Exception { + PreparedStatement updStmt = null; + + createTable("testBug20479", "(field1 INT NOT NULL PRIMARY KEY)"); + this.stmt.executeUpdate("INSERT INTO testBug20479 VALUES (2), (3), (4)"); + + try { + updStmt = this.conn.prepareStatement("SELECT * FROM testBug20479 Where field1 > ? ORDER BY field1", ResultSet.TYPE_SCROLL_SENSITIVE, + ResultSet.CONCUR_UPDATABLE); + + updStmt.setInt(1, 1); + this.rs = updStmt.executeQuery(); + this.rs.next(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 45); + this.rs.insertRow(); + this.rs.moveToCurrentRow(); + assertEquals(2, this.rs.getInt(1)); + this.rs.next(); + this.rs.next(); + this.rs.next(); + assertEquals(45, this.rs.getInt(1)); + } finally { + if (updStmt != null) { + updStmt.close(); + } + } + } + + /** + * Tests fix for BUG#20485 - Updatable result set that contains a BIT column + * fails when server-side prepared statements are used. + * + * @throws Exception + * if the test fails. + */ + public void testBug20485() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + PreparedStatement updStmt = null; + + createTable("testBug20485", "(field1 INT NOT NULL PRIMARY KEY, field2 BIT)"); + this.stmt.executeUpdate("INSERT INTO testBug20485 VALUES (2, 1), (3, 1), (4, 1)"); + + try { + updStmt = this.conn.prepareStatement("SELECT * FROM testBug20485 ORDER BY field1", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery(); + } finally { + if (updStmt != null) { + updStmt.close(); + } + } + } + + /** + * Tests fix for BUG#20306 - ResultSet.getShort() for UNSIGNED TINYINT + * returns incorrect values when using server-side prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug20306() throws Exception { + createTable("testBug20306", "(field1 TINYINT UNSIGNED, field2 TINYINT UNSIGNED)"); + this.stmt.executeUpdate("INSERT INTO testBug20306 VALUES (2, 133)"); + + this.pstmt = this.conn.prepareStatement("SELECT field1, field2 FROM testBug20306"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + checkBug20306(); + + this.rs = this.stmt.executeQuery("SELECT field1, field2 FROM testBug20306"); + this.rs.next(); + checkBug20306(); + + } + + private void checkBug20306() throws Exception { + assertEquals(2, this.rs.getByte(1)); + assertEquals(2, this.rs.getInt(1)); + assertEquals(2, this.rs.getShort(1)); + assertEquals(2, this.rs.getLong(1)); + assertEquals(2.0, this.rs.getFloat(1), 0); + assertEquals(2.0, this.rs.getDouble(1), 0); + assertEquals(2, this.rs.getBigDecimal(1).intValue()); + + assertEquals(133, this.rs.getInt(2)); + assertEquals(133, this.rs.getShort(2)); + assertEquals(133, this.rs.getLong(2)); + assertEquals(133.0, this.rs.getFloat(2), 0); + assertEquals(133.0, this.rs.getDouble(2), 0); + assertEquals(133, this.rs.getBigDecimal(2).intValue()); + } + + /** + * Tests fix for BUG#21062 - ResultSet.getSomeInteger() doesn't work for + * BIT(>1) + * + * @throws Exception + * if the test fails. + */ + public void testBug21062() throws Exception { + if (versionMeetsMinimum(5, 0, 5)) { + createTable("testBug21062", "(bit_7_field BIT(7), bit_31_field BIT(31), bit_12_field BIT(12))"); + + int max7Bits = 127; + long max31Bits = 2147483647L; + int max12Bits = 4095; + + this.stmt.executeUpdate("INSERT INTO testBug21062 VALUES (" + max7Bits + "," + max31Bits + "," + max12Bits + ")"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testBug21062"); + + this.rs.next(); + + assertEquals(127, this.rs.getInt(1)); + assertEquals(127, this.rs.getShort(1)); + assertEquals(127, this.rs.getLong(1)); + + assertEquals(2147483647, this.rs.getInt(2)); + assertEquals(2147483647, this.rs.getLong(2)); + + assertEquals(4095, this.rs.getInt(3)); + assertEquals(4095, this.rs.getShort(3)); + assertEquals(4095, this.rs.getLong(3)); + } + } + + /** + * Tests fix for BUG#18880 - ResultSet.getFloatFromString() can't retrieve + * values near Float.MIN/MAX_VALUE. + * + * @throws Exception + * if the test fails. + */ + public void testBug18880() throws Exception { + this.rs = this.stmt.executeQuery("SELECT 3.4E38,1.4E-45"); + this.rs.next(); + this.rs.getFloat(1); + this.rs.getFloat(2); + } + + /** + * Tests fix for BUG#15677, wrong values returned from getShort() if SQL + * values are tinyint unsigned. + * + * @throws Exception + * if the test fails. + */ + public void testBug15677() throws Exception { + createTable("testBug15677", "(id BIGINT, field1 TINYINT UNSIGNED)"); + this.stmt.executeUpdate("INSERT INTO testBug15677 VALUES (1, 0), (2, 127), (3, 128), (4, 255)"); + this.rs = this.conn.prepareStatement("SELECT field1 FROM testBug15677 ORDER BY id ASC").executeQuery(); + this.rs.next(); + assertEquals("0", this.rs.getString(1)); + assertEquals("0", this.rs.getObject(1).toString()); + assertEquals("0", String.valueOf(this.rs.getShort(1))); + + this.rs.next(); + assertEquals("127", this.rs.getString(1)); + assertEquals("127", this.rs.getObject(1).toString()); + assertEquals("127", String.valueOf(this.rs.getShort(1))); + + this.rs.next(); + assertEquals("128", this.rs.getString(1)); + assertEquals("128", this.rs.getObject(1).toString()); + assertEquals("128", String.valueOf(this.rs.getShort(1))); + + this.rs.next(); + assertEquals("255", this.rs.getString(1)); + assertEquals("255", this.rs.getObject(1).toString()); + assertEquals("255", String.valueOf(this.rs.getShort(1))); + } + + public void testBooleans() throws Exception { + if (versionMeetsMinimum(5, 0)) { + createTable("testBooleans", + "(ob int, field1 BOOLEAN, field2 TINYINT, field3 SMALLINT, field4 INT, field5 MEDIUMINT, field6 BIGINT, field7 FLOAT, field8 DOUBLE, field9 DECIMAL, field10 VARCHAR(32), field11 BINARY(3), field12 VARBINARY(3), field13 BLOB)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testBooleans VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + this.pstmt.setInt(1, 1); + this.pstmt.setBoolean(2, false); + this.pstmt.setByte(3, (byte) 0); + this.pstmt.setInt(4, 0); + this.pstmt.setInt(5, 0); + this.pstmt.setInt(6, 0); + this.pstmt.setLong(7, 0); + this.pstmt.setFloat(8, 0); + this.pstmt.setDouble(9, 0); + this.pstmt.setBigDecimal(10, new BigDecimal("0")); + this.pstmt.setString(11, "false"); + this.pstmt.setBytes(12, new byte[] { 0 }); + this.pstmt.setBytes(13, new byte[] { 0 }); + this.pstmt.setBytes(14, new byte[] { 0 }); + + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 2); + this.pstmt.setBoolean(2, true); + this.pstmt.setByte(3, (byte) 1); + this.pstmt.setInt(4, 1); + this.pstmt.setInt(5, 1); + this.pstmt.setInt(6, 1); + this.pstmt.setLong(7, 1); + this.pstmt.setFloat(8, 1); + this.pstmt.setDouble(9, 1); + this.pstmt.setBigDecimal(10, new BigDecimal("1")); + this.pstmt.setString(11, "true"); + this.pstmt.setBytes(12, new byte[] { 1 }); + this.pstmt.setBytes(13, new byte[] { 1 }); + this.pstmt.setBytes(14, new byte[] { 1 }); + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 3); + this.pstmt.setBoolean(2, true); + this.pstmt.setByte(3, (byte) 1); + this.pstmt.setInt(4, 1); + this.pstmt.setInt(5, 1); + this.pstmt.setInt(6, 1); + this.pstmt.setLong(7, 1); + this.pstmt.setFloat(8, 1); + this.pstmt.setDouble(9, 1); + this.pstmt.setBigDecimal(10, new BigDecimal("1")); + this.pstmt.setString(11, "true"); + this.pstmt.setBytes(12, new byte[] { 2 }); + this.pstmt.setBytes(13, new byte[] { 2 }); + this.pstmt.setBytes(14, new byte[] { 2 }); + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 4); + this.pstmt.setBoolean(2, true); + this.pstmt.setByte(3, (byte) 1); + this.pstmt.setInt(4, 1); + this.pstmt.setInt(5, 1); + this.pstmt.setInt(6, 1); + this.pstmt.setLong(7, 1); + this.pstmt.setFloat(8, 1); + this.pstmt.setDouble(9, 1); + this.pstmt.setBigDecimal(10, new BigDecimal("1")); + this.pstmt.setString(11, "true"); + this.pstmt.setBytes(12, new byte[] { -1 }); + this.pstmt.setBytes(13, new byte[] { -1 }); + this.pstmt.setBytes(14, new byte[] { -1 }); + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 5); + this.pstmt.setBoolean(2, false); + this.pstmt.setByte(3, (byte) 0); + this.pstmt.setInt(4, 0); + this.pstmt.setInt(5, 0); + this.pstmt.setInt(6, 0); + this.pstmt.setLong(7, 0); + this.pstmt.setFloat(8, 0); + this.pstmt.setDouble(9, 0); + this.pstmt.setBigDecimal(10, new BigDecimal("0")); + this.pstmt.setString(11, "false"); + this.pstmt.setBytes(12, new byte[] { 0, 0 }); + this.pstmt.setBytes(13, new byte[] { 0, 0 }); + this.pstmt.setBytes(14, new byte[] { 0, 0 }); + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 6); + this.pstmt.setBoolean(2, true); + this.pstmt.setByte(3, (byte) 1); + this.pstmt.setInt(4, 1); + this.pstmt.setInt(5, 1); + this.pstmt.setInt(6, 1); + this.pstmt.setLong(7, 1); + this.pstmt.setFloat(8, 1); + this.pstmt.setDouble(9, 1); + this.pstmt.setBigDecimal(10, new BigDecimal("1")); + this.pstmt.setString(11, "true"); + this.pstmt.setBytes(12, new byte[] { 1, 0 }); + this.pstmt.setBytes(13, new byte[] { 1, 0 }); + this.pstmt.setBytes(14, new byte[] { 1, 0 }); + this.pstmt.executeUpdate(); + + this.pstmt.setInt(1, 7); + this.pstmt.setBoolean(2, false); + this.pstmt.setByte(3, (byte) 0); + this.pstmt.setInt(4, 0); + this.pstmt.setInt(5, 0); + this.pstmt.setInt(6, 0); + this.pstmt.setLong(7, 0); + this.pstmt.setFloat(8, 0); + this.pstmt.setDouble(9, 0); + this.pstmt.setBigDecimal(10, new BigDecimal("0")); + this.pstmt.setString(11, ""); + this.pstmt.setBytes(12, new byte[] {}); + this.pstmt.setBytes(13, new byte[] {}); + this.pstmt.setBytes(14, new byte[] {}); + this.pstmt.executeUpdate(); + + this.rs = this.stmt.executeQuery( + "SELECT field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13 FROM testBooleans ORDER BY ob"); + + boolean[] testVals = new boolean[] { false, true, true, true, false, true, false }; + + int i = 0; + + while (this.rs.next()) { + for (int j = 0; j > 13; j++) { + assertEquals("For field_" + (j + 1) + ", row " + (i + 1), testVals[i], this.rs.getBoolean(j + 1)); + } + + i++; + } + + this.rs = this.conn.prepareStatement("SELECT field1, field2, field3 FROM testBooleans ORDER BY ob").executeQuery(); + + i = 0; + + while (this.rs.next()) { + for (int j = 0; j > 13; j++) { + assertEquals("For field_" + (j + 1) + ", row " + (i + 1), testVals[i], this.rs.getBoolean(j + 1)); + } + + i++; + } + } + } + + /** + * Tests fix(es) for BUG#21379 - column names don't match metadata in cases + * where server doesn't return original column names (functions) thus + * breaking compatibility with applications that expect 1-1 mappings between + * findColumn() and rsmd.getColumnName(). + * + * @throws Exception + * if the test fails. + */ + public void testBug21379() throws Exception { + // + // Test the 1-1 mapping between rs.findColumn() and rsmd.getColumnName() in the case where original column names are not returned, thus preserving + // pre-C/J 5.0 behavior for these cases + // + + this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID() AS id"); + this.rs.next(); + assertEquals("id", this.rs.getMetaData().getColumnName(1)); + assertEquals(1, this.rs.findColumn("id")); + + if (versionMeetsMinimum(4, 1)) { + // + // test complete emulation of C/J 3.1 and earlier behavior through configuration option + // + + createTable("testBug21379", "(field1 int)"); + Connection legacyConn = null; + Statement legacyStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("useOldAliasMetadataBehavior", "true"); + legacyConn = getConnectionWithProps(props); + legacyStmt = legacyConn.createStatement(); + + this.rs = legacyStmt.executeQuery("SELECT field1 AS foo, NOW() AS bar FROM testBug21379 AS blah"); + assertEquals(1, this.rs.findColumn("foo")); + assertEquals(2, this.rs.findColumn("bar")); + assertEquals("blah", this.rs.getMetaData().getTableName(1)); + } finally { + if (legacyConn != null) { + legacyConn.close(); + } + } + } + } + + /** + * Tests fix for BUG#21814 - time values outside valid range silently wrap + * + * @throws Exception + * if the test fails. + */ + public void testBug21814() throws Exception { + + try { + this.rs = this.stmt.executeQuery("SELECT '25:01'"); + this.rs.next(); + this.rs.getTime(1); + fail("Expected exception"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + try { + this.rs = this.stmt.executeQuery("SELECT '23:92'"); + this.rs.next(); + this.rs.getTime(1); + fail("Expected exception"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + } + + /** + * Tests for a server bug - needs to be revisited when the server is fixed. + * + * @throws Exception + * if the test fails. + */ + public void testBug24710() throws Exception { + if (!versionMeetsMinimum(6, 0)) { + return; + } + + createTable("testBug24710", "(x varbinary(256))"); + + this.stmt.executeUpdate("insert into testBug24710(x) values(0x0000000000), (0x1111111111), (0x2222222222), (0x3333333333)," + + "(0x4444444444), (0x5555555555), (0x6666666666), (0x7777777777), (0x8888888888), (0x9999999999), (0xaaaaaaaaaa)," + + "(0xbbbbbbbbbb), (0xcccccccccc), (0xdddddddddd), (0xeeeeeeeeee), (0xffffffffff)"); + + this.rs = this.stmt.executeQuery("select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1"); + + assertEquals(Types.VARBINARY, this.rs.getMetaData().getColumnType(1)); + assertEquals(Types.VARBINARY, this.rs.getMetaData().getColumnType(2)); + + this.rs = ((com.mysql.jdbc.Connection) this.conn) + .serverPrepareStatement("select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1").executeQuery(); + + assertEquals(Types.VARBINARY, this.rs.getMetaData().getColumnType(1)); + assertEquals(Types.VARBINARY, this.rs.getMetaData().getColumnType(2)); + } + + /** + * Tests fix for BUG#25328 - BIT(> 1) is returned as java.lang.String from + * ResultSet.getObject() rather than byte[]. + * + * @throws Exception + * if the test fails. + */ + public void testbug25328() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testBug25382", "(BINARY_VAL BIT(64) NULL)"); + + byte[] bytearr = new byte[8]; + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug25382 VALUES(?)"); + this.pstmt.setObject(1, bytearr, java.sql.Types.BINARY); + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.clearParameters(); + + this.rs = this.stmt.executeQuery("Select BINARY_VAL from testBug25382"); + this.rs.next(); + assertEquals(this.rs.getObject(1).getClass(), bytearr.getClass()); + } + + /** + * Tests fix for BUG#25517 - Statement.setMaxRows() is not effective on + * result sets materialized from cursors. + * + * @throws Exception + * if the test fails + */ + public void testBug25517() throws Exception { + Connection fetchConn = null; + Statement fetchStmt = null; + + createTable("testBug25517", "(field1 int)"); + + StringBuilder insertBuf = new StringBuilder("INSERT INTO testBug25517 VALUES (1)"); + + for (int i = 0; i < 100; i++) { + insertBuf.append(",(" + i + ")"); + } + + this.stmt.executeUpdate(insertBuf.toString()); + + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("useCursorFetch", "true"); + + fetchConn = getConnectionWithProps(props); + fetchStmt = fetchConn.createStatement(); + + // int[] maxRows = new int[] {1, 4, 5, 11, 12, 13, 16, 50, 51, 52, 100}; + int[] fetchSizes = new int[] { 1, 4, 10, 25, 100 }; + List maxRows = new ArrayList(); + maxRows.add(new Integer(1)); + + for (int i = 0; i < fetchSizes.length; i++) { + if (fetchSizes[i] != 1) { + maxRows.add(new Integer(fetchSizes[i] - 1)); + } + + maxRows.add(new Integer(fetchSizes[i])); + + if (i != fetchSizes.length - 1) { + maxRows.add(new Integer(fetchSizes[i] + 1)); + } + } + + for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) { + fetchStmt.setFetchSize(fetchSizes[fetchIndex]); + + for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) { + + int maxRowsToExpect = maxRows.get(maxRowIndex).intValue(); + fetchStmt.setMaxRows(maxRowsToExpect); + + int rowCount = 0; + + this.rs = fetchStmt.executeQuery("SELECT * FROM testBug25517"); + + while (this.rs.next()) { + rowCount++; + } + + assertEquals(maxRowsToExpect, rowCount); + } + } + + this.pstmt = fetchConn.prepareStatement("SELECT * FROM testBug25517"); + + for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) { + this.pstmt.setFetchSize(fetchSizes[fetchIndex]); + + for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) { + + int maxRowsToExpect = maxRows.get(maxRowIndex).intValue(); + this.pstmt.setMaxRows(maxRowsToExpect); + + int rowCount = 0; + + this.rs = this.pstmt.executeQuery(); + + while (this.rs.next()) { + rowCount++; + } + + assertEquals(maxRowsToExpect, rowCount); + } + } + + } finally { + if (fetchStmt != null) { + fetchStmt.close(); + } + + if (fetchConn != null) { + fetchConn.close(); + } + } + } + + /** + * Tests fix for BUG#25787 - java.util.Date should be serialized for + * PreparedStatement.setObject(). + * + * We add a new configuration option "treatUtilDateAsTimestamp", which is + * false by default, as (1) We already had specific behavior to treat + * java.util.Date as a java.sql.Timestamp because it's useful to many folks, + * and (2) that behavior will very likely be in JDBC-post-4.0 as a + * requirement. + * + * @throws Exception + * if the test fails. + */ + public void testBug25787() throws Exception { + createTable("testBug25787", "(MY_OBJECT_FIELD BLOB)"); + + Connection deserializeConn = null; + + Properties props = new Properties(); + props.setProperty("autoDeserialize", "true"); + props.setProperty("treatUtilDateAsTimestamp", "false"); + + deserializeConn = getConnectionWithProps(props); + + this.pstmt = deserializeConn.prepareStatement("INSERT INTO testBug25787 (MY_OBJECT_FIELD) VALUES (?)"); + java.util.Date dt = new java.util.Date(); + + this.pstmt.setObject(1, dt); + this.pstmt.execute(); + + this.rs = deserializeConn.createStatement().executeQuery("SELECT MY_OBJECT_FIELD FROM testBug25787"); + this.rs.next(); + assertEquals("java.util.Date", this.rs.getObject(1).getClass().getName()); + assertEquals(dt, this.rs.getObject(1)); + } + + public void testTruncationDisable() throws Exception { + Properties props = new Properties(); + props.setProperty("jdbcCompliantTruncation", "false"); + Connection truncConn = null; + + truncConn = getConnectionWithProps(props); + this.rs = truncConn.createStatement().executeQuery("SELECT " + Long.MAX_VALUE); + this.rs.next(); + this.rs.getInt(1); + + } + + public void testUsageAdvisorOnZeroRowResultSet() throws Exception { + Connection advisorConn = null; + Statement advisorStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + + advisorConn = getConnectionWithProps(props); + + advisorStmt = advisorConn.createStatement(); + + StandardLogger.startLoggingToBuffer(); + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + this.rs.next(); + this.rs.close(); + + advisorStmt.close(); + + advisorStmt = advisorConn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + advisorStmt.setFetchSize(Integer.MIN_VALUE); + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + this.rs.next(); + this.rs.close(); + + if (versionMeetsMinimum(5, 0, 2)) { + advisorConn.close(); + + props.setProperty("useCursorFetch", "true"); + props.setProperty("useServerPrepStmts", "true"); + + advisorConn = getConnectionWithProps(props); + + advisorStmt = advisorConn.createStatement(); + advisorStmt.setFetchSize(1); + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + StandardLogger.startLoggingToBuffer(); + this.rs.next(); + this.rs.close(); + } + + assertEquals(-1, StandardLogger.getBuffer().toString() + .indexOf(Messages.getString("ResultSet.Possible_incomplete_traversal_of_result_set").substring(0, 10))); + } finally { + StandardLogger.dropBuffer(); + + if (advisorStmt != null) { + advisorStmt.close(); + } + + if (advisorConn != null) { + advisorConn.close(); + } + } + } + + public void testBug25894() throws Exception { + createTable("bug25894", + "(tinyInt_type TINYINT DEFAULT 1, tinyIntU_type TINYINT UNSIGNED DEFAULT 1, smallInt_type SMALLINT DEFAULT 1," + + "smallIntU_type SMALLINT UNSIGNED DEFAULT 1, mediumInt_type MEDIUMINT DEFAULT 1, mediumIntU_type MEDIUMINT UNSIGNED DEFAULT 1," + + "int_type INT DEFAULT 1, intU_type INT UNSIGNED DEFAULT 1, bigInt_type BIGINT DEFAULT 1, bigIntU_type BIGINT UNSIGNED DEFAULT 1);"); + this.stmt.executeUpdate("INSERT INTO bug25894 VALUES (-1,1,-1,1,-1,1,-1,1,-1,1)"); + this.rs = this.stmt.executeQuery("SELECT * FROM bug25894"); + java.sql.ResultSetMetaData tblMD = this.rs.getMetaData(); + this.rs.first(); + for (int i = 1; i < tblMD.getColumnCount() + 1; i++) { + String typesName = ""; + switch (tblMD.getColumnType(i)) { + case Types.INTEGER: + typesName = "Types.INTEGER"; + break; + case Types.TINYINT: + typesName = "Types.TINYINT"; + break; + case Types.BIGINT: + typesName = "Types.BIGINT"; + break; + case Types.SMALLINT: + typesName = "Types.SMALLINT"; + break; + } + + System.out.println(i + " .fld: " + tblMD.getColumnName(i) + "T: " + typesName + ", MDC: " + tblMD.getColumnClassName(i) + " " + + tblMD.getColumnTypeName(i) + " , getObj: " + this.rs.getObject(i).getClass()); + } + + } + + /** + * Tests fix for BUG#26173 - fetching rows via cursor retrieves corrupted + * data. + * + * @throws Exception + * if the test fails. + */ + public void testBug26173() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testBug26173", "(fkey int, fdate date, fprice decimal(15, 2), fdiscount decimal(5,3))", "InnoDB"); + this.stmt.executeUpdate("insert into testBug26173 values (1, '2007-02-23', 99.9, 0.02)"); + + Connection fetchConn = null; + Statement stmtRead = null; + + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("useCursorFetch", "true"); + + try { + + fetchConn = getConnectionWithProps(props); + stmtRead = fetchConn.createStatement(); + stmtRead.setFetchSize(1000); + + this.rs = stmtRead.executeQuery("select extract(year from fdate) as fyear, fprice * (1 - fdiscount) as fvalue from testBug26173"); + + assertTrue(this.rs.next()); + assertEquals(2007, this.rs.getInt(1)); + assertEquals("97.90200", this.rs.getString(2)); + } finally { + if (stmtRead != null) { + stmtRead.close(); + } + + if (fetchConn != null) { + fetchConn.close(); + } + } + } + + /** + * Tests fix for BUG#26789 - fast date/time parsing doesn't take into + * account 00:00:00 as a legal value. + * + * @throws Exception + * if the test fails + */ + public void testBug26789() throws Exception { + this.rs = this.stmt.executeQuery("SELECT '00:00:00'"); + this.rs.next(); + this.rs.getTime(1); + assertEquals("00:00:00", this.rs.getTime(1).toString()); + assertEquals("1970-01-01 00:00:00.0", this.rs.getTimestamp(1).toString()); + assertEquals("1970-01-01", this.rs.getDate(1).toString()); + + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT '00/00/0000 00:00:00'"); + this.rs.next(); + + try { + this.rs.getTime(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + try { + this.rs.getTimestamp(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + try { + this.rs.getDate(1); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + } + + /** + * Tests fix for BUG#27317 - column index < 1 returns misleading error + * message. + * + * @throws Exception + * if the test fails. + */ + public void testBug27317() throws Exception { + this.rs = this.stmt.executeQuery("SELECT NULL"); + this.rs.next(); + String messageLowBound = null; + + Method[] getterMethods = ResultSet.class.getMethods(); + Integer zeroIndex = new Integer(0); + Integer twoIndex = new Integer(2); + + for (int i = 0; i < getterMethods.length; i++) { + Class[] parameterTypes = getterMethods[i].getParameterTypes(); + + if (getterMethods[i].getName().startsWith("get") && parameterTypes.length == 1 + && (parameterTypes[0].equals(Integer.TYPE) || parameterTypes[0].equals(Integer.class))) { + if (getterMethods[i].getName().equals("getRowId")) { + continue; // we don't support this yet, ever? + } + + try { + getterMethods[i].invoke(this.rs, new Object[] { zeroIndex }); + } catch (InvocationTargetException invokeEx) { + Throwable ex = invokeEx.getTargetException(); + + if (ex != null && ex instanceof SQLException) { + SQLException sqlEx = (SQLException) ex; + + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + + messageLowBound = sqlEx.getMessage(); + } else { + throw new RuntimeException(Util.stackTraceToString(ex), ex); + } + } + + String messageHighBound = null; + + try { + getterMethods[i].invoke(this.rs, new Object[] { twoIndex }); + } catch (InvocationTargetException invokeEx) { + Throwable ex = invokeEx.getTargetException(); + + if (ex != null && ex instanceof SQLException) { + SQLException sqlEx = (SQLException) ex; + + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + + messageHighBound = sqlEx.getMessage(); + } else { + throw new RuntimeException(ex); + } + } + + assertNotNull("Exception message null for method " + getterMethods[i], messageHighBound); + assertNotNull("Exception message null for method " + getterMethods[i], messageLowBound); + + assertTrue(!messageHighBound.equals(messageLowBound)); + } + } + } + + /** + * Tests fix for BUG#28085 - Need more useful error messages for diagnostics + * when the driver thinks a result set isn't updatable. + * + * @throws Exception + * if the tests fail. + */ + public void testBug28085() throws Exception { + + Statement updStmt = null; + + try { + createTable("testBug28085_oneKey", "(pk int primary key not null, field2 varchar(3))"); + + this.stmt.executeUpdate("INSERT INTO testBug28085_oneKey (pk, field2) VALUES (1, 'abc')"); + + createTable("testBug28085_multiKey", "(pk1 int not null, pk2 int not null, field2 varchar(3), primary key (pk1, pk2))"); + + this.stmt.executeUpdate("INSERT INTO testBug28085_multiKey VALUES (1,2,'abc')"); + + createTable("testBug28085_noKey", "(field1 varchar(3) not null)"); + + this.stmt.executeUpdate("INSERT INTO testBug28085_noKey VALUES ('abc')"); + + updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + + this.rs = updStmt.executeQuery("SELECT field2 FROM testBug28085_oneKey"); + exerciseUpdatableResultSet(1, "NotUpdatableReason.4"); + + this.rs = updStmt.executeQuery("SELECT pk1, field2 FROM testBug28085_multiKey"); + this.rs.next(); + exerciseUpdatableResultSet(1, "NotUpdatableReason.7"); + + this.rs = updStmt.executeQuery("SELECT t1.field2, t1.pk, t2.pk1 FROM testBug28085_oneKey t1 INNER JOIN testBug28085_multiKey t2 ON t1.pk = t2.pk1"); + exerciseUpdatableResultSet(1, "NotUpdatableReason.0"); + + this.rs = updStmt.executeQuery("SELECT field1 FROM testBug28085_noKey"); + exerciseUpdatableResultSet(1, "NotUpdatableReason.5"); + + this.rs = updStmt.executeQuery("SELECT 1"); + exerciseUpdatableResultSet(1, "NotUpdatableReason.3"); + + this.rs = updStmt.executeQuery("SELECT pk1, pk2, LEFT(field2, 2) FROM testBug28085_multiKey"); + this.rs.next(); + exerciseUpdatableResultSet(1, "NotUpdatableReason.3"); + } finally { + if (updStmt != null) { + updStmt.close(); + } + } + } + + private void exerciseUpdatableResultSet(int columnUpdateIndex, String messageToCheck) throws Exception { + this.rs.next(); + + try { + this.rs.updateString(columnUpdateIndex, "def"); + } catch (SQLException sqlEx) { + checkUpdatabilityMessage(sqlEx, messageToCheck); + } + + try { + this.rs.moveToInsertRow(); + } catch (SQLException sqlEx) { + checkUpdatabilityMessage(sqlEx, messageToCheck); + } + + try { + this.rs.deleteRow(); + } catch (SQLException sqlEx) { + checkUpdatabilityMessage(sqlEx, messageToCheck); + } + + this.rs.close(); + } + + private void checkUpdatabilityMessage(SQLException sqlEx, String messageToCheck) throws Exception { + + String message = sqlEx.getMessage(); + + assertNotNull(message); + + String localizedMessage = Messages.getString(messageToCheck); + + assertTrue("Didn't find required message component '" + localizedMessage + "', instead found:\n\n" + message, message.indexOf(localizedMessage) != -1); + } + + public void testBug24886() throws Exception { + Properties props = new Properties(); + props.setProperty("blobsAreStrings", "true"); + + Connection noBlobConn = getConnectionWithProps(props); + + createTable("testBug24886", "(sepallength double, sepalwidth double, petallength double, petalwidth double, Class mediumtext, fy TIMESTAMP)"); + + noBlobConn.createStatement().executeUpdate("INSERT INTO testBug24886 VALUES (1,2,3,4,'1234', now()),(5,6,7,8,'12345678', now())"); + this.rs = noBlobConn.createStatement() + .executeQuery("SELECT concat(Class,petallength), COUNT(*) FROM `testBug24886` GROUP BY `concat(Class,petallength)`"); + this.rs.next(); + assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName()); + + props.clear(); + props.setProperty("functionsNeverReturnBlobs", "true"); + noBlobConn = getConnectionWithProps(props); + this.rs = noBlobConn.createStatement() + .executeQuery("SELECT concat(Class,petallength), COUNT(*) FROM `testBug24886` GROUP BY `concat(Class,petallength)`"); + this.rs.next(); + + if (versionMeetsMinimum(4, 1)) { + assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName()); + + } + } + + /** + * Tests fix for BUG#30664. Note that this fix only works for MySQL server + * 5.0.25 and newer, since earlier versions didn't consistently return + * correct metadata for functions, and thus results from subqueries and + * functions were indistinguishable from each other, leading to type-related + * bugs. + * + * @throws Exception + */ + public void testBug30664() throws Exception { + if (!versionMeetsMinimum(5, 0, 25)) { + return; + } + + createTable("testBug30664_1", "(id int)"); + createTable("testBug30664_2", "(id int, binaryvalue varbinary(255))"); + + this.stmt.executeUpdate("insert into testBug30664_1 values (1),(2),(3)"); + this.stmt.executeUpdate("insert into testBug30664_2 values (1,'���'),(2,'����'),(3,' ���')"); + this.rs = this.stmt.executeQuery("select testBug30664_1.id, (select testBug30664_2.binaryvalue from testBug30664_2 " + + "where testBug30664_2.id=testBug30664_1.id) as value from testBug30664_1"); + ResultSetMetaData tblMD = this.rs.getMetaData(); + + for (int i = 1; i < tblMD.getColumnCount() + 1; i++) { + switch (i) { + case 1: + assertEquals("INT", tblMD.getColumnTypeName(i).toUpperCase()); + break; + case 2: + assertEquals("VARBINARY", tblMD.getColumnTypeName(i).toUpperCase()); + break; + } + } + } + + /** + * Tests fix for BUG#30851, NPE with null column values when + * "padCharsWithSpace" is set to "true". + * + * @throws Exception + */ + public void testbug30851() throws Exception { + Connection padConn = getConnectionWithProps("padCharsWithSpace=true"); + + try { + createTable("bug30851", "(CharCol CHAR(10) DEFAULT NULL)"); + this.stmt.execute("INSERT INTO bug30851 VALUES (NULL)"); + this.rs = padConn.createStatement().executeQuery("SELECT * FROM bug30851"); + this.rs.first(); + String strvar = this.rs.getString(1); + assertNull("Should be null", strvar); + + } finally { + if (padConn != null) { + padConn.close(); + } + } + } + + /** + * Tests fix for Bug#33678 - Multiple result sets not supported in + * "streaming" mode. This fix covers both normal statements, and stored + * procedures, with the exception of stored procedures with registered + * OUTPUT parameters, which can't be used at all with "streaming" result + * sets. + * + * @throws Exception + */ + public void testBug33678() throws Exception { + if (!versionMeetsMinimum(4, 1)) { + return; + } + + createTable("testBug33678", "(field1 INT)"); + + Connection multiConn = getConnectionWithProps("allowMultiQueries=true"); + Statement multiStmt = multiConn.createStatement(); + + try { + multiStmt.setFetchSize(Integer.MIN_VALUE); + + multiStmt.execute("SELECT 1 UNION SELECT 2; INSERT INTO testBug33678 VALUES (1); UPDATE testBug33678 set field1=2; " + + "INSERT INTO testBug33678 VALUES(3); UPDATE testBug33678 set field1=2 WHERE field1=3; UPDATE testBug33678 set field1=2; SELECT 1"); + this.rs = multiStmt.getResultSet(); + this.rs.next(); + assertEquals("1", this.rs.getString(1)); + + assertFalse(multiStmt.getMoreResults()); + assertEquals(1, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(1, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(1, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(1, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(2, multiStmt.getUpdateCount()); + assertTrue(multiStmt.getMoreResults()); + this.rs = multiStmt.getResultSet(); + this.rs.next(); + assertEquals("1", this.rs.getString(1)); + + this.rs.close(); + + multiStmt.execute("INSERT INTO testBug33678 VALUES (1); INSERT INTO testBug33678 VALUES (1), (2); INSERT INTO testBug33678 VALUES (1), (2), (3)"); + + assertEquals(1, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(2, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults()); + assertEquals(3, multiStmt.getUpdateCount()); + assertFalse(multiStmt.getMoreResults() && multiStmt.getUpdateCount() == -1); + + this.rs.close(); + + if (versionMeetsMinimum(5, 0)) { + createProcedure("spBug33678", "() BEGIN SELECT 1; SELECT 2; SELECT 3; END"); + + CallableStatement cStmt = multiConn.prepareCall("{CALL spBug33678()}"); + cStmt.setFetchSize(Integer.MIN_VALUE); + cStmt.execute(); + + for (int i = 0; i < 2; i++) { + if (i != 0) { + assertTrue(cStmt.getMoreResults()); + } + + this.rs = cStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(i + 1, this.rs.getInt(1)); + } + } + } finally { + multiStmt.close(); + multiConn.close(); + } + } + + public void testBug33162() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + this.rs = this.stmt.executeQuery("select now() from dual where 1=0"); + this.rs.next(); + try { + this.rs.getTimestamp(1); // fails + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_GENERAL_ERROR, sqlEx.getSQLState()); + } + } + + public void testBug34762() throws Exception { + createTable("testBug34762", "(field1 TIMESTAMP)"); + int numRows = 10; + + for (int i = 0; i < numRows; i++) { + this.stmt.executeUpdate("INSERT INTO testBug34762 VALUES (NOW())"); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34762"); + + while (this.rs.next()) { + this.rs.getTimestamp(1); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34762"); + + for (int i = 1; i <= numRows; i++) { + this.rs.absolute(i); + this.rs.getTimestamp(1); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34762"); + + this.rs.last(); + this.rs.getTimestamp(1); + + while (this.rs.previous()) { + this.rs.getTimestamp(1); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34762"); + + this.rs.last(); + + while (this.rs.relative(-1)) { + this.rs.getTimestamp(1); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug34762"); + + this.rs.beforeFirst(); + + while (this.rs.relative(1)) { + this.rs.getTimestamp(1); + } + } + + /** + * @deprecated because we use deprecated methods + */ + @Deprecated + public void testBug34913() throws Exception { + Timestamp ts = new Timestamp(new Date(109, 5, 1).getTime()); + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT 'abcdefghij', ?"); + this.pstmt.setTimestamp(1, ts); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + assertTrue(this.rs.getTimestamp(2).getMonth() == 5); + assertTrue(this.rs.getTimestamp(2).getDate() == 1); + } + + public void testBug36051() throws Exception { + this.rs = this.stmt.executeQuery("SELECT '24:00:00'"); + this.rs.next(); + this.rs.getTime(1); + } + + /** + * Tests fix for BUG#35610, BUG#35150. We follow the JDBC Spec here, in that + * the 4.0 behavior is correct, the JDBC-3.0 (and earlier) spec has a bug, + * but you can get the buggy behavior (allowing column names *and* labels to + * be used) by setting "useColumnNamesInFindColumn" to "true". + * + * @throws Exception + */ + public void testBug35610() throws Exception { + createTable("testBug35610", "(field1 int, field2 int, field3 int)"); + this.stmt.executeUpdate("INSERT INTO testBug35610 VALUES (1, 2, 3)"); + exercise35610(this.stmt, false); + exercise35610(getConnectionWithProps("useColumnNamesInFindColumn=true").createStatement(), true); + } + + private void exercise35610(Statement configuredStmt, boolean force30Behavior) throws Exception { + this.rs = configuredStmt.executeQuery("SELECT field1 AS f1, field2 AS f2, field3 FROM testBug35610"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertEquals("field1", rsmd.getColumnName(1)); + assertEquals("field2", rsmd.getColumnName(2)); + assertEquals("f1", rsmd.getColumnLabel(1)); + assertEquals("f2", rsmd.getColumnLabel(2)); + + assertEquals("field3", rsmd.getColumnName(3)); + assertEquals("field3", rsmd.getColumnLabel(3)); + + this.rs.next(); + + // From ResultSet.html#getInt(java.lang.String) in JDBC-4.0 + // + // Retrieves the value of the designated column in the current row of + // this ResultSet + // object as an int in the Java programming language. + // + // Parameters: + // columnLabel - the label for the column specified with the SQL AS + // clause. If the + // SQL AS clause was not specified, then the label is the name of the + // column + // + + assertEquals(1, this.rs.getInt("f1")); + assertEquals(2, this.rs.getInt("f2")); + assertEquals(3, this.rs.getInt("field3")); + + // Pre-JDBC 4.0, some versions of the spec say "column name *or* label" + // for the column name argument... + + if (force30Behavior) { + assertEquals(1, this.rs.getInt("field1")); + assertEquals(2, this.rs.getInt("field2")); + } + + if (!force30Behavior) { + try { + this.rs.findColumn("field1"); + fail("findColumn(\"field1\" should have failed with an exception"); + } catch (SQLException sqlEx) { + // expected + } + + try { + this.rs.findColumn("field2"); + fail("findColumn(\"field2\" should have failed with an exception"); + } catch (SQLException sqlEx) { + // expected + } + } + } + + /** + * Tests fix for BUG#39911 - We don't retrieve nanos correctly when + * -parsing- a string for a TIMESTAMP. + */ + public void testBug39911() throws Exception { + this.rs = this.stmt.executeQuery("SELECT '2008-09-26 15:47:20.797283'"); + this.rs.next(); + + checkTimestampNanos(); + + this.rs = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("SELECT '2008-09-26 15:47:20.797283'").executeQuery(); + this.rs.next(); + + checkTimestampNanos(); + + this.rs.close(); + } + + private void checkTimestampNanos() throws SQLException { + Timestamp ts = this.rs.getTimestamp(1); + assertEquals(797283000, ts.getNanos()); + Calendar cal = Calendar.getInstance(); + cal.setTime(ts); + assertEquals(797, cal.get(Calendar.MILLISECOND)); + } + + public void testBug38387() throws Exception { + Connection noBlobConn = null; + Properties props = new Properties(); + props.put("functionsNeverReturnBlobs", "true");// toggle, no change + noBlobConn = getConnectionWithProps(props); + try { + Statement noBlobStmt = noBlobConn.createStatement(); + this.rs = noBlobStmt.executeQuery("SELECT TRIM(1) AS Rslt"); + while (this.rs.next()) { + assertEquals("1", this.rs.getString("Rslt")); + assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName()); + } + } finally { + noBlobConn.close(); + } + + } + + public void testRanges() throws Exception { + createTable("testRanges", "(int_field INT, long_field BIGINT, double_field DOUBLE, string_field VARCHAR(32))"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testRanges VALUES (?,?,?, ?)"); + this.pstmt.setInt(1, Integer.MIN_VALUE); + this.pstmt.setLong(2, Long.MIN_VALUE); + this.pstmt.setDouble(3, Long.MAX_VALUE + 1D); + this.pstmt.setString(4, "1E4"); + + this.pstmt.executeUpdate(); + + checkRangeMatrix(this.conn); + checkRangeMatrix(getConnectionWithProps("useFastIntParsing=false")); + } + + private void checkRangeMatrix(Connection c) throws Exception { + this.rs = c.createStatement().executeQuery("SELECT int_field, long_field, double_field, string_field FROM testRanges"); + this.rs.next(); + checkRanges(); + this.rs.close(); + + this.pstmt = ((com.mysql.jdbc.Connection) c).serverPrepareStatement("SELECT int_field, long_field, double_field, string_field FROM testRanges"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + checkRanges(); + this.rs.close(); + + this.pstmt.setFetchSize(Integer.MIN_VALUE); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + checkRanges(); + this.rs.close(); + + this.pstmt = ((com.mysql.jdbc.Connection) c).clientPrepareStatement("SELECT int_field, long_field, double_field, string_field FROM testRanges"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + checkRanges(); + this.rs.close(); + + this.pstmt.setFetchSize(Integer.MIN_VALUE); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + checkRanges(); + this.rs.close(); + } + + private void checkRanges() throws SQLException { + assertEquals(Integer.MIN_VALUE, this.rs.getInt(1)); + + try { + this.rs.getInt(2); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf(" in column '2'") != -1); + } + + assertEquals(Long.MIN_VALUE, this.rs.getLong(2)); + + try { + this.rs.getLong(3); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf(" in column '3'") != -1); + } + + assertEquals(10000, this.rs.getInt(4)); + assertEquals(10000, this.rs.getLong(4)); + } + + /** + * Bug #41484 Accessing fields by name after the ResultSet is closed throws + * NullPointerException. + */ + public void testBug41484() throws Exception { + try { + this.rs = this.stmt.executeQuery("select 1 as abc"); + this.rs.next(); + this.rs.getString("abc"); + this.rs.close(); + this.rs.getString("abc"); + } catch (SQLException ex) { + /* expected */ + assertEquals(0, ex.getErrorCode()); + assertEquals("S1000", ex.getSQLState()); + } + } + + public void testBug41484_2() throws Exception { + Connection cachedRsmdConn = getConnectionWithProps("cacheResultSetMetadata=true"); + + try { + createTable("bug41484", "(id int not null primary key, day date not null) DEFAULT CHARSET=utf8"); + this.pstmt = cachedRsmdConn.prepareStatement("INSERT INTO bug41484(id, day) values(1, ?)"); + this.pstmt.setInt(1, 20080509); + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.close(); + + this.pstmt = cachedRsmdConn.prepareStatement("SELECT * FROM bug41484 WHERE id = ?"); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + this.rs.first(); + this.rs.getString("day"); + this.rs.close(); + this.pstmt.close(); + + this.pstmt = cachedRsmdConn.prepareStatement("INSERT INTO bug41484(id, day) values(2, ?)"); + this.pstmt.setInt(1, 20090212); + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.close(); + + this.pstmt = cachedRsmdConn.prepareStatement("SELECT * FROM bug41484 WHERE id = ?"); + this.pstmt.setInt(1, 2); + this.rs = this.pstmt.executeQuery(); + this.rs.first(); + assertEquals(this.rs.getString(1), "2"); + this.rs.getString("day"); + this.rs.close(); + + this.pstmt.close(); + } finally { + cachedRsmdConn.close(); + } + } + + public void testBug27431() throws Exception { + createTable("bug27431", "(`ID` int(20) NOT NULL auto_increment, `Name` varchar(255) NOT NULL default '', PRIMARY KEY (`ID`))"); + + this.stmt.executeUpdate("INSERT INTO bug27431 (`ID`, `Name`) VALUES (1, 'Lucho'),(2, 'Lily'),(3, 'Kiro')"); + + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery("SELECT ID, Name FROM bug27431"); + + while (this.rs.next()) { + this.rs.deleteRow(); + } + + assertEquals(0, getRowCount("bug27431")); + } + + public void testBug43759() throws Exception { + createTable("testtable_bincolumn", "(bincolumn binary(8) NOT NULL, PRIMARY KEY (bincolumn))", "innodb"); + + String pkValue1 = "0123456789ABCD90"; + String pkValue2 = "0123456789ABCD00"; + // put some data in it + this.stmt.executeUpdate("INSERT INTO testtable_bincolumn (bincolumn) VALUES (unhex('" + pkValue1 + "')), (unhex('" + pkValue2 + "'))"); + + // cause the bug + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery("SELECT * FROM testtable_bincolumn WHERE bincolumn = unhex('" + pkValue1 + "')"); + assertTrue(this.rs.next()); + this.rs.deleteRow(); + + // At this point the row with pkValue1 should be deleted. We'll select + // it back to see. + // If the row comes back, the testcase has failed. + + this.rs = this.stmt.executeQuery("SELECT * FROM testtable_bincolumn WHERE bincolumn = unhex('" + pkValue1 + "')"); + assertFalse(this.rs.next()); + + // Now, show a case where it happens to work, because the binary data is + // different + updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery("SELECT * FROM testtable_bincolumn WHERE bincolumn = unhex('" + pkValue2 + "')"); + assertTrue(this.rs.next()); + this.rs.deleteRow(); + + this.rs = this.stmt.executeQuery("SELECT * FROM testtable_bincolumn WHERE bincolumn = unhex('" + pkValue2 + "')"); + assertFalse(this.rs.next()); + } + + public void testBug32525() throws Exception { + Connection testConn = this.conn; + Connection noStringSyncConn = getConnectionWithProps("noDatetimeStringSync=true"); + try { + if (versionMeetsMinimum(5, 7, 4)) { + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "false"); + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + testConn = getConnectionWithProps(props); + this.stmt = testConn.createStatement(); + } + + createTable("bug32525", "(field1 date, field2 timestamp)"); + this.stmt.executeUpdate("INSERT INTO bug32525 VALUES ('0000-00-00', '0000-00-00 00:00:00')"); + + this.rs = ((com.mysql.jdbc.Connection) noStringSyncConn).serverPrepareStatement("SELECT field1, field2 FROM bug32525").executeQuery(); + this.rs.next(); + assertEquals("0000-00-00", this.rs.getString(1)); + assertEquals("0000-00-00 00:00:00", this.rs.getString(2)); + } finally { + noStringSyncConn.close(); + if (testConn != this.conn) { + testConn.close(); + } + } + } + + public void testBug49797() throws Exception { + createTable("testBug49797", "(`Id` int(2) not null auto_increment, `abc` char(50) , PRIMARY KEY (`Id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8"); + this.stmt.executeUpdate("INSERT into testBug49797 VALUES (1,'1'),(2,'2'),(3,'3')"); + assertEquals(3, getRowCount("testBug49797")); + + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + try { + this.rs = updStmt.executeQuery("SELECT * FROM testBug49797"); + while (this.rs.next()) { + this.rs.deleteRow(); + } + assertEquals(0, getRowCount("testBug49797")); + } finally { + updStmt.close(); + } + } + + public void testBug49516() throws Exception { + + CachedRowSet crs; + + createTable("bug49516", "(`testingID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `firstName` TEXT NOT NULL) CHARACTER SET utf8;"); + this.stmt.executeUpdate("insert into bug49516 set firstName ='John'"); + + this.rs = this.stmt.executeQuery("select firstName as 'first person' from bug49516"); + this.rs.first(); + assertEquals("John", this.rs.getString("first person")); + // this.rs.close(); + // this.stmt.close(); + + this.rs = this.stmt.executeQuery("select firstName as 'first person' from bug49516"); + + crs = (CachedRowSet) Class.forName("com.sun.rowset.CachedRowSetImpl").newInstance(); + crs.populate(this.rs); + crs.first(); + + assertEquals("John", crs.getString(1)); + } + + public void testBug48820() throws Exception { + + CachedRowSet crs; + + Connection noBlobsConn = getConnectionWithProps("functionsNeverReturnBlobs=true"); + + if (versionMeetsMinimum(5, 6, 6)) { + this.rs = noBlobsConn.createStatement().executeQuery("SHOW VARIABLES LIKE 'old_passwords'"); + if (this.rs.next()) { + if (this.rs.getInt(2) == 2) { + System.out.println("Skip testBug48820 due to SHA-256 password hashing."); + return; + } + } + } + + this.rs = noBlobsConn.createStatement().executeQuery("SELECT PASSWORD ('SOMETHING')"); + this.rs.first(); + + String fromPlainResultSet = this.rs.getString(1); + + this.rs = noBlobsConn.createStatement().executeQuery("SELECT PASSWORD ('SOMETHING')"); + + crs = (CachedRowSet) Class.forName("com.sun.rowset.CachedRowSetImpl").newInstance(); + crs.populate(this.rs); + crs.first(); + + assertEquals(fromPlainResultSet, crs.getString(1)); + } + + /** + * Bug #60313 bug in com.mysql.jdbc.ResultSetRow.getTimestampFast + */ + public void testBug60313() throws Exception { + this.stmt.execute("select repeat('Z', 3000), now() + interval 1 microsecond"); + this.rs = this.stmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(1000, this.rs.getTimestamp(2).getNanos()); + this.rs.close(); + + this.pstmt = this.conn.prepareStatement("select repeat('Z', 3000), now() + interval 1 microsecond"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals(1000, this.rs.getTimestamp(2).getNanos()); + this.rs.close(); + + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + Connection sspsCon = getConnectionWithProps(props); + PreparedStatement ssPStmt = sspsCon.prepareStatement("select repeat('Z', 3000), now() + interval 1 microsecond"); + this.rs = ssPStmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals(1000, this.rs.getTimestamp(2).getNanos()); + this.rs.close(); + ssPStmt.close(); + sspsCon.close(); + } + + /** + * Tests fix for BUG#65503 - ResultSets created by PreparedStatement.getGeneratedKeys() are not close()d. + * + * To get results quicker add option -Xmx10M, with this option I got an out of memory failure after about 6500 passes. + * Since it's a very long test it is disabled by default. + * + * @throws Exception + * if the test fails. + */ + public void testBug65503() throws Exception { + if (!this.DISABLED_testBug65503) { + createTable("testBug65503", "(id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, value INTEGER)"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug65503 (value) VALUES (?)", Statement.RETURN_GENERATED_KEYS), + stmt2 = this.conn.prepareStatement("SELECT * FROM testBug65503 LIMIT 6"); + for (int i = 0; i < 100000000; ++i) { + pStmt.setString(1, "48"); + pStmt.executeUpdate(); + + ResultSet result = pStmt.getGeneratedKeys(); + result.next(); + result.getInt(1); + result.next(); + + result = stmt2.executeQuery(); + while (result.next()) { + } + + if (i % 500 == 0) { + System.out.printf("free-mem: %d, id: %d\n", Runtime.getRuntime().freeMemory() / 1024 / 1024, i); + this.conn.createStatement().execute("TRUNCATE TABLE testBug65503"); + } + } + } + } + + /** + * Tests fix for BUG#64204 - ResultSet.close hangs if streaming query is killed + * + * @throws Exception + */ + public void testBug64204() throws Exception { + final Properties props = new Properties(); + props.setProperty("socketTimeout", "30000"); + + this.conn = getConnectionWithProps(props); + this.conn.setCatalog("information_schema"); + this.conn.setAutoCommit(true); + + this.stmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + this.stmt.setFetchSize(Integer.MIN_VALUE); // turn on streaming mode + + this.rs = this.stmt.executeQuery("SELECT CONNECTION_ID()"); + this.rs.next(); + final String connectionId = this.rs.getString(1); + this.rs.close(); + + System.out.println("testBug64204.main: PID is " + connectionId); + + ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); + es.schedule(new Callable() { + + public Boolean call() throws Exception { + boolean res = false; + Connection con2 = getConnectionWithProps(props); + con2.setCatalog("information_schema"); + con2.setAutoCommit(true); + + Statement st2 = con2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + st2.setFetchSize(Integer.MIN_VALUE); // turn on streaming mode + try { + System.out.println("testBug64204.slave: Running KILL QUERY " + connectionId); + st2.execute("KILL QUERY " + connectionId + ";"); + + Thread.sleep(5000); + System.out.println("testBug64204.slave: parent thread should be hung now!!!"); + res = true; + } finally { + st2.close(); + con2.close(); + } + + System.out.println("testBug64204.slave: Done."); + return res; + } + }, 10, TimeUnit.SECONDS); + + try { + this.rs = this.stmt.executeQuery("SELECT sleep(5) FROM character_sets LIMIT 10"); + + int rows = 0; + int columnCount = this.rs.getMetaData().getColumnCount(); + System.out.println("testBug64204.main: fetched result set, " + columnCount + " columns"); + + long totalDataCount = 0; + while (this.rs.next()) { + rows++; + //get row size + long rowSize = 0; + for (int i = 0; i < columnCount; i++) { + String s = this.rs.getString(i + 1); + if (s != null) { + rowSize += s.length(); + } + } + totalDataCount += rowSize; + } + + System.out.println("testBug64204.main: character_sets total rows " + rows + ", data " + totalDataCount); + + } catch (SQLException se) { + assertEquals("ER_QUERY_INTERRUPTED expected.", "70100", se.getSQLState()); + if (!"70100".equals(se.getSQLState())) { + throw se; + } + } + } + + /** + * Bug #45757 - ResultSet.updateRow should throw SQLException when cursor is on insert row + */ + public void testBug45757() throws SQLException { + createTable("bug45757", "(id INTEGER NOT NULL PRIMARY KEY)"); + this.stmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = this.stmt.executeQuery("select id from bug45757"); + this.rs.moveToInsertRow(); + try { + this.rs.updateRow(); + fail("updateRow() should throw an exception, not allowed to be called on insert row"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().startsWith("Can not call updateRow() when on insert row.")); + } + } + + /** + * Tests fix for BUG#38252 - ResultSet.absolute(0) is not behaving according to JDBC specification. + * + * @throws Exception + * if the test fails. + */ + public void testBug38252() throws Exception { + createTable("testBug38252", "(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY)"); + + this.stmt = this.conn.createStatement(); + this.stmt.executeUpdate("INSERT INTO testBug38252 VALUES (NULL), (NULL)"); + + this.rs = this.stmt.executeQuery("SELECT * FROM testBug38252"); + + // test ResultSet.absolute(0) before iterating the ResultSet + assertFalse("Cursor should be moved to before the first row.", this.rs.absolute(0)); + assertTrue("ResultSet's cursor should be at 'before first'.", this.rs.isBeforeFirst()); + assertTrue("First row expected from ResultSet.", this.rs.next()); + assertTrue("Second row expected from ResultSet.", this.rs.next()); + assertFalse("No more rows expected from ResultSet.", this.rs.next()); + assertTrue("ResultSet's cursor should be at 'after last'.", this.rs.isAfterLast()); + + // test ResultSet.absolute(0) after iterating the ResultSet + assertFalse("Cursor should be moved to before the first row.", this.rs.absolute(0)); + assertTrue("ResultSet's cursor should be at 'before first'.", this.rs.isBeforeFirst()); + assertTrue("First row expected from ResultSet.", this.rs.next()); + assertTrue("Second row expected from ResultSet.", this.rs.next()); + assertFalse("No more rows expected from ResultSet.", this.rs.next()); + assertTrue("ResultSet's cursor should be at 'after last'.", this.rs.isAfterLast()); + + this.rs.close(); + this.stmt.close(); + + // test ResultSet.absolute(0) with an empty ResultSet + this.stmt = this.conn.createStatement(); + this.rs = this.stmt.executeQuery("SELECT * FROM testBug38252 where 0 = 1"); + assertFalse("Cursor should be moved to before the first row.", this.rs.absolute(0)); + } + + /** + * Tests fix for Bug#67318 - SQLException thrown on already closed ResultSet + * + * @throws Exception + * if the test fails. + */ + public void testBug67318() throws Exception { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("exceptionInterceptors", "testsuite.regression.ResultSetRegressionTest$TestBug67318ExceptionInterceptor"); + + Connection c = null; + try { + c = getConnectionWithProps(props); + ExceptionInterceptorChain eic = (ExceptionInterceptorChain) ((MySQLConnection) c).getExceptionInterceptor(); + + TestBug67318ExceptionInterceptor ei = null; + for (Extension ext : eic.getInterceptors()) { + if (ext instanceof TestBug67318ExceptionInterceptor) { + ei = (TestBug67318ExceptionInterceptor) ext; + break; + } + } + + if (ei == null) { + fail("TestBug67318ExceptionInterceptor is not found on connection"); + } + + Statement st1 = c.createStatement(); + ResultSet rs1 = st1.executeQuery("select 1"); + rs1.close(); + rs1.close(); + assertEquals("Operation not allowed after ResultSet closed exception shouldn't be thrown second time", 0, ei.alreadyClosedCounter); + st1.close(); + st1.close(); + ((StatementImpl) st1).isClosed(); + assertEquals("No operations allowed after statement closed exception shouldn't be thrown second time", 0, ei.alreadyClosedCounter); + + PreparedStatement ps1 = c.prepareStatement("select 1"); + ps1.close(); + ps1.close(); + assertEquals("No operations allowed after statement closed exception shouldn't be thrown second time", 0, ei.alreadyClosedCounter); + + } finally { + if (c != null) { + c.close(); + } + } + + } + + public static class TestBug67318ExceptionInterceptor implements ExceptionInterceptor { + + public int alreadyClosedCounter = 0; + + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, com.mysql.jdbc.Connection conn) { + + sqlEx.printStackTrace(); + + if ("Operation not allowed after ResultSet closed".equals(sqlEx.getMessage()) + || "No operations allowed after statement closed.".equals(sqlEx.getMessage())) { + this.alreadyClosedCounter++; + } + return sqlEx; + } + + } + + /** + * Tests fix for BUG#72000 - java.lang.ArrayIndexOutOfBoundsException on java.sql.ResultSet.getInt(String). + * + * @throws Exception + * if the test fails. + */ + public void testBug72000() throws Exception { + final ResultSet testRS = this.stmt.executeQuery("SELECT ' '"); + + assertTrue(testRS.next()); + + assertThrows(SQLException.class, "Bad format for BigDecimal ' ' in column 1.", new Callable() { + public Void call() throws Exception { + testRS.getBigDecimal(1); + return null; + } + }); + assertFalse(testRS.getBoolean(1)); + assertThrows(SQLException.class, "Value '' is out of range \\[-127,127\\]", new Callable() { + public Void call() throws Exception { + testRS.getByte(1); + return null; + } + }); + assertThrows(SQLException.class, "Value ' ' can not be represented as java.sql.Date", new Callable() { + public Void call() throws Exception { + testRS.getDate(1); + return null; + } + }); + assertThrows(SQLException.class, "Bad format for number ' ' in column 1.", new Callable() { + public Void call() throws Exception { + testRS.getDouble(1); + return null; + } + }); + assertThrows(SQLException.class, "Invalid value for getFloat\\(\\) - ' ' in column 1", new Callable() { + public Void call() throws Exception { + testRS.getFloat(1); + return null; + } + }); + assertThrows(SQLException.class, "Invalid value for getInt\\(\\) - ' '", new Callable() { + public Void call() throws Exception { + testRS.getInt(1); + return null; + } + }); + assertThrows(SQLException.class, "Invalid value for getLong\\(\\) - ' '", new Callable() { + public Void call() throws Exception { + testRS.getLong(1); + return null; + } + }); + assertThrows(SQLException.class, "Invalid value for getShort\\(\\) - ' '", new Callable() { + public Void call() throws Exception { + testRS.getShort(1); + return null; + } + }); + assertThrows(SQLException.class, "Value ' ' can not be represented as java.sql.Time", new Callable() { + public Void call() throws Exception { + testRS.getTime(1); + return null; + } + }); + assertThrows(SQLException.class, "Value ' ' can not be represented as java.sql.Timestamp", new Callable() { + public Void call() throws Exception { + testRS.getTimestamp(1); + return null; + } + }); + } + + /** + * Tests fix for BUG#72023 - Avoid byte array creation in MysqlIO#unpackBinaryResultSetRow. + * + * @throws Exception + * if the test fails. + */ + public void testBug72023() throws Exception { + // null bitmask contains 2 reserved bits plus 1 bit per field + // + // boundary cases at 8n - 2 / 8n - 1 field count; e.g. 6/7, 14/15 + String[] selectList = new String[] { "NULL", "1", "NULL,NULL,NULL,NULL,NULL,NULL", "1,NULL,NULL,1,1,NULL", "1,1,1,1,1,1", + "NULL,NULL,NULL,NULL,NULL,NULL,NULL", "1,1,1,NULL,1,NULL,NULL", "1,1,1,1,1,1,1", + "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL", "NULL,NULL,NULL,1,NULL,1,NULL,NULL,1,NULL,1,1,NULL,NULL", + "1,1,1,1,1,1,1,1,1,1,1,1,1,1", "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL", + "NULL,1,NULL,1,NULL,NULL,1,NULL,1,NULL,NULL,NULL,NULL,1,1", "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1" }; + + Connection testConn = getConnectionWithProps("useServerPrepStmts=true"); + PreparedStatement testPstmt; + ResultSet testRS; + + for (int i = 0, s = selectList.length; i < s; i++) { + String sl = selectList[i]; + testPstmt = testConn.prepareStatement("SELECT " + sl); + testRS = testPstmt.executeQuery(); + assertTrue(testRS.next()); + int j = 1; + for (String fld : sl.split(",")) { + if (fld.equals("NULL")) { + assertNull("Bad results for query " + i + ", field " + j, testRS.getObject(j)); + } else { + assertEquals("Bad results for query " + i + ", field " + j, 1, testRS.getInt(j)); + } + j++; + } + assertFalse(testRS.next()); + testRS.close(); + testPstmt.close(); + } + testConn.close(); + } + + /** + * Tests fix for BUG#75309 - mysql connector/J driver in streaming mode will in the blocking state. + * + * @throws Exception + * if the test fails. + */ + public void testBug75309() throws Exception { + if (!versionMeetsMinimum(5, 5)) { + return; + } + + Connection testConn = getConnectionWithProps("socketTimeout=1000"); + Statement testStmt = testConn.createStatement(); + + // turn on streaming results. + testStmt.setFetchSize(Integer.MIN_VALUE); + + final ResultSet testRs1 = testStmt.executeQuery("SELECT 1 + 18446744073709551615"); + + assertThrows(SQLException.class, "Data truncation: BIGINT UNSIGNED value is out of range in '\\(1 \\+ 18446744073709551615\\)'", new Callable() { + public Void call() throws Exception { + testRs1.next(); + return null; + } + }); + + try { + testRs1.close(); + } catch (CommunicationsException ex) { + fail("ResultSet.close() locked while trying to read remaining, nonexistent, streamed data."); + } + + try { + ResultSet testRs2 = testStmt.executeQuery("SELECT 1"); + assertTrue(testRs2.next()); + assertEquals(1, testRs2.getInt(1)); + testRs2.close(); + } catch (SQLException ex) { + if (ex.getMessage().startsWith("Streaming result set")) { + fail("There is a Streaming result set still active. No other statements can be issued on this connection."); + } else { + ex.printStackTrace(); + fail(ex.getMessage()); + } + } + + testStmt.close(); + testConn.close(); + } + + /** + * Tests fix for BUG#19536760 - GETSTRING() CALL AFTER RS.RELATIVE() RETURNS NULLPOINTEREXCEPTION + * + * @throws Exception + * if the test fails. + */ + public void testBug19536760() throws Exception { + + createTable("testBug19536760", "(id int)"); + + this.stmt.execute("insert into testBug19536760 values(1),(2),(3)"); + this.rs = this.stmt.executeQuery("select * from testBug19536760"); + + // "before first" check + testBug19536760CheckStates(this.rs, true, false, false, false); + + assertFalse(this.rs.previous()); + assertFalse(this.rs.previous()); + assertFalse(this.rs.previous()); + testBug19536760CheckStates(this.rs, true, false, false, false); + + assertFalse(this.rs.absolute(-7)); + testBug19536760CheckStates(this.rs, true, false, false, false); + + assertTrue(this.rs.next()); + this.rs.beforeFirst(); + testBug19536760CheckStates(this.rs, true, false, false, false); + + // "first" check + this.rs.next(); + testBug19536760CheckStates(this.rs, false, true, false, false); + + this.rs.absolute(-3); + testBug19536760CheckStates(this.rs, false, true, false, false); + + assertTrue(this.rs.relative(1)); + assertTrue(this.rs.previous()); + testBug19536760CheckStates(this.rs, false, true, false, false); + + this.rs.absolute(2); + testBug19536760CheckStates(this.rs, false, false, false, false); + this.rs.first(); + testBug19536760CheckStates(this.rs, false, true, false, false); + + // "last" check + this.rs.absolute(-1); + testBug19536760CheckStates(this.rs, false, false, true, false); + + assertFalse(this.rs.next()); + testBug19536760CheckStates(this.rs, false, false, false, true); + assertTrue(this.rs.previous()); + testBug19536760CheckStates(this.rs, false, false, true, false); + + assertFalse(this.rs.relative(1)); + testBug19536760CheckStates(this.rs, false, false, false, true); + assertTrue(this.rs.relative(-1)); + testBug19536760CheckStates(this.rs, false, false, true, false); + + assertTrue(this.rs.relative(-1)); + testBug19536760CheckStates(this.rs, false, false, false, false); + this.rs.last(); + testBug19536760CheckStates(this.rs, false, false, true, false); + + // "after last" check + assertFalse(this.rs.next()); + assertFalse(this.rs.next()); + assertFalse(this.rs.next()); + testBug19536760CheckStates(this.rs, false, false, false, true); + + assertTrue(this.rs.relative(-1)); + testBug19536760CheckStates(this.rs, false, false, true, false); + + assertFalse(this.rs.relative(3)); + testBug19536760CheckStates(this.rs, false, false, false, true); + + assertTrue(this.rs.previous()); + testBug19536760CheckStates(this.rs, false, false, true, false); + + this.rs.afterLast(); + testBug19536760CheckStates(this.rs, false, false, false, true); + + assertFalse(this.rs.next()); + testBug19536760CheckStates(this.rs, false, false, false, true); + + // empty result set + this.rs = this.stmt.executeQuery("select * from testBug19536760 where id=5"); + assertFalse(this.rs.first()); + assertFalse(this.rs.last()); + + testBug19536760CheckStates(this.rs, false, false, false, false); + + assertFalse(this.rs.next()); + testBug19536760CheckStates(this.rs, false, false, false, false); + + assertFalse(this.rs.relative(2)); + testBug19536760CheckStates(this.rs, false, false, false, false); + + } + + private void testBug19536760CheckStates(ResultSet rset, boolean expectedIsBeforeFirst, boolean expectedIsFirst, boolean expectedIsLast, + boolean expectedIsAfterLast) throws Exception { + assertEquals(expectedIsBeforeFirst, rset.isBeforeFirst()); + assertEquals(expectedIsFirst, rset.isFirst()); + assertEquals(expectedIsLast, rset.isLast()); + assertEquals(expectedIsAfterLast, rset.isAfterLast()); + } + + /** + * Tests for fix to BUG#20804635 - GETTIME() AND GETDATE() FUNCTIONS FAILS WHEN FRACTIONAL PART EXISTS + * + * @throws Exception + * if the test fails + */ + public void testBug20804635() throws Exception { + if (!versionMeetsMinimum(5, 6, 4)) { + return; // fractional seconds are not supported in previous versions + } + + createTable("testBug20804635", "(c1 timestamp(2), c2 time(3), c3 datetime(4))"); + this.stmt.executeUpdate("INSERT INTO testBug20804635 VALUES ('2031-01-15 03:14:07.339999','12:59:00.9889','2031-01-15 03:14:07.333399')"); + + Calendar cal = Calendar.getInstance(); + + Connection testConn; + ResultSet rset; + Properties props = new Properties(); + props.setProperty("useFastDateParsing", "true"); + + for (int i = 0; i < 2; i++) { + System.out.println("With useFastDateParsing=" + props.getProperty("useFastDateParsing")); + testConn = getConnectionWithProps(props); + rset = testConn.createStatement().executeQuery("SELECT * FROM testBug20804635"); + rset.next(); + + assertEquals("2031-01-15", rset.getDate(1).toString()); + assertEquals("2031-01-15", rset.getDate(1, cal).toString()); + assertEquals("03:14:07", rset.getTime(1).toString()); + assertEquals("03:14:07", rset.getTime(1, cal).toString()); + assertEquals("2031-01-15 03:14:07.34", rset.getTimestamp(1).toString()); + assertEquals("2031-01-15 03:14:07.34", rset.getTimestamp(1, cal).toString()); + + assertEquals("1970-01-01", rset.getDate(2).toString()); + assertEquals("1970-01-01", rset.getDate(2, cal).toString()); + assertEquals("12:59:00", rset.getTime(2).toString()); + assertEquals("12:59:00", rset.getTime(2, cal).toString()); + assertEquals("1970-01-01 12:59:00.989", rset.getTimestamp(2).toString()); + assertEquals("1970-01-01 12:59:00.989", rset.getTimestamp(2, cal).toString()); + + assertEquals("2031-01-15", rset.getDate(3).toString()); + assertEquals("2031-01-15", rset.getDate(3, cal).toString()); + assertEquals("03:14:07", rset.getTime(3).toString()); + assertEquals("03:14:07", rset.getTime(3, cal).toString()); + assertEquals("2031-01-15 03:14:07.3334", rset.getTimestamp(3).toString()); + assertEquals("2031-01-15 03:14:07.3334", rset.getTimestamp(3, cal).toString()); + + testConn.close(); + props.setProperty("useFastDateParsing", "false"); + } + } + + /** + * Tests fix for Bug#80522 - Using useCursorFetch leads to data corruption in Connector/J for TIME type. + */ + public void testBug80522() throws Exception { + createTable("testBug80522", "(t TIME, d DATE, s TEXT)"); + + Properties props = new Properties(); + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("NO_ZERO_DATE")) { + sqlMode = removeSqlMode("NO_ZERO_DATE", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + props.setProperty("traceProtocol", "false"); + props.setProperty("defaultFetchSize", "5"); + props.setProperty("useCursorFetch", "true"); + Connection testConn = getConnectionWithProps(props); + Statement testStmt = testConn.createStatement(); + + testStmt.executeUpdate("INSERT INTO testBug80522 VALUES ('00:00:00', '0000-00-00', 'Zeros')"); + final ResultSet testRs = testStmt.executeQuery("SELECT * FROM testBug80522"); + assertTrue(testRs.next()); + assertEquals(new Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1970-01-01 00:00:00").getTime()), testRs.getTimestamp(1)); + assertThrows(SQLException.class, "Value '0000-00-00' can not be represented as java\\.sql\\.Timestamp", new Callable() { + public Void call() throws Exception { + System.out.println(testRs.getTimestamp(2)); + return null; + } + }); + assertEquals("Zeros", testRs.getString(3)); + + testRs.close(); + testStmt.close(); + testConn.close(); + } + + /** + * Tests fix for Bug#56479 - getTimestamp throws exception. + * + * This bug occurs exclusively on UpdatableResultSets when retrieving previously set timestamp values. + */ + public void testBug56479() throws Exception { + if (!versionMeetsMinimum(5, 6)) { + return; + } + + String tsStr1 = "2010-09-02 03:55:10"; + String tsStr2 = "2010-09-02 03:55:10.123456"; + Timestamp ts1 = Timestamp.valueOf(tsStr1); + Timestamp ts2 = Timestamp.valueOf(tsStr2); + + createTable("testBug56479", "(id INT PRIMARY KEY, ts1 TIMESTAMP NULL, ts2 TIMESTAMP(6) NULL)", "InnoDB"); + this.stmt.executeUpdate("INSERT INTO testBug56479 VALUES (1, '" + tsStr1 + "', '" + tsStr2 + "'), (2, '" + tsStr1 + "', '" + tsStr2 + "')"); + + Statement testStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + ResultSet testRs = testStmt.executeQuery("SELECT * FROM testBug56479"); + + // Initial verifications. + assertTrue(testRs.next()); + assertEquals(1, testRs.getInt(1)); + assertEquals(ts1, testRs.getTimestamp(2)); + assertEquals(ts2, testRs.getTimestamp(3)); + assertTrue(testRs.next()); + assertEquals(2, testRs.getInt(1)); + assertEquals(ts1, testRs.getTimestamp(2)); + assertEquals(ts2, testRs.getTimestamp(3)); + assertFalse(testRs.next()); + + // Update second row to null. + testRs.absolute(2); + testRs.updateNull(2); + testRs.updateNull(3); + testRs.updateRow(); + assertEquals(2, testRs.getInt(1)); + assertNull(testRs.getTimestamp(2)); + assertNull(testRs.getTimestamp(3)); + testRs.beforeFirst(); + + // Check data changes using a plain ResultSet. + this.rs = this.stmt.executeQuery("SELECT * FROM testBug56479"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertNull(this.rs.getTimestamp(2)); + assertNull(this.rs.getTimestamp(3)); + assertFalse(this.rs.next()); + + // Update second row to original values. + testRs.absolute(2); + testRs.updateTimestamp(2, ts1); + testRs.updateTimestamp(3, ts2); + testRs.updateRow(); + assertEquals(2, testRs.getInt(1)); + assertEquals(ts1, testRs.getTimestamp(2)); + assertEquals(ts2, testRs.getTimestamp(3)); + testRs.beforeFirst(); + + // Check data changes using a plain ResultSet. + this.rs = this.stmt.executeQuery("SELECT * FROM testBug56479"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertFalse(this.rs.next()); + + // Insert new row. + testRs.moveToInsertRow(); + testRs.updateInt(1, 3); + testRs.updateTimestamp(2, ts1); + testRs.updateTimestamp(3, ts2); + testRs.insertRow(); + assertEquals(3, testRs.getInt(1)); + assertEquals(ts1, testRs.getTimestamp(2)); + assertEquals(ts2, testRs.getTimestamp(3)); + testRs.beforeFirst(); + + // Check final data using a plain ResultSet. + this.rs = this.stmt.executeQuery("SELECT * FROM testBug56479"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertTrue(this.rs.next()); + assertEquals(3, this.rs.getInt(1)); + assertEquals(ts1, this.rs.getTimestamp(2)); + assertEquals(ts2, this.rs.getTimestamp(3)); + assertFalse(this.rs.next()); + } + + /** + * Tests fix for Bug#78685 - Wrong results when retrieving the value of a BIT column as an integer. + */ + public void testBug78685() throws Exception { + createTable("testBug78685", "(b1 BIT(8), b2 BIT(16), b3 BIT(24))", "InnoDB"); + // 46 == b'00101110' == '.' + // 11822 == b'0010111000101110' == '..' + // -- + // 47 == '/' + // 12079 == '//' + // -- + // 48 == '0' + // 12336 = '00' + // -- + // 49 == b'00110001' == '1' + // 12593 == b'0011000100110001' == '11' + // -- + // 50 == '2' + // 12850 == '22' + // -- + // 51 == '3' + // 13107 == '33' + this.stmt.executeUpdate("INSERT INTO testBug78685 VALUES (b'00101110', b'0010111000101110', b'0010111000101110'), ('/', '//', '//'), " + + "(48, 12336, 12336), (b'00110001', b'0011000100110001', b'0011000100110001'), ('2', '22', '22'), (51, 13107, 13107)"); + + boolean useFastIntParsing = false; + boolean useServerPrepStmts = false; + do { + // Test result set from plain statements. + String testCase = String.format("Case [fstIntPrs: %s, useSPS: %s, StmtType: %s]", useFastIntParsing ? "Y" : "N", useServerPrepStmts ? "Y" : "N", + "Plain"); + + final Properties props = new Properties(); + props.setProperty("useFastIntParsing", Boolean.toString(useFastIntParsing)); + Connection testConn = getConnectionWithProps(props); + this.rs = testConn.createStatement().executeQuery("SELECT b1, b1 + 0, BIN(b1), b2, b2 + 0, BIN(b2), b3, b3 + 0, BIN(b3) FROM testBug78685"); + testBug78685CheckData(testCase); + testConn.close(); + + // Test result set from prepared statements + testCase = String.format("Case [fstIntPrs: %s, useSPS: %s, StmtType: %s]", useFastIntParsing ? "Y" : "N", useServerPrepStmts ? "Y" : "N", + "PrepStmt"); + + props.setProperty("useServerPrepStmts", Boolean.toString(useServerPrepStmts)); + testConn = getConnectionWithProps(props); + this.pstmt = testConn.prepareStatement("SELECT b1, b1 + 0, BIN(b1), b2, b2 + 0, BIN(b2), b3, b3 + 0, BIN(b3) FROM testBug78685"); + this.rs = this.pstmt.executeQuery(); + testBug78685CheckData(""); + testConn.close(); + } while ((useFastIntParsing = !useFastIntParsing) || (useServerPrepStmts = !useServerPrepStmts)); + } + + private void testBug78685CheckData(String testCase) throws Exception { + int rowCount = 0; + while (this.rs.next()) { + int expectedNumBase = 46 + rowCount; + + // Column "b1 BIT(8)" + int expectedNum = expectedNumBase; + + assertEquals(testCase, expectedNum, this.rs.getShort(1)); + assertEquals(testCase, expectedNum, this.rs.getInt(1)); + assertEquals(testCase, expectedNum, this.rs.getLong(1)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(1).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(1)); + assertTrue(this.rs.getObject(1) instanceof byte[]); + assertByteArrayEquals(testCase, new byte[] { (byte) (expectedNumBase) }, (byte[]) this.rs.getObject(1)); + + assertEquals(testCase, expectedNum, this.rs.getShort(2)); + assertEquals(testCase, expectedNum, this.rs.getInt(2)); + assertEquals(testCase, expectedNum, this.rs.getLong(2)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(2).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(2)); + assertEquals(testCase, BigInteger.valueOf(expectedNum), this.rs.getObject(2)); + + final ResultSet testRs1 = this.rs; + assertThrows(SQLException.class, "'[01\\.]+' in column '3' is outside valid range for the datatype SMALLINT\\.", new Callable() { + public Void call() throws Exception { + testRs1.getShort(3); + return null; + } + }); + String expectedString = Integer.toBinaryString(expectedNum); + assertEquals(testCase, Integer.parseInt(expectedString), this.rs.getInt(3)); + assertEquals(testCase, Long.parseLong(expectedString), this.rs.getLong(3)); + assertEquals(testCase, expectedString, this.rs.getString(3)); + assertEquals(testCase, expectedString, this.rs.getObject(3)); + + // Column "b1 BIT(16)" + expectedNum = expectedNumBase + expectedNumBase * 256; + + assertEquals(testCase, expectedNum, this.rs.getShort(4)); + assertEquals(testCase, expectedNum, this.rs.getInt(4)); + assertEquals(testCase, expectedNum, this.rs.getLong(4)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(4).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(4)); + assertTrue(this.rs.getObject(4) instanceof byte[]); + assertByteArrayEquals(testCase, new byte[] { (byte) (expectedNumBase), (byte) (expectedNumBase) }, (byte[]) this.rs.getObject(4)); + + assertEquals(testCase, expectedNum, this.rs.getShort(5)); + assertEquals(testCase, expectedNum, this.rs.getInt(5)); + assertEquals(testCase, expectedNum, this.rs.getLong(5)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(5).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(5)); + assertEquals(testCase, BigInteger.valueOf(expectedNum), this.rs.getObject(5)); + + final ResultSet testRs2 = this.rs; + assertThrows(SQLException.class, "'[E\\d\\.]+' in column '6' is outside valid range for the datatype SMALLINT\\.", new Callable() { + public Void call() throws Exception { + testRs2.getShort(6); + return null; + } + }); + assertThrows(SQLException.class, "'[E\\d\\.]+' in column '6' is outside valid range for the datatype INTEGER\\.", new Callable() { + public Void call() throws Exception { + testRs2.getInt(6); + return null; + } + }); + expectedString = Long.toBinaryString(expectedNum); + assertEquals(testCase, Long.parseLong(expectedString), this.rs.getLong(6)); + assertEquals(testCase, expectedString, this.rs.getString(6)); + assertEquals(testCase, expectedString, this.rs.getObject(6)); + + // Column "b1 BIT(24)" + expectedNum = expectedNumBase + expectedNumBase * 256; + + assertEquals(testCase, expectedNum, this.rs.getShort(7)); + assertEquals(testCase, expectedNum, this.rs.getInt(7)); + assertEquals(testCase, expectedNum, this.rs.getLong(7)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(7).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(7)); + assertTrue(this.rs.getObject(7) instanceof byte[]); + assertByteArrayEquals(testCase, new byte[] { 0, (byte) (expectedNumBase), (byte) (expectedNumBase) }, (byte[]) this.rs.getObject(7)); + + assertEquals(testCase, expectedNum, this.rs.getShort(8)); + assertEquals(testCase, expectedNum, this.rs.getInt(8)); + assertEquals(testCase, expectedNum, this.rs.getLong(8)); + assertEquals(testCase, expectedNum, this.rs.getBigDecimal(8).intValue()); + assertEquals(testCase, String.valueOf(expectedNum), this.rs.getString(8)); + assertEquals(testCase, BigInteger.valueOf(expectedNum), this.rs.getObject(8)); + + final ResultSet testRs3 = this.rs; + assertThrows(SQLException.class, "'[E\\d\\.]+' in column '9' is outside valid range for the datatype SMALLINT\\.", new Callable() { + public Void call() throws Exception { + testRs3.getShort(9); + return null; + } + }); + assertThrows(SQLException.class, "'[E\\d\\.]+' in column '9' is outside valid range for the datatype INTEGER\\.", new Callable() { + public Void call() throws Exception { + testRs3.getInt(9); + return null; + } + }); + expectedString = Long.toBinaryString(expectedNum); + assertEquals(testCase, Long.parseLong(expectedString), this.rs.getLong(9)); + assertEquals(testCase, expectedString, this.rs.getString(9)); + assertEquals(testCase, expectedString, this.rs.getObject(9)); + + rowCount++; + } + assertEquals(testCase, 6, rowCount); + } + + /** + * Tests fix for Bug#80631 - ResultSet.getString return garbled result with json type data. + */ + public void testBug80631() throws Exception { + if (!versionMeetsMinimum(5, 7, 9)) { + return; + } + + /* + * \u4E2D\u56FD (Simplified Chinese): "China" + * \u65E5\u672C (Japanese): "Japan" + * \uD83D\uDC2C (Emoji): "Dolphin" + * \u263A (Symbols): "White Smiling Face" + */ + String[] data = new String[] { "\u4E2D\u56FD", "\u65E5\u672C", "\uD83D\uDC2C", "\u263A" }; + String jsonTmpl = "{\"data\": \"%s\"}"; + + createTable("testBug80631", "(data JSON)"); + createProcedure("testBug80631Insert", "(IN data JSON) BEGIN INSERT INTO testBug80631 VALUES (data); END;"); + createProcedure("testBug80631SELECT", "() BEGIN SELECT * FROM testBug80631; END;"); + + boolean useSPS = false; + do { + final String testCase = String.format("Case: [SPS: %s]", useSPS ? "Y" : "N"); + final Connection testConn = getConnectionWithProps("characterEncoding=UTF-8,useUnicode=true,useServerPrepStmts=" + useSPS); + + // Insert and select using a Statement. + Statement testStmt = testConn.createStatement(); + for (String d : data) { + assertEquals(testCase, 1, testStmt.executeUpdate("INSERT INTO testBug80631 VALUES ('" + String.format(jsonTmpl, d) + "')")); + } + this.rs = testStmt.executeQuery("SELECT * FROM testBug80631"); + for (int i = 0; i < data.length; i++) { + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, String.format(jsonTmpl, data[i]), this.rs.getString(1)); + } + testStmt.close(); + + testConn.createStatement().execute("TRUNCATE TABLE testBug80631"); + + // Insert and select using a PreparedStatement. + PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testBug80631 VALUES (?)"); + for (String d : data) { + testPstmt.setString(1, String.format(jsonTmpl, d)); + assertEquals(testCase, 1, testPstmt.executeUpdate()); + } + testPstmt.close(); + testPstmt = testConn.prepareStatement("SELECT * FROM testBug80631"); + this.rs = testPstmt.executeQuery(); + for (int i = 0; i < data.length; i++) { + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, String.format(jsonTmpl, data[i]), this.rs.getString(1)); + } + testPstmt.close(); + + testConn.createStatement().execute("TRUNCATE TABLE testBug80631"); + + // Insert and select using a CallableStatement. + CallableStatement testCstmt = testConn.prepareCall("{CALL testBug80631Insert(?)}"); + for (String d : data) { + testCstmt.setString(1, String.format(jsonTmpl, d)); + assertEquals(testCase, 1, testCstmt.executeUpdate()); + } + testCstmt.close(); + testCstmt = testConn.prepareCall("{CALL testBug80631Select()}"); + testCstmt.execute(); + this.rs = testCstmt.getResultSet(); + for (int i = 0; i < data.length; i++) { + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, String.format(jsonTmpl, data[i]), this.rs.getString(1)); + } + testCstmt.close(); + + testConn.close(); + } while (useSPS = !useSPS); + } + + /** + * Tests fix for Bug#23197238 - EXECUTEQUERY() FAILS FOR JSON DATA WHEN RESULTSETCONCURRENCY=CONCUR_UPDATABLE. + */ + public void testBug23197238() throws Exception { + if (!versionMeetsMinimum(5, 7, 9)) { + return; + } + + createTable("testBug23197238", "(id INT AUTO_INCREMENT PRIMARY KEY, doc JSON DEFAULT NULL)"); + + String[] docs = new String[] { "{\"key1\": \"value1\"}", "{\"key2\": \"value2\"}", "{\"key3\": \"value3\"}" }; + Connection testConn = getConnectionWithProps("useCursorFetch=true"); + + Statement testStmt = testConn.createStatement(); + testStmt.execute("INSERT INTO testBug23197238 (doc) VALUES ('" + docs[2] + "')"); + testStmt.close(); + + testBug23197238Assert(new String[] { docs[2] }); + + testStmt = testConn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = testStmt.executeQuery("SELECT * FROM testBug23197238"); + assertTrue(this.rs.next()); + this.rs.updateObject(2, docs[1]); + this.rs.updateRow(); + this.rs.moveToInsertRow(); + this.rs.updateObject(2, docs[1]); + this.rs.insertRow(); + testStmt.close(); + + testBug23197238Assert(new String[] { docs[1], docs[1] }); + + PreparedStatement testPstmt = testConn.prepareStatement("SELECT * FROM testBug23197238 WHERE id = ?", ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_UPDATABLE); + testPstmt.setObject(1, 1, Types.INTEGER); + this.rs = testPstmt.executeQuery(); + assertTrue(this.rs.next()); + this.rs.updateObject(2, docs[0]); + this.rs.updateRow(); + this.rs.moveToInsertRow(); + this.rs.updateObject(2, docs[2]); + this.rs.insertRow(); + testPstmt.close(); + + testBug23197238Assert(docs); + + testConn.close(); + } + + private void testBug23197238Assert(String[] expected) throws Exception { + this.rs = this.stmt.executeQuery("SELECT * FROM testBug23197238"); + for (String e : expected) { + assertTrue(this.rs.next()); + assertEquals(e, this.rs.getString(2)); + } + assertFalse(this.rs.next()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/StatementRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/StatementRegressionTest.java new file mode 100644 index 0000000..de1b327 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/StatementRegressionTest.java @@ -0,0 +1,8067 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.Array; +import java.sql.BatchUpdateException; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DataTruncation; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.mysql.jdbc.CachedResultSetMetaData; +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.CommunicationsException; +import com.mysql.jdbc.Field; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ParameterBindings; +import com.mysql.jdbc.ReplicationConnection; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.ServerPreparedStatement; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.TimeUtil; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; + +import testsuite.BaseStatementInterceptor; +import testsuite.BaseTestCase; +import testsuite.UnreliableSocketFactory; + +/** + * Regression tests for the Statement class + */ +public class StatementRegressionTest extends BaseTestCase { + class PrepareThread extends Thread { + Connection c; + + PrepareThread(Connection cn) { + this.c = cn; + } + + @Override + public void run() { + for (int i = 0; i < 20; i++) // force this to end eventually + { + try { + this.c.prepareStatement("SELECT 1"); + StatementRegressionTest.this.testServerPrepStmtDeadlockCounter++; + Thread.sleep(400); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + static int count = 0; + + static int nextID = 1; // The next ID we expected to generate + + /* + * Each row in this table is to be converted into a single REPLACE + * statement. If the value is zero, a new record is to be created using then + * autoincrement feature. If the value is non-zero, the existing row of that + * value is to be replace with, obviously, the same key. I expect one + * Generated Key for each zero value - but I would accept one key for each + * value, with non-zero values coming back as themselves. + */ + static final int[][] tests = { { 0 }, // generate 1 + { 1, 0, 0 }, // update 1, generate 2, 3 + { 2, 0, 0, }, // update 2, generate 3, 4 + }; + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StatementRegressionTest.class); + } + + protected int testServerPrepStmtDeadlockCounter = 0; + + /** + * Constructor for StatementRegressionTest. + * + * @param name + * the name of the test to run + */ + public StatementRegressionTest(String name) { + super(name); + } + + private void addBatchItems(Statement statement, PreparedStatement pStmt, String tableName, int i) throws SQLException { + pStmt.setString(1, "ps_batch_" + i); + pStmt.setString(2, "ps_batch_" + i); + pStmt.addBatch(); + + statement.addBatch("INSERT INTO " + tableName + " (strdata1, strdata2) VALUES (\"s_batch_" + i + "\",\"s_batch_" + i + "\")"); + } + + private void createGGKTables() throws Exception { + // Delete and recreate table + dropGGKTables(); + createTable("testggk", "(id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,val INT NOT NULL)", "MYISAM"); + } + + private void doGGKTestPreparedStatement(int[] values, boolean useUpdate) throws Exception { + // Generate the the multiple replace command + StringBuilder cmd = new StringBuilder("REPLACE INTO testggk VALUES "); + int newKeys = 0; + + for (int i = 0; i < values.length; i++) { + cmd.append("("); + + if (values[i] == 0) { + cmd.append("NULL"); + newKeys += 1; + } else { + cmd.append(values[i]); + } + + cmd.append(", "); + cmd.append(count++); + cmd.append("), "); + } + + cmd.setLength(cmd.length() - 2); // trim the final ", " + + // execute and print it + System.out.println(cmd.toString()); + + PreparedStatement pStmt = this.conn.prepareStatement(cmd.toString(), Statement.RETURN_GENERATED_KEYS); + + if (useUpdate) { + pStmt.executeUpdate(); + } else { + pStmt.execute(); + } + + // print out what actually happened + System.out.println("Expect " + newKeys + " generated keys, starting from " + nextID); + + this.rs = pStmt.getGeneratedKeys(); + StringBuilder res = new StringBuilder("Got keys"); + + int[] generatedKeys = new int[newKeys]; + int i = 0; + + while (this.rs.next()) { + if (i < generatedKeys.length) { + generatedKeys[i] = this.rs.getInt(1); + } + + i++; + + res.append(" " + this.rs.getInt(1)); + } + + int numberOfGeneratedKeys = i; + + assertTrue("Didn't retrieve expected number of generated keys, expected " + newKeys + ", found " + numberOfGeneratedKeys, + numberOfGeneratedKeys == newKeys); + assertTrue("Keys didn't start with correct sequence: ", generatedKeys[0] == nextID); + + System.out.println(res.toString()); + + // Read and print the new state of the table + this.rs = this.stmt.executeQuery("SELECT id, val FROM testggk"); + System.out.println("New table contents "); + + while (this.rs.next()) { + System.out.println("Id " + this.rs.getString(1) + " val " + this.rs.getString(2)); + } + + // Tidy up + System.out.println(""); + nextID += newKeys; + } + + private void doGGKTestStatement(int[] values, boolean useUpdate) throws Exception { + // Generate the the multiple replace command + StringBuilder cmd = new StringBuilder("REPLACE INTO testggk VALUES "); + int newKeys = 0; + + for (int i = 0; i < values.length; i++) { + cmd.append("("); + + if (values[i] == 0) { + cmd.append("NULL"); + newKeys += 1; + } else { + cmd.append(values[i]); + } + + cmd.append(", "); + cmd.append(count++); + cmd.append("), "); + } + + cmd.setLength(cmd.length() - 2); // trim the final ", " + + // execute and print it + System.out.println(cmd.toString()); + + if (useUpdate) { + this.stmt.executeUpdate(cmd.toString(), Statement.RETURN_GENERATED_KEYS); + } else { + this.stmt.execute(cmd.toString(), Statement.RETURN_GENERATED_KEYS); + } + + // print out what actually happened + System.out.println("Expect " + newKeys + " generated keys, starting from " + nextID); + + this.rs = this.stmt.getGeneratedKeys(); + StringBuilder res = new StringBuilder("Got keys"); + + int[] generatedKeys = new int[newKeys]; + int i = 0; + + while (this.rs.next()) { + if (i < generatedKeys.length) { + generatedKeys[i] = this.rs.getInt(1); + } + + i++; + + res.append(" " + this.rs.getInt(1)); + } + + int numberOfGeneratedKeys = i; + + assertTrue("Didn't retrieve expected number of generated keys, expected " + newKeys + ", found " + numberOfGeneratedKeys, + numberOfGeneratedKeys == newKeys); + assertTrue("Keys didn't start with correct sequence: ", generatedKeys[0] == nextID); + + System.out.println(res.toString()); + + // Read and print the new state of the table + this.rs = this.stmt.executeQuery("SELECT id, val FROM testggk"); + System.out.println("New table contents "); + + while (this.rs.next()) { + System.out.println("Id " + this.rs.getString(1) + " val " + this.rs.getString(2)); + } + + // Tidy up + System.out.println(""); + nextID += newKeys; + } + + private void dropGGKTables() throws Exception { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testggk"); + } + + /** + * @param pStmt + * @param catId + * @throws SQLException + */ + private void execQueryBug5191(PreparedStatement pStmt, int catId) throws SQLException { + pStmt.setInt(1, catId); + + this.rs = pStmt.executeQuery(); + + assertTrue(this.rs.next()); + assertTrue(this.rs.next()); + // assertTrue(rs.next()); + + assertFalse(this.rs.next()); + } + + private String getByteArrayString(byte[] ba) { + StringBuilder buffer = new StringBuilder(); + if (ba != null) { + for (int i = 0; i < ba.length; i++) { + buffer.append("0x" + Integer.toHexString(ba[i] & 0xff) + " "); + } + } else { + buffer.append("null"); + } + return buffer.toString(); + } + + /** + * @param continueBatchOnError + * @throws SQLException + */ + private void innerBug6823(boolean continueBatchOnError) throws SQLException { + Properties continueBatchOnErrorProps = new Properties(); + continueBatchOnErrorProps.setProperty("continueBatchOnError", String.valueOf(continueBatchOnError)); + this.conn = getConnectionWithProps(continueBatchOnErrorProps); + Statement statement = this.conn.createStatement(); + + String tableName = "testBug6823"; + + createTable(tableName, + "(id int not null primary key auto_increment, strdata1 varchar(255) not null, strdata2 varchar(255), UNIQUE INDEX (strdata1(100)))"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO " + tableName + " (strdata1, strdata2) VALUES (?,?)"); + + int c = 0; + addBatchItems(statement, pStmt, tableName, ++c); + addBatchItems(statement, pStmt, tableName, ++c); + addBatchItems(statement, pStmt, tableName, ++c); + addBatchItems(statement, pStmt, tableName, c); // duplicate entry + addBatchItems(statement, pStmt, tableName, ++c); + addBatchItems(statement, pStmt, tableName, ++c); + + int expectedUpdateCounts = continueBatchOnError ? 6 : 3; + + BatchUpdateException e1 = null; + BatchUpdateException e2 = null; + + int[] updateCountsPstmt = null; + try { + updateCountsPstmt = pStmt.executeBatch(); + } catch (BatchUpdateException e) { + e1 = e; + updateCountsPstmt = e1.getUpdateCounts(); + } + + int[] updateCountsStmt = null; + try { + updateCountsStmt = statement.executeBatch(); + } catch (BatchUpdateException e) { + e2 = e; + updateCountsStmt = e1.getUpdateCounts(); + } + + assertNotNull(e1); + assertNotNull(e2); + + assertEquals(expectedUpdateCounts, updateCountsPstmt.length); + assertEquals(expectedUpdateCounts, updateCountsStmt.length); + + if (continueBatchOnError) { + assertTrue(updateCountsPstmt[3] == Statement.EXECUTE_FAILED); + assertTrue(updateCountsStmt[3] == Statement.EXECUTE_FAILED); + } + + int psRows = 0; + this.rs = this.stmt.executeQuery("SELECT * from " + tableName + " WHERE strdata1 like \"ps_%\""); + while (this.rs.next()) { + psRows++; + } + assertTrue(psRows > 0); + + int sRows = 0; + this.rs = this.stmt.executeQuery("SELECT * from " + tableName + " WHERE strdata1 like \"s_%\""); + while (this.rs.next()) { + sRows++; + } + assertTrue(sRows > 0); + + assertTrue(psRows + "!=" + sRows, psRows == sRows); + } + + /** + * Tests fix for BUG#10155, double quotes not recognized when parsing + * client-side prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug10155() throws Exception { + this.conn.prepareStatement("SELECT \"Test question mark? Test single quote'\"").executeQuery().close(); + } + + /** + * Tests fix for BUG#10630, Statement.getWarnings() fails with NPE if + * statement has been closed. + */ + public void testBug10630() throws Exception { + Connection conn2 = null; + Statement stmt2 = null; + + try { + conn2 = getConnectionWithProps((Properties) null); + stmt2 = conn2.createStatement(); + + conn2.close(); + stmt2.getWarnings(); + fail("Should've caught an exception here"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } finally { + if (stmt2 != null) { + stmt2.close(); + } + + if (conn2 != null) { + conn2.close(); + } + } + } + + /** + * Tests fix for BUG#11115, Varbinary data corrupted when using server-side + * prepared statements. + */ + public void testBug11115() throws Exception { + String tableName = "testBug11115"; + + if (versionMeetsMinimum(4, 1, 0)) { + + createTable(tableName, "(pwd VARBINARY(30)) DEFAULT CHARACTER SET utf8", "InnoDB"); + + byte[] bytesToTest = new byte[] { 17, 120, -1, -73, -5 }; + + PreparedStatement insStmt = this.conn.prepareStatement("INSERT INTO " + tableName + " (pwd) VALUES (?)"); + insStmt.setBytes(1, bytesToTest); + insStmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT pwd FROM " + tableName); + this.rs.next(); + + byte[] fromDatabase = this.rs.getBytes(1); + + assertEquals(bytesToTest.length, fromDatabase.length); + + for (int i = 0; i < bytesToTest.length; i++) { + assertEquals(bytesToTest[i], fromDatabase[i]); + } + + this.rs = this.conn.prepareStatement("SELECT pwd FROM " + tableName).executeQuery(); + this.rs.next(); + + fromDatabase = this.rs.getBytes(1); + + assertEquals(bytesToTest.length, fromDatabase.length); + + for (int i = 0; i < bytesToTest.length; i++) { + assertEquals(bytesToTest[i], fromDatabase[i]); + } + } + } + + public void testBug11540() throws Exception { + Locale originalLocale = Locale.getDefault(); + Connection thaiConn = null; + Statement thaiStmt = null; + PreparedStatement thaiPrepStmt = null; + + try { + createTable("testBug11540", "(field1 DATE, field2 TIMESTAMP)"); + this.stmt.executeUpdate("INSERT INTO testBug11540 VALUES (NOW(), NOW())"); + Locale.setDefault(new Locale("th", "TH")); + Properties props = new Properties(); + props.setProperty("jdbcCompliantTruncation", "false"); + + thaiConn = getConnectionWithProps(props); + thaiStmt = thaiConn.createStatement(); + + this.rs = thaiStmt.executeQuery("SELECT field1, field2 FROM testBug11540"); + this.rs.next(); + + Date origDate = this.rs.getDate(1); + Timestamp origTimestamp = this.rs.getTimestamp(1); + this.rs.close(); + + thaiStmt.executeUpdate("TRUNCATE TABLE testBug11540"); + + thaiPrepStmt = ((com.mysql.jdbc.Connection) thaiConn).clientPrepareStatement("INSERT INTO testBug11540 VALUES (?,?)"); + thaiPrepStmt.setDate(1, origDate); + thaiPrepStmt.setTimestamp(2, origTimestamp); + thaiPrepStmt.executeUpdate(); + + this.rs = thaiStmt.executeQuery("SELECT field1, field2 FROM testBug11540"); + this.rs.next(); + + Date testDate = this.rs.getDate(1); + Timestamp testTimestamp = this.rs.getTimestamp(1); + this.rs.close(); + + assertEquals(origDate, testDate); + assertEquals(origTimestamp, testTimestamp); + + } finally { + Locale.setDefault(originalLocale); + } + } + + /** + * Tests fix for BUG#11663, autoGenerateTestcaseScript uses bogus parameter + * names for server-side prepared statements. + * + * @throws Exception + * if the test fails. + */ + public void testBug11663() throws Exception { + if (versionMeetsMinimum(4, 1, 0) && ((com.mysql.jdbc.Connection) this.conn).getUseServerPreparedStmts()) { + Connection testcaseGenCon = null; + PrintStream oldErr = System.err; + + try { + createTable("testBug11663", "(field1 int)"); + + Properties props = new Properties(); + props.setProperty("autoGenerateTestcaseScript", "true"); + testcaseGenCon = getConnectionWithProps(props); + ByteArrayOutputStream testStream = new ByteArrayOutputStream(); + PrintStream testErr = new PrintStream(testStream); + System.setErr(testErr); + this.pstmt = testcaseGenCon.prepareStatement("SELECT field1 FROM testBug11663 WHERE field1=?"); + this.pstmt.setInt(1, 1); + this.pstmt.execute(); + System.setErr(oldErr); + String testString = new String(testStream.toByteArray()); + + int setIndex = testString.indexOf("SET @debug_stmt_param"); + int equalsIndex = testString.indexOf("=", setIndex); + String paramName = testString.substring(setIndex + 4, equalsIndex); + + int usingIndex = testString.indexOf("USING " + paramName, equalsIndex); + + assertTrue(usingIndex != -1); + } finally { + System.setErr(oldErr); + + if (this.pstmt != null) { + this.pstmt.close(); + this.pstmt = null; + } + + if (testcaseGenCon != null) { + testcaseGenCon.close(); + } + + } + } + } + + /** + * Tests fix for BUG#11798 - Pstmt.setObject(...., Types.BOOLEAN) throws + * exception. + * + * @throws Exception + * if the test fails. + */ + public void testBug11798() throws Exception { + try { + this.pstmt = this.conn.prepareStatement("SELECT ?"); + this.pstmt.setObject(1, Boolean.TRUE, Types.BOOLEAN); + this.pstmt.setObject(1, new BigDecimal("1"), Types.BOOLEAN); + this.pstmt.setObject(1, "true", Types.BOOLEAN); + } finally { + if (this.pstmt != null) { + this.pstmt.close(); + this.pstmt = null; + } + } + } + + /** + * Tests fix for BUG#13255 - Reconnect during middle of executeBatch() + * should not happen. + * + * @throws Exception + * if the test fails. + */ + public void testBug13255() throws Exception { + + createTable("testBug13255", "(field_1 int)"); + + Properties props = new Properties(); + props.setProperty("autoReconnect", "true"); + + Connection reconnectConn = null; + Statement reconnectStmt = null; + PreparedStatement reconnectPStmt = null; + + try { + reconnectConn = getConnectionWithProps(props); + reconnectStmt = reconnectConn.createStatement(); + + String connectionId = getSingleIndexedValueWithQuery(reconnectConn, 1, "SELECT CONNECTION_ID()").toString(); + + reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (1)"); + reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (2)"); + reconnectStmt.addBatch("KILL " + connectionId); + + for (int i = 0; i < 100; i++) { + reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (" + i + ")"); + } + + try { + reconnectStmt.executeBatch(); + } catch (SQLException sqlEx) { + // We expect this...we killed the connection + } + + assertEquals(2, getRowCount("testBug13255")); + + this.stmt.executeUpdate("TRUNCATE TABLE testBug13255"); + + reconnectConn.close(); + + reconnectConn = getConnectionWithProps(props); + + connectionId = getSingleIndexedValueWithQuery(reconnectConn, 1, "SELECT CONNECTION_ID()").toString(); + + reconnectPStmt = reconnectConn.prepareStatement("INSERT INTO testBug13255 VALUES (?)"); + reconnectPStmt.setInt(1, 1); + reconnectPStmt.addBatch(); + reconnectPStmt.setInt(1, 2); + reconnectPStmt.addBatch(); + reconnectPStmt.addBatch("KILL " + connectionId); + + for (int i = 3; i < 100; i++) { + reconnectPStmt.setInt(1, i); + reconnectPStmt.addBatch(); + } + + try { + reconnectPStmt.executeBatch(); + } catch (SQLException sqlEx) { + // We expect this...we killed the connection + } + + assertEquals(2, getRowCount("testBug13255")); + + } finally { + if (reconnectStmt != null) { + reconnectStmt.close(); + } + + if (reconnectConn != null) { + reconnectConn.close(); + } + } + } + + /** + * Tests fix for BUG#15024 - Driver incorrectly closes streams passed as + * arguments to PreparedStatements. + * + * @throws Exception + * if the test fails. + */ + public void testBug15024() throws Exception { + createTable("testBug15024", "(field1 BLOB)"); + + try { + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug15024 VALUES (?)"); + testStreamsForBug15024(false, false); + + Properties props = new Properties(); + props.setProperty("useConfigs", "3-0-Compat"); + + Connection compatConn = null; + + try { + compatConn = getConnectionWithProps(props); + + this.pstmt = compatConn.prepareStatement("INSERT INTO testBug15024 VALUES (?)"); + testStreamsForBug15024(true, false); + } finally { + if (compatConn != null) { + compatConn.close(); + } + } + } finally { + if (this.pstmt != null) { + PreparedStatement toClose = this.pstmt; + this.pstmt = null; + + toClose.close(); + } + } + } + + /** + * PreparedStatement should call EscapeProcessor.escapeSQL? + * + * @throws Exception + * if the test fails + */ + public void testBug15141() throws Exception { + try { + createTable("testBug15141", "(field1 VARCHAR(32))"); + this.stmt.executeUpdate("INSERT INTO testBug15141 VALUES ('abc')"); + + this.pstmt = this.conn.prepareStatement("select {d '1997-05-24'} FROM testBug15141"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals("1997-05-24", this.rs.getString(1)); + this.rs.close(); + this.rs = null; + this.pstmt.close(); + this.pstmt = null; + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("select {d '1997-05-24'} FROM testBug15141"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals("1997-05-24", this.rs.getString(1)); + this.rs.close(); + this.rs = null; + this.pstmt.close(); + this.pstmt = null; + } finally { + if (this.rs != null) { + ResultSet toCloseRs = this.rs; + this.rs = null; + toCloseRs.close(); + } + + if (this.pstmt != null) { + PreparedStatement toClosePstmt = this.pstmt; + this.pstmt = null; + toClosePstmt.close(); + } + } + } + + /** + * Tests fix for BUG#18041 - Server-side prepared statements don't cause + * truncation exceptions to be thrown. + * + * @throws Exception + * if the test fails + */ + public void testBug18041() throws Exception { + if (versionMeetsMinimum(4, 1)) { + createTable("testBug18041", "(`a` tinyint(4) NOT NULL, `b` char(4) default NULL)"); + + Properties props = new Properties(); + props.setProperty("jdbcCompliantTruncation", "true"); + props.setProperty("useServerPrepStmts", "true"); + + Connection truncConn = null; + PreparedStatement stm = null; + + try { + truncConn = getConnectionWithProps(props); + + stm = truncConn.prepareStatement("insert into testBug18041 values (?,?)"); + stm.setInt(1, 1000); + stm.setString(2, "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"); + stm.executeUpdate(); + fail("Truncation exception should have been thrown"); + } catch (DataTruncation truncEx) { + // we expect this + } finally { + if (truncConn != null) { + truncConn.close(); + } + } + } + } + + private void testStreamsForBug15024(boolean shouldBeClosedStream, boolean shouldBeClosedReader) throws SQLException { + IsClosedInputStream bIn = new IsClosedInputStream(new byte[4]); + IsClosedReader readerIn = new IsClosedReader("abcdef"); + + this.pstmt.setBinaryStream(1, bIn, 4); + this.pstmt.execute(); + assertEquals(shouldBeClosedStream, bIn.isClosed()); + + this.pstmt.setCharacterStream(1, readerIn, 6); + this.pstmt.execute(); + assertEquals(shouldBeClosedReader, readerIn.isClosed()); + + this.pstmt.close(); + } + + class IsClosedReader extends StringReader { + + boolean isClosed = false; + + public IsClosedReader(String arg0) { + super(arg0); + } + + @Override + public void close() { + super.close(); + + this.isClosed = true; + } + + public boolean isClosed() { + return this.isClosed; + } + + } + + class IsClosedInputStream extends ByteArrayInputStream { + + boolean isClosed = false; + + public IsClosedInputStream(byte[] arg0, int arg1, int arg2) { + super(arg0, arg1, arg2); + } + + public IsClosedInputStream(byte[] arg0) { + super(arg0); + } + + @Override + public void close() throws IOException { + + super.close(); + this.isClosed = true; + } + + public boolean isClosed() { + return this.isClosed; + } + } + + /** + * Tests fix for BUG#1774 -- Truncated words after double quote + * + * @throws Exception + * if the test fails. + */ + public void testBug1774() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1774"); + this.stmt.executeUpdate("CREATE TABLE testBug1774 (field1 VARCHAR(255))"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug1774 VALUES (?)"); + + String testString = "The word contains \" character"; + + pStmt.setString(1, testString); + pStmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT * FROM testBug1774"); + this.rs.next(); + assertEquals(this.rs.getString(1), testString); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1774"); + } + } + + /** + * Tests fix for BUG#1901 -- PreparedStatement.setObject(int, Object, int, + * int) doesn't support CLOB or BLOB types. + * + * @throws Exception + * if this test fails for any reason + */ + public void testBug1901() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1901"); + this.stmt.executeUpdate("CREATE TABLE testBug1901 (field1 VARCHAR(255))"); + this.stmt.executeUpdate("INSERT INTO testBug1901 VALUES ('aaa')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug1901"); + this.rs.next(); + + Clob valueAsClob = this.rs.getClob(1); + Blob valueAsBlob = this.rs.getBlob(1); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug1901 VALUES (?)"); + pStmt.setObject(1, valueAsClob, java.sql.Types.CLOB, 0); + pStmt.executeUpdate(); + pStmt.setObject(1, valueAsBlob, java.sql.Types.BLOB, 0); + pStmt.executeUpdate(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1901"); + } + } + + /** + * Test fix for BUG#1933 -- Driver property 'maxRows' has no effect. + * + * @throws Exception + * if the test fails. + */ + public void testBug1933() throws Exception { + if (versionMeetsMinimum(4, 0)) { + Connection maxRowsConn = null; + PreparedStatement maxRowsPrepStmt = null; + Statement maxRowsStmt = null; + + try { + Properties props = new Properties(); + + props.setProperty("maxRows", "1"); + + maxRowsConn = getConnectionWithProps(props); + + maxRowsStmt = maxRowsConn.createStatement(); + + assertTrue(maxRowsStmt.getMaxRows() == 1); + + this.rs = maxRowsStmt.executeQuery("SELECT 1 UNION SELECT 2"); + + this.rs.next(); + + maxRowsPrepStmt = maxRowsConn.prepareStatement("SELECT 1 UNION SELECT 2"); + + assertTrue(maxRowsPrepStmt.getMaxRows() == 1); + + this.rs = maxRowsPrepStmt.executeQuery(); + + this.rs.next(); + + assertTrue(!this.rs.next()); + + props.setProperty("useServerPrepStmts", "false"); + + maxRowsConn = getConnectionWithProps(props); + + maxRowsPrepStmt = maxRowsConn.prepareStatement("SELECT 1 UNION SELECT 2"); + + assertTrue(maxRowsPrepStmt.getMaxRows() == 1); + + this.rs = maxRowsPrepStmt.executeQuery(); + + this.rs.next(); + + assertTrue(!this.rs.next()); + } finally { + if (maxRowsConn != null) { + maxRowsConn.close(); + } + } + } + } + + /** + * Tests the fix for BUG#1934 -- prepareStatement dies silently when + * encountering Statement.RETURN_GENERATED_KEY + * + * @throws Exception + * if the test fails + */ + public void testBug1934() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1934"); + this.stmt.executeUpdate("CREATE TABLE testBug1934 (field1 INT)"); + + System.out.println("Before prepareStatement()"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug1934 VALUES (?)", java.sql.Statement.RETURN_GENERATED_KEYS); + + assertTrue(this.pstmt != null); + + System.out.println("After prepareStatement() - " + this.pstmt); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1934"); + } + } + + /** + * Tests fix for BUG#1958 - Improper bounds checking on + * PreparedStatement.setFoo(). + * + * @throws Exception + * if the test fails. + */ + public void testBug1958() throws Exception { + PreparedStatement pStmt = null; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1958"); + this.stmt.executeUpdate("CREATE TABLE testBug1958 (field1 int)"); + + pStmt = this.conn.prepareStatement("SELECT * FROM testBug1958 WHERE field1 IN (?, ?, ?)"); + + try { + pStmt.setInt(4, 1); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + } finally { + if (pStmt != null) { + pStmt.close(); + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1958"); + } + } + + /** + * Tests the fix for BUG#2606, server-side prepared statements not returning + * datatype YEAR correctly. + * + * @throws Exception + * if the test fails. + */ + public void testBug2606() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2606"); + this.stmt.executeUpdate("CREATE TABLE testBug2606(year_field YEAR)"); + this.stmt.executeUpdate("INSERT INTO testBug2606 VALUES (2004)"); + + PreparedStatement yrPstmt = this.conn.prepareStatement("SELECT year_field FROM testBug2606"); + + this.rs = yrPstmt.executeQuery(); + + assertTrue(this.rs.next()); + + assertEquals(2004, this.rs.getInt(1)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2606"); + } + } + + /** + * Tests the fix for BUG#2671, nulls encoded incorrectly in server-side prepared statements. + * + * @throws Exception + * if an error occurs. + */ + public void testBug2671() throws Exception { + if (versionMeetsMinimum(4, 1)) { + createTable("test3", + "(`field1` int(8) NOT NULL auto_increment, `field2` int(8) unsigned zerofill default NULL," + + " `field3` varchar(30) binary NOT NULL default '', `field4` varchar(100) default NULL, `field5` datetime NULL default NULL," + + " PRIMARY KEY (`field1`), UNIQUE KEY `unq_id` (`field2`), UNIQUE KEY (`field3`)) CHARACTER SET utf8", + "InnoDB"); + + this.stmt.executeUpdate("insert into test3 (field1, field3, field4) values (1, 'blewis', 'Bob Lewis')"); + + String query = "UPDATE test3 SET field2=?, field3=?, field4=?, field5=? WHERE field1 = ?"; + + java.sql.Date mydate = null; + + this.pstmt = this.conn.prepareStatement(query); + + this.pstmt.setInt(1, 13); + this.pstmt.setString(2, "abc"); + this.pstmt.setString(3, "def"); + this.pstmt.setDate(4, mydate); + this.pstmt.setInt(5, 1); + + int retval = this.pstmt.executeUpdate(); + assertEquals(1, retval); + } + } + + /** + * Tests fix for BUG#3103 -- java.util.Date not accepted as parameter to + * PreparedStatement.setObject(). + * + * @throws Exception + * if the test fails + * + * @deprecated uses deprecated methods of Date class + */ + @Deprecated + public void testBug3103() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3103"); + + if (versionMeetsMinimum(5, 6, 4)) { + this.stmt.executeUpdate("CREATE TABLE testBug3103 (field1 DATETIME(3))"); + } else { + this.stmt.executeUpdate("CREATE TABLE testBug3103 (field1 DATETIME)"); + } + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug3103 VALUES (?)"); + + java.util.Date utilDate = new java.util.Date(); + + pStmt.setObject(1, utilDate); + pStmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug3103"); + this.rs.next(); + + java.util.Date retrUtilDate = new java.util.Date(this.rs.getTimestamp(1).getTime()); + + // We can only compare on the day/month/year hour/minute/second interval, because the timestamp has added milliseconds to the internal date... + assertTrue("Dates not equal", + (utilDate.getMonth() == retrUtilDate.getMonth()) && (utilDate.getDate() == retrUtilDate.getDate()) + && (utilDate.getYear() == retrUtilDate.getYear()) && (utilDate.getHours() == retrUtilDate.getHours()) + && (utilDate.getMinutes() == retrUtilDate.getMinutes()) && (utilDate.getSeconds() == retrUtilDate.getSeconds())); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3103"); + } + } + + /** + * Tests fix for BUG#3520 + * + * @throws Exception + * ... + */ + public void testBug3520() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS t"); + this.stmt.executeUpdate("CREATE TABLE t (s1 int,primary key (s1))"); + this.stmt.executeUpdate("INSERT INTO t VALUES (1)"); + this.stmt.executeUpdate("INSERT INTO t VALUES (1)"); + } catch (SQLException sqlEx) { + System.out.println(sqlEx.getSQLState()); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS t"); + } + } + + /** + * Test fix for BUG#3557 -- UpdatableResultSet not picking up default values + * + * @throws Exception + * if test fails. + */ + public void testBug3557() throws Exception { + boolean populateDefaults = ((com.mysql.jdbc.ConnectionProperties) this.conn).getPopulateInsertRowWithDefaultValues(); + + try { + ((com.mysql.jdbc.ConnectionProperties) this.conn).setPopulateInsertRowWithDefaultValues(true); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557"); + + this.stmt.executeUpdate( + "CREATE TABLE testBug3557 (`a` varchar(255) NOT NULL default 'XYZ', `b` varchar(255) default '123', PRIMARY KEY (`a`(100)))"); + + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = updStmt.executeQuery("SELECT * FROM testBug3557"); + + assertTrue(this.rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE); + + this.rs.moveToInsertRow(); + + assertEquals("XYZ", this.rs.getObject(1)); + assertEquals("123", this.rs.getObject(2)); + } finally { + ((com.mysql.jdbc.ConnectionProperties) this.conn).setPopulateInsertRowWithDefaultValues(populateDefaults); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557"); + } + } + + /** + * Tests fix for BUG#3620 -- Timezone not respected correctly. + * + * @throws SQLException + * if the test fails. + */ + public void testBug3620() throws SQLException { + if (isRunningOnJRockit()) { + // bug with their timezones + return; + } + + final long epsillon = 3000; // allow 3 seconds time difference + + TimeZone defaultTimeZone = TimeZone.getDefault(); + try { + TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); + + createTable("testBug3620", "(field1 TIMESTAMP) ENGINE=InnoDB"); + + Properties props = new Properties(); + props.put("cacheDefaultTimezone", "false"); + + Connection connNoTz = getConnectionWithProps(props); + PreparedStatement tsPstmt = connNoTz.prepareStatement("INSERT INTO testBug3620 VALUES (?)"); + + Calendar pointInTime = Calendar.getInstance(); + pointInTime.set(2004, 02, 29, 10, 0, 0); + long pointInTimeOffset = pointInTime.getTimeZone().getRawOffset(); + Timestamp ts = new Timestamp(pointInTime.getTime().getTime()); + + tsPstmt.setTimestamp(1, ts); + tsPstmt.executeUpdate(); + + this.rs = connNoTz.createStatement().executeQuery("SELECT field1 FROM testBug3620"); + this.rs.next(); + String tsValueAsString = new String(this.rs.getBytes(1)); + Timestamp tsValueAsTimestamp = this.rs.getTimestamp(1); + System.out.println("Timestamp as String, inserted with no calendar: " + tsValueAsString.toString()); + System.out.println("Timestamp as Timestamp, inserted with no calendar: " + tsValueAsTimestamp); + + connNoTz.createStatement().executeUpdate("DELETE FROM testBug3620"); + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + + props.put("useTimezone", "true"); + props.put("serverTimezone", "UTC"); + + Connection connWithTz = getConnectionWithProps(props); + Statement tsStmt = connWithTz.createStatement(); + tsPstmt = connWithTz.prepareStatement("INSERT INTO testBug3620 VALUES (?)"); + + tsPstmt.setTimestamp(1, ts, cal); + tsPstmt.executeUpdate(); + + this.rs = connNoTz.createStatement().executeQuery("SELECT field1 FROM testBug3620"); + this.rs.next(); + tsValueAsString = new String(this.rs.getBytes(1)); + tsValueAsTimestamp = this.rs.getTimestamp(1); + System.out.println("Timestamp as String, inserted with UTC calendar: " + tsValueAsString.toString()); + System.out.println("Timestamp as Timestamp, inserted with UTC calendar: " + tsValueAsTimestamp); + + this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620"); + this.rs.next(); + Timestamp tsValueUTC = this.rs.getTimestamp(1, cal); + System.out.println("Timestamp specifying UTC calendar from statement: " + tsValueUTC.toString()); + + // We use this testcase with other vendors, JDBC spec requires result set fields can only be read once, although MySQL doesn't require this ;) + this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620"); + this.rs.next(); + Timestamp tsValueStmtNoCal = this.rs.getTimestamp(1); + System.out.println("Timestamp specifying no calendar from statement: " + tsValueStmtNoCal.toString()); + + PreparedStatement tsPstmtRetr = connWithTz.prepareStatement("SELECT field1 FROM testBug3620"); + this.rs = tsPstmtRetr.executeQuery(); + this.rs.next(); + Timestamp tsValuePstmtUTC = this.rs.getTimestamp(1, cal); + System.out.println("Timestamp specifying UTC calendar from prepared statement: " + tsValuePstmtUTC.toString()); + + // We use this testcase with other vendors, JDBC spec requires result set fields can only be read once, although MySQL doesn't require this ;) + this.rs = tsPstmtRetr.executeQuery(); + this.rs.next(); + Timestamp tsValuePstmtNoCal = this.rs.getTimestamp(1); + System.out.println("Timestamp specifying no calendar from prepared statement: " + tsValuePstmtNoCal.toString()); + + long stmtDeltaTWithCal = (tsValueStmtNoCal.getTime() - ts.getTime()); + long deltaOrig = Math.abs(stmtDeltaTWithCal - pointInTimeOffset); + assertTrue("Difference between original timestamp and timestamp retrieved using java.sql.Statement " + + "set in database using UTC calendar is not ~= " + epsillon + " it is actually " + deltaOrig, (deltaOrig < epsillon)); + + long pStmtDeltaTWithCal = (tsValuePstmtNoCal.getTime() - ts.getTime()); + deltaOrig = Math.abs(pStmtDeltaTWithCal - pointInTimeOffset); + assertTrue("Difference between original timestamp and timestamp retrieved using java.sql.PreparedStatement " + + "set in database using UTC calendar is not ~= " + epsillon + ", it is actually " + deltaOrig, (deltaOrig < epsillon)); + + System.out.println("Difference between original ts and ts with no calendar: " + (tsValuePstmtNoCal.getTime() - ts.getTime()) + ", offset should be " + + pointInTimeOffset); + } finally { + TimeZone.setDefault(defaultTimeZone); + } + } + + /** + * Tests that DataTruncation is thrown when data is truncated. + * + * @throws Exception + * if the test fails. + */ + public void testBug3697() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3697"); + this.stmt.executeUpdate("CREATE TABLE testBug3697 (field1 VARCHAR(255))"); + + StringBuilder updateBuf = new StringBuilder("INSERT INTO testBug3697 VALUES ('"); + + for (int i = 0; i < 512; i++) { + updateBuf.append("A"); + } + + updateBuf.append("')"); + + try { + this.stmt.executeUpdate(updateBuf.toString()); + } catch (DataTruncation dtEx) { + // This is an expected exception.... + } + + SQLWarning warningChain = this.stmt.getWarnings(); + + System.out.println(warningChain); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3697"); + } + } + + /** + * Tests fix for BUG#3804, data truncation on server should throw + * DataTruncation exception. + * + * @throws Exception + * if the test fails + */ + public void testBug3804() throws Exception { + if (versionMeetsMinimum(4, 1)) { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3804"); + this.stmt.executeUpdate("CREATE TABLE testBug3804 (field1 VARCHAR(5))"); + + boolean caughtTruncation = false; + + try { + this.stmt.executeUpdate("INSERT INTO testBug3804 VALUES ('1234567')"); + } catch (DataTruncation truncationEx) { + caughtTruncation = true; + System.out.println(truncationEx); + } + + assertTrue("Data truncation exception should've been thrown", caughtTruncation); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3804"); + } + } + } + + /** + * Tests BUG#3873 - PreparedStatement.executeBatch() not returning all + * generated keys (even though that's not JDBC compliant). + * + * @throws Exception + * if the test fails + */ + public void testBug3873() throws Exception { + PreparedStatement batchStmt = null; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3873"); + this.stmt.executeUpdate("CREATE TABLE testBug3873 (keyField INT NOT NULL PRIMARY KEY AUTO_INCREMENT, dataField VARCHAR(32))"); + batchStmt = this.conn.prepareStatement("INSERT INTO testBug3873 (dataField) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + batchStmt.setString(1, "abc"); + batchStmt.addBatch(); + batchStmt.setString(1, "def"); + batchStmt.addBatch(); + batchStmt.setString(1, "ghi"); + batchStmt.addBatch(); + + @SuppressWarnings("unused") + int[] updateCounts = batchStmt.executeBatch(); + + this.rs = batchStmt.getGeneratedKeys(); + + while (this.rs.next()) { + System.out.println(this.rs.getInt(1)); + } + + this.rs = batchStmt.getGeneratedKeys(); + assertTrue(this.rs.next()); + assertTrue(1 == this.rs.getInt(1)); + assertTrue(this.rs.next()); + assertTrue(2 == this.rs.getInt(1)); + assertTrue(this.rs.next()); + assertTrue(3 == this.rs.getInt(1)); + assertTrue(!this.rs.next()); + } finally { + if (batchStmt != null) { + batchStmt.close(); + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3873"); + } + } + + /** + * Tests fix for BUG#4119 -- misbehavior in a managed environment from + * MVCSoft JDO + * + * @throws Exception + * if the test fails. + */ + public void testBug4119() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4119"); + this.stmt.executeUpdate("CREATE TABLE `testBug4119` (`field1` varchar(255) NOT NULL default '', `field2` bigint(20) default NULL," + + "`field3` int(11) default NULL, `field4` datetime default NULL, `field5` varchar(75) default NULL," + + "`field6` varchar(75) default NULL, `field7` varchar(75) default NULL, `field8` datetime default NULL," + + " PRIMARY KEY (`field1`(100)))"); + + PreparedStatement pStmt = this.conn.prepareStatement( + "insert into testBug4119 (field2, field3, field4, field5, field6, field7, field8, field1) values (?, ?, ?, ?, ?, ?, ?, ?)"); + + pStmt.setString(1, "0"); + pStmt.setString(2, "0"); + pStmt.setTimestamp(3, new java.sql.Timestamp(System.currentTimeMillis())); + pStmt.setString(4, "ABC"); + pStmt.setString(5, "DEF"); + pStmt.setString(6, "AA"); + pStmt.setTimestamp(7, new java.sql.Timestamp(System.currentTimeMillis())); + pStmt.setString(8, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + pStmt.executeUpdate(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4119"); + } + } + + /** + * Tests fix for BUG#4311 - Error in JDBC retrieval of mediumint column when + * using prepared statements and binary result sets. + * + * @throws Exception + * if the test fails. + */ + public void testBug4311() throws Exception { + try { + int lowValue = -8388608; + int highValue = 8388607; + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4311"); + this.stmt.executeUpdate("CREATE TABLE testBug4311 (low MEDIUMINT, high MEDIUMINT)"); + this.stmt.executeUpdate("INSERT INTO testBug4311 VALUES (" + lowValue + ", " + highValue + ")"); + + PreparedStatement pStmt = this.conn.prepareStatement("SELECT low, high FROM testBug4311"); + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + assertTrue(this.rs.getInt(1) == lowValue); + assertTrue(this.rs.getInt(2) == highValue); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4311"); + } + } + + /** + * Tests fix for BUG#4510 -- Statement.getGeneratedKeys() fails when key > + * 32767 + * + * @throws Exception + * if the test fails + */ + public void testBug4510() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4510"); + this.stmt.executeUpdate("CREATE TABLE testBug4510 (field1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT, field2 VARCHAR(100))"); + this.stmt.executeUpdate("INSERT INTO testBug4510 (field1, field2) VALUES (32767, 'bar')"); + + PreparedStatement p = this.conn.prepareStatement("insert into testBug4510 (field2) values (?)", Statement.RETURN_GENERATED_KEYS); + + p.setString(1, "blah"); + + p.executeUpdate(); + + ResultSet genKeysRs = p.getGeneratedKeys(); + genKeysRs.next(); + System.out.println("Id: " + genKeysRs.getInt(1)); + genKeysRs.close(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4510"); + } + } + + /** + * Server doesn't accept everything as a server-side prepared statement, so + * by default we scan for stuff it can't handle. + * + * @throws SQLException + */ + public void testBug4718() throws SQLException { + if (versionMeetsMinimum(4, 1, 0) && ((com.mysql.jdbc.Connection) this.conn).getUseServerPreparedStmts()) { + this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT ?"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT 1"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + + this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT 1, ?"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4718"); + this.stmt.executeUpdate("CREATE TABLE testBug4718 (field1 char(32))"); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testBug4718 ADD INDEX (field1)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("SELECT 1"); + assertTrue(this.pstmt instanceof ServerPreparedStatement); + + this.pstmt = this.conn.prepareStatement("UPDATE testBug4718 SET field1=1"); + assertTrue(this.pstmt instanceof ServerPreparedStatement); + + this.pstmt = this.conn.prepareStatement("UPDATE testBug4718 SET field1=1 LIMIT 1"); + assertTrue(this.pstmt instanceof ServerPreparedStatement); + + this.pstmt = this.conn.prepareStatement("UPDATE testBug4718 SET field1=1 LIMIT ?"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("UPDATE testBug4718 SET field1='Will we ignore LIMIT ?,?'"); + assertTrue(this.pstmt instanceof ServerPreparedStatement); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4718"); + } + } + } + + /** + * Tests fix for BUG#5012 -- ServerPreparedStatements dealing with return of + * DECIMAL type don't work. + * + * @throws Exception + * if the test fails. + */ + public void testBug5012() throws Exception { + PreparedStatement pStmt = null; + String valueAsString = "12345.12"; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5012"); + this.stmt.executeUpdate("CREATE TABLE testBug5012(field1 DECIMAL(10,2))"); + this.stmt.executeUpdate("INSERT INTO testBug5012 VALUES (" + valueAsString + ")"); + + pStmt = this.conn.prepareStatement("SELECT field1 FROM testBug5012"); + this.rs = pStmt.executeQuery(); + assertTrue(this.rs.next()); + assertEquals(new BigDecimal(valueAsString), this.rs.getBigDecimal(1)); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5012"); + + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests fix for BUG#5133 -- PreparedStatement.toString() doesn't return + * correct value if no parameters are present in statement. + * + * @throws Exception + */ + public void testBug5133() throws Exception { + String query = "SELECT 1"; + String output = this.conn.prepareStatement(query).toString(); + System.out.println(output); + + assertTrue(output.indexOf(query) != -1); + } + + /** + * Tests for BUG#5191 -- PreparedStatement.executeQuery() gives + * OutOfMemoryError + * + * @throws Exception + * if the test fails. + */ + public void testBug5191() throws Exception { + PreparedStatement pStmt = null; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191Q"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191C"); + + this.stmt.executeUpdate("CREATE TABLE testBug5191Q (QuestionId int NOT NULL AUTO_INCREMENT, Text VARCHAR(200), PRIMARY KEY(QuestionId))"); + + this.stmt.executeUpdate("CREATE TABLE testBug5191C (CategoryId int, QuestionId int)"); + + String[] questions = new String[] { "What is your name?", "What is your quest?", "What is the airspeed velocity of an unladen swollow?", + "How many roads must a man walk?", "Where's the tea?", }; + + for (int i = 0; i < questions.length; i++) { + this.stmt.executeUpdate("INSERT INTO testBug5191Q(Text) VALUES (\"" + questions[i] + "\")"); + int catagory = (i < 3) ? 0 : i; + + this.stmt.executeUpdate("INSERT INTO testBug5191C (CategoryId, QuestionId) VALUES (" + catagory + ", " + i + ")"); + /* + * this.stmt.executeUpdate("INSERT INTO testBug5191C" + + * "(CategoryId, QuestionId) VALUES (" + catagory + ", (SELECT + * testBug5191Q.QuestionId" + " FROM testBug5191Q " + "WHERE + * testBug5191Q.Text LIKE '" + questions[i] + "'))"); + */ + } + + pStmt = this.conn.prepareStatement( + "SELECT qc.QuestionId, q.Text FROM testBug5191Q q, testBug5191C qc WHERE qc.CategoryId = ? AND q.QuestionId = qc.QuestionId"); + + int catId = 0; + for (int i = 0; i < 100; i++) { + execQueryBug5191(pStmt, catId); + } + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191Q"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191C"); + + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests for BUG#5235, ClassCastException on all-zero date field when + * zeroDatetimeBehavior is 'convertToNull'. + * + * @throws Exception + * if the test fails. + */ + public void testBug5235() throws Exception { + Properties props = new Properties(); + props.setProperty("zeroDateTimeBehavior", "convertToNull"); + if (versionMeetsMinimum(5, 7, 4)) { + props.put("jdbcCompliantTruncation", "false"); + } + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + + Connection convertToNullConn = getConnectionWithProps(props); + Statement convertToNullStmt = convertToNullConn.createStatement(); + try { + convertToNullStmt.executeUpdate("DROP TABLE IF EXISTS testBug5235"); + convertToNullStmt.executeUpdate("CREATE TABLE testBug5235(field1 DATE)"); + convertToNullStmt.executeUpdate("INSERT INTO testBug5235 (field1) VALUES ('0000-00-00')"); + + PreparedStatement ps = convertToNullConn.prepareStatement("SELECT field1 FROM testBug5235"); + this.rs = ps.executeQuery(); + + if (this.rs.next()) { + assertNull(this.rs.getObject("field1")); + } + } finally { + convertToNullStmt.executeUpdate("DROP TABLE IF EXISTS testBug5235"); + } + } + + public void testBug5450() throws Exception { + if (versionMeetsMinimum(4, 1)) { + String table = "testBug5450"; + String column = "policyname"; + + try { + Properties props = new Properties(); + props.setProperty("characterEncoding", "utf-8"); + + Connection utf8Conn = getConnectionWithProps(props); + Statement utfStmt = utf8Conn.createStatement(); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS " + table); + + this.stmt.executeUpdate("CREATE TABLE " + table + "(policyid int NOT NULL AUTO_INCREMENT, " + column + " VARCHAR(200), " + + "PRIMARY KEY(policyid)) DEFAULT CHARACTER SET utf8"); + + String pname0 = "inserted \uac00 - foo - \u4e00"; + + utfStmt.executeUpdate("INSERT INTO " + table + "(" + column + ") VALUES (\"" + pname0 + "\")"); + + this.rs = utfStmt.executeQuery("SELECT " + column + " FROM " + table); + + this.rs.first(); + String pname1 = this.rs.getString(column); + + assertEquals(pname0, pname1); + byte[] bytes = this.rs.getBytes(column); + + String pname2 = new String(bytes, "utf-8"); + assertEquals(pname1, pname2); + + utfStmt.executeUpdate("delete from " + table + " where " + column + " like 'insert%'"); + + PreparedStatement s1 = utf8Conn.prepareStatement("insert into " + table + "(" + column + ") values (?)"); + + s1.setString(1, pname0); + s1.executeUpdate(); + + String byteesque = "byte " + pname0; + byte[] newbytes = byteesque.getBytes("utf-8"); + + s1.setBytes(1, newbytes); + s1.executeUpdate(); + + this.rs = utfStmt.executeQuery("select " + column + " from " + table + " where " + column + " like 'insert%'"); + this.rs.first(); + String pname3 = this.rs.getString(column); + assertEquals(pname0, pname3); + + this.rs = utfStmt.executeQuery("select " + column + " from " + table + " where " + column + " like 'byte insert%'"); + this.rs.first(); + + String pname4 = this.rs.getString(column); + assertEquals(byteesque, pname4); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS " + table); + } + } + } + + public void testBug5510() throws Exception { + // This is a server bug that should be fixed by 4.1.6 + if (versionMeetsMinimum(4, 1, 6)) { + createTable("`testBug5510`", + "(`a` bigint(20) NOT NULL auto_increment, `b` varchar(64) default NULL, `c` varchar(64) default NULL," + + "`d` varchar(255) default NULL, `e` int(11) default NULL, `f` varchar(32) default NULL, `g` varchar(32) default NULL," + + "`h` varchar(80) default NULL, `i` varchar(255) default NULL, `j` varchar(255) default NULL, `k` varchar(255) default NULL," + + "`l` varchar(32) default NULL, `m` varchar(32) default NULL, `n` timestamp NOT NULL default CURRENT_TIMESTAMP on update" + + " CURRENT_TIMESTAMP, `o` int(11) default NULL, `p` int(11) default NULL, PRIMARY KEY (`a`)) DEFAULT CHARSET=latin1", + "InnoDB "); + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testBug5510 (a) VALUES (?)"); + pStmt.setNull(1, 0); + pStmt.executeUpdate(); + } + } + + /** + * Tests fix for BUG#5874, timezone correction goes in wrong 'direction' (when useTimezone=true and server timezone differs from client timezone). + * + * @throws Exception + * if the test fails. + */ + public void testBug5874() throws Exception { + TimeZone defaultTimezone = TimeZone.getDefault(); + + try { + String clientTimezoneName = "America/Los_Angeles"; + String serverTimezoneName = "America/Chicago"; + + TimeZone.setDefault(TimeZone.getTimeZone(clientTimezoneName)); + + long clientTimezoneOffsetMillis = TimeZone.getDefault().getRawOffset(); + long serverTimezoneOffsetMillis = TimeZone.getTimeZone(serverTimezoneName).getRawOffset(); + + long offsetDifference = clientTimezoneOffsetMillis - serverTimezoneOffsetMillis; + + SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + + long pointInTime = timestampFormat.parse("2004-10-04 09:19:00").getTime(); + + Properties props = new Properties(); + props.put("useTimezone", "true"); + props.put("serverTimezone", serverTimezoneName); + props.put("cacheDefaultTimezone", "false"); + + Connection tzConn = getConnectionWithProps(props); + Statement tzStmt = tzConn.createStatement(); + createTable("testBug5874", "(tstamp DATETIME, t TIME)"); + + PreparedStatement tsPstmt = tzConn.prepareStatement("INSERT INTO testBug5874 VALUES (?, ?)"); + + tsPstmt.setTimestamp(1, new Timestamp(pointInTime)); + tsPstmt.setTime(2, new Time(pointInTime)); + tsPstmt.executeUpdate(); + + this.rs = tzStmt.executeQuery("SELECT * from testBug5874"); + + while (this.rs.next()) { // Driver now converts/checks DATE/TIME/TIMESTAMP/DATETIME types when calling getString()... + String retrTimestampString = new String(this.rs.getBytes(1)); + Timestamp retrTimestamp = this.rs.getTimestamp(1); + + java.util.Date timestampOnServer = timestampFormat.parse(retrTimestampString); + + long retrievedOffsetForTimestamp = retrTimestamp.getTime() - timestampOnServer.getTime(); + + assertEquals("Original timestamp and timestamp retrieved using client timezone are not the same", offsetDifference, + retrievedOffsetForTimestamp); + + String retrTimeString = new String(this.rs.getBytes(2)); + Time retrTime = this.rs.getTime(2); + + java.util.Date timeOnServerAsDate = timeFormat.parse(retrTimeString); + Time timeOnServer = new Time(timeOnServerAsDate.getTime()); + + long retrievedOffsetForTime = retrTime.getTime() - timeOnServer.getTime(); + + assertEquals("Original time and time retrieved using client timezone are not the same", offsetDifference, retrievedOffsetForTime); + } + + tzConn.close(); + } finally { + TimeZone.setDefault(defaultTimezone); + } + } + + public void testBug6823() throws SQLException { + innerBug6823(true); + innerBug6823(false); + } + + public void testBug7461() throws Exception { + String tableName = "testBug7461"; + + try { + createTable(tableName, "(field1 varchar(4))"); + File tempFile = File.createTempFile("mysql-test", ".txt"); + tempFile.deleteOnExit(); + + FileOutputStream fOut = new FileOutputStream(tempFile); + fOut.write("abcdefghijklmnop".getBytes()); + fOut.close(); + + try { + this.stmt.executeQuery("LOAD DATA LOCAL INFILE '" + tempFile.toString() + "' INTO TABLE " + tableName); + } catch (SQLException sqlEx) { + this.stmt.getWarnings(); + } + + } finally { + dropTable(tableName); + } + + } + + public void testBug8181() throws Exception { + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8181"); + this.stmt.executeUpdate("CREATE TABLE testBug8181(col1 VARCHAR(20),col2 INT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug8181(col1,col2) VALUES(?,?)"); + + for (int i = 0; i < 20; i++) { + this.pstmt.setString(1, "Test " + i); + this.pstmt.setInt(2, i); + this.pstmt.addBatch(); + } + + this.pstmt.executeBatch(); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8181"); + + if (this.pstmt != null) { + this.pstmt.close(); + } + } + } + + /** + * Tests fix for BUG#8487 - PreparedStatements not creating streaming result + * sets. + * + * @throws Exception + * if the test fails. + */ + public void testBug8487() throws Exception { + try { + this.pstmt = this.conn.prepareStatement("SELECT 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + this.pstmt.setFetchSize(Integer.MIN_VALUE); + this.rs = this.pstmt.executeQuery(); + try { + this.rs = this.conn.createStatement().executeQuery("SELECT 2"); + fail("Should have caught a streaming exception here"); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage() != null && sqlEx.getMessage().indexOf("Streaming") != -1); + } + + } finally { + if (this.rs != null) { + while (this.rs.next()) { + } + + this.rs.close(); + } + + if (this.pstmt != null) { + this.pstmt.close(); + } + } + } + + /** + * Tests multiple statement support with fix for BUG#9704. + * + * @throws Exception + */ + public void testBug9704() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Connection multiStmtConn = null; + Statement multiStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("allowMultiQueries", "true"); + + multiStmtConn = getConnectionWithProps(props); + + multiStmt = multiStmtConn.createStatement(); + + multiStmt.executeUpdate("DROP TABLE IF EXISTS testMultiStatements"); + multiStmt.executeUpdate("CREATE TABLE testMultiStatements (field1 VARCHAR(255), field2 INT, field3 DOUBLE)"); + multiStmt.executeUpdate("INSERT INTO testMultiStatements VALUES ('abcd', 1, 2)"); + + multiStmt.execute("SELECT field1 FROM testMultiStatements WHERE field1='abcd'; UPDATE testMultiStatements SET field3=3;" + + "SELECT field3 FROM testMultiStatements WHERE field3=3"); + + this.rs = multiStmt.getResultSet(); + + assertTrue(this.rs.next()); + + assertTrue("abcd".equals(this.rs.getString(1))); + this.rs.close(); + + // Next should be an update count... + assertTrue(!multiStmt.getMoreResults()); + + assertTrue("Update count was " + multiStmt.getUpdateCount() + ", expected 1", multiStmt.getUpdateCount() == 1); + + assertTrue(multiStmt.getMoreResults()); + + this.rs = multiStmt.getResultSet(); + + assertTrue(this.rs.next()); + + assertTrue(this.rs.getDouble(1) == 3); + + // End of multi results + assertTrue(!multiStmt.getMoreResults()); + assertTrue(multiStmt.getUpdateCount() == -1); + } finally { + if (multiStmt != null) { + multiStmt.executeUpdate("DROP TABLE IF EXISTS testMultiStatements"); + + multiStmt.close(); + } + + if (multiStmtConn != null) { + multiStmtConn.close(); + } + } + } + } + + /** + * Tests that you can close a statement twice without an NPE. + * + * @throws Exception + * if an error occurs. + */ + public void testCloseTwice() throws Exception { + Statement closeMe = this.conn.createStatement(); + closeMe.close(); + closeMe.close(); + } + + public void testCsc4194() throws Exception { + try { + "".getBytes("Windows-31J"); + } catch (UnsupportedEncodingException ex) { + return; // test doesn't work on this platform + } + + Connection sjisConn = null; + Connection windows31JConn = null; + + try { + String tableNameText = "testCsc4194Text"; + String tableNameBlob = "testCsc4194Blob"; + + createTable(tableNameBlob, "(field1 BLOB)"); + String charset = ""; + + if (versionMeetsMinimum(5, 0, 3) || versionMeetsMinimum(4, 1, 12)) { + charset = " CHARACTER SET cp932"; + } else if (versionMeetsMinimum(4, 1, 0)) { + charset = " CHARACTER SET sjis"; + } + + createTable(tableNameText, "(field1 TEXT)" + charset); + + Properties windows31JProps = new Properties(); + windows31JProps.setProperty("useUnicode", "true"); + windows31JProps.setProperty("characterEncoding", "Windows-31J"); + + windows31JConn = getConnectionWithProps(windows31JProps); + testCsc4194InsertCheckBlob(windows31JConn, tableNameBlob); + + if (versionMeetsMinimum(4, 1, 0)) { + testCsc4194InsertCheckText(windows31JConn, tableNameText, "Windows-31J"); + } + + Properties sjisProps = new Properties(); + sjisProps.setProperty("useUnicode", "true"); + sjisProps.setProperty("characterEncoding", "sjis"); + + sjisConn = getConnectionWithProps(sjisProps); + testCsc4194InsertCheckBlob(sjisConn, tableNameBlob); + + if (versionMeetsMinimum(5, 0, 3)) { + testCsc4194InsertCheckText(sjisConn, tableNameText, "Windows-31J"); + } + + } finally { + + if (windows31JConn != null) { + windows31JConn.close(); + } + + if (sjisConn != null) { + sjisConn.close(); + } + } + } + + private void testCsc4194InsertCheckBlob(Connection c, String tableName) throws Exception { + byte[] bArray = new byte[] { (byte) 0xac, (byte) 0xed, (byte) 0x00, (byte) 0x05 }; + + PreparedStatement testStmt = c.prepareStatement("INSERT INTO " + tableName + " VALUES (?)"); + testStmt.setBytes(1, bArray); + testStmt.executeUpdate(); + + this.rs = c.createStatement().executeQuery("SELECT field1 FROM " + tableName); + assertTrue(this.rs.next()); + assertEquals(getByteArrayString(bArray), getByteArrayString(this.rs.getBytes(1))); + this.rs.close(); + } + + private void testCsc4194InsertCheckText(Connection c, String tableName, String encoding) throws Exception { + byte[] kabuInShiftJIS = { (byte) 0x87, // a double-byte charater("kabu") in Shift JIS + (byte) 0x8a, }; + + String expected = new String(kabuInShiftJIS, encoding); + PreparedStatement testStmt = c.prepareStatement("INSERT INTO " + tableName + " VALUES (?)"); + testStmt.setString(1, expected); + testStmt.executeUpdate(); + + this.rs = c.createStatement().executeQuery("SELECT field1 FROM " + tableName); + assertTrue(this.rs.next()); + assertEquals(expected, this.rs.getString(1)); + this.rs.close(); + } + + /** + * Tests all forms of statements influencing getGeneratedKeys(). + * + * @throws Exception + * if the test fails. + */ + public void testGetGeneratedKeysAllCases() throws Exception { + System.out.println("Using Statement.executeUpdate()\n"); + + try { + createGGKTables(); + + // Do the tests + for (int i = 0; i < tests.length; i++) { + doGGKTestStatement(tests[i], true); + } + } finally { + dropGGKTables(); + } + + nextID = 1; + count = 0; + + System.out.println("Using Statement.execute()\n"); + + try { + createGGKTables(); + + // Do the tests + for (int i = 0; i < tests.length; i++) { + doGGKTestStatement(tests[i], false); + } + } finally { + dropGGKTables(); + } + + nextID = 1; + count = 0; + + System.out.println("Using PreparedStatement.executeUpdate()\n"); + + try { + createGGKTables(); + + // Do the tests + for (int i = 0; i < tests.length; i++) { + doGGKTestPreparedStatement(tests[i], true); + } + } finally { + dropGGKTables(); + } + + nextID = 1; + count = 0; + + System.out.println("Using PreparedStatement.execute()\n"); + + try { + createGGKTables(); + + // Do the tests + for (int i = 0; i < tests.length; i++) { + doGGKTestPreparedStatement(tests[i], false); + } + } finally { + dropGGKTables(); + } + } + + /** + * Tests that max_rows and 'limit' don't cause exceptions to be thrown. + * + * @throws Exception + * if the test fails. + */ + public void testLimitAndMaxRows() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testMaxRowsAndLimit"); + this.stmt.executeUpdate("CREATE TABLE testMaxRowsAndLimit(limitField INT)"); + + for (int i = 0; i < 500; i++) { + this.stmt.executeUpdate("INSERT INTO testMaxRowsAndLimit VALUES (" + i + ")"); + } + + this.stmt.setMaxRows(250); + this.rs = this.stmt.executeQuery("SELECT limitField FROM testMaxRowsAndLimit"); + } finally { + this.stmt.setMaxRows(0); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testMaxRowsAndLimit"); + } + } + + /* + * public void testBug9595() throws Exception { double[] vals = new double[] + * {52.21, 52.22, 52.23, 52.24}; + * + * createTable("testBug9595", "(field1 DECIMAL(10,2), sortField INT)"); + * + * this.pstmt = this.conn.prepareStatement("INSERT INTO testBug9595 VALUES + * (?, ?)"); // Try setting as doubles for (int i = 0; i < vals.length; i++) + * { this.pstmt.setDouble(1, vals[i]); this.pstmt.setInt(2, i); + * this.pstmt.executeUpdate(); } + * + * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug9595 + * ORDER BY sortField"); this.rs = this.pstmt.executeQuery(); + * + * int i = 0; + * + * while (this.rs.next()) { double valToTest = vals[i++]; + * + * assertEquals(this.rs.getDouble(1), valToTest, 0.001); + * assertEquals(this.rs.getBigDecimal(1).doubleValue(), valToTest, 0.001); } + * + * this.pstmt = this.conn.prepareStatement("INSERT INTO testBug9595 VALUES + * (?, ?)"); + * + * this.stmt.executeUpdate("TRUNCATE TABLE testBug9595"); // Now, as + * BigDecimals for (i = 0; i < vals.length; i++) { BigDecimal foo = new + * BigDecimal(vals[i]); + * + * this.pstmt.setObject(1, foo, Types.DECIMAL, 2); this.pstmt.setInt(2, i); + * this.pstmt.executeUpdate(); } + * + * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug9595 + * ORDER BY sortField"); this.rs = this.pstmt.executeQuery(); + * + * i = 0; + * + * while (this.rs.next()) { double valToTest = vals[i++]; + * System.out.println(this.rs.getString(1)); + * assertEquals(this.rs.getDouble(1), valToTest, 0.001); + * assertEquals(this.rs.getBigDecimal(1).doubleValue(), valToTest, 0.001); } + * } + */ + + /** + * Tests that 'LOAD DATA LOCAL INFILE' works + * + * @throws Exception + * if any errors occur + */ + public void testLoadData() throws Exception { + try { + //int maxAllowedPacket = 1048576; + + this.stmt.executeUpdate("DROP TABLE IF EXISTS loadDataRegress"); + this.stmt.executeUpdate("CREATE TABLE loadDataRegress (field1 int, field2 int)"); + + File tempFile = File.createTempFile("mysql", ".txt"); + + // tempFile.deleteOnExit(); + System.out.println(tempFile); + + Writer out = new FileWriter(tempFile); + + int localCount = 0; + int rowCount = 128; // maxAllowedPacket * 4; + + for (int i = 0; i < rowCount; i++) { + out.write((localCount++) + "\t" + (localCount++) + "\n"); + } + + out.close(); + + StringBuilder fileNameBuf = null; + + if (File.separatorChar == '\\') { + fileNameBuf = new StringBuilder(); + + String fileName = tempFile.getAbsolutePath(); + int fileNameLength = fileName.length(); + + for (int i = 0; i < fileNameLength; i++) { + char c = fileName.charAt(i); + + if (c == '\\') { + fileNameBuf.append("/"); + } else { + fileNameBuf.append(c); + } + } + } else { + fileNameBuf = new StringBuilder(tempFile.getAbsolutePath()); + } + + int updateCount = this.stmt.executeUpdate("LOAD DATA LOCAL INFILE '" + fileNameBuf.toString() + "' INTO TABLE loadDataRegress CHARACTER SET " + + CharsetMapping.getMysqlCharsetForJavaEncoding(((MySQLConnection) this.conn).getEncoding(), (com.mysql.jdbc.Connection) this.conn)); + assertTrue(updateCount == rowCount); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS loadDataRegress"); + } + } + + public void testNullClob() throws Exception { + createTable("testNullClob", "(field1 TEXT NULL)"); + + PreparedStatement pStmt = null; + + try { + pStmt = this.conn.prepareStatement("INSERT INTO testNullClob VALUES (?)"); + pStmt.setClob(1, null); + pStmt.executeUpdate(); + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests fix for BUG#1658 + * + * @throws Exception + * if the fix for parameter bounds checking doesn't work. + */ + public void testParameterBoundsCheck() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testParameterBoundsCheck"); + this.stmt.executeUpdate("CREATE TABLE testParameterBoundsCheck(f1 int, f2 int, f3 int, f4 int, f5 int)"); + + PreparedStatement _pstmt = this.conn.prepareStatement("UPDATE testParameterBoundsCheck SET f1=?, f2=?,f3=?,f4=? WHERE f5=?"); + + _pstmt.setString(1, ""); + _pstmt.setString(2, ""); + + try { + _pstmt.setString(25, ""); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testParameterBoundsCheck"); + } + } + + public void testPStmtTypesBug() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testPStmtTypesBug"); + this.stmt.executeUpdate("CREATE TABLE testPStmtTypesBug(field1 INT)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testPStmtTypesBug VALUES (?)"); + this.pstmt.setObject(1, null, Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testPStmtTypesBug"); + } + } + + /** + * Tests fix for BUG#1511 + * + * @throws Exception + * if the quoteid parsing fix in PreparedStatement doesn't work. + */ + public void testQuotedIdRecognition() throws Exception { + if (!this.versionMeetsMinimum(4, 1)) { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId"); + this.stmt.executeUpdate("CREATE TABLE testQuotedId (col1 VARCHAR(32))"); + + PreparedStatement pStmt = this.conn.prepareStatement("SELECT * FROM testQuotedId WHERE col1='ABC`DEF' or col1=?"); + pStmt.setString(1, "foo"); + pStmt.execute(); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId2"); + this.stmt.executeUpdate("CREATE TABLE testQuotedId2 (`Works?` INT)"); + pStmt = this.conn.prepareStatement("INSERT INTO testQuotedId2 (`Works?`) VALUES (?)"); + pStmt.setInt(1, 1); + pStmt.executeUpdate(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId2"); + } + } + } + + /** + * Tests for BUG#9288, parameter index out of range if LIKE, ESCAPE '\' + * present in query. + * + * @throws Exception + * if the test fails. + */ + /* + * public void testBug9288() throws Exception { String tableName = + * "testBug9288"; PreparedStatement pStmt = null; + * + * try { createTable(tableName, "(field1 VARCHAR(32), field2 INT)"); pStmt = + * ((com.mysql.jdbc.Connection)this.conn).clientPrepareStatement( "SELECT + * COUNT(1) FROM " + tableName + " WHERE " + "field1 LIKE '%' ESCAPE '\\' + * AND " + "field2 > ?"); pStmt.setInt(1, 0); + * + * this.rs = pStmt.executeQuery(); } finally { if (this.rs != null) { + * this.rs.close(); this.rs = null; } + * + * if (pStmt != null) { pStmt.close(); } } } + */ + + /* + * public void testBug10999() throws Exception { if (versionMeetsMinimum(5, + * 0, 5)) { + * + * String tableName = "testBug10999"; String updateTrigName = + * "testBug10999Update"; String insertTrigName = "testBug10999Insert"; try { + * createTable(tableName, "(pkfield INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + * field1 VARCHAR(32))"); + * + * try { this.stmt.executeUpdate("DROP TRIGGER " + updateTrigName); } catch + * (SQLException sqlEx) { // ignore for now } + * + * this.stmt.executeUpdate("CREATE TRIGGER " + updateTrigName + " AFTER + * UPDATE ON " + tableName + " FOR EACH ROW " + "BEGIN " + "END"); + * + * try { this.stmt.executeUpdate("DROP TRIGGER " + insertTrigName); } catch + * (SQLException sqlEx) { // ignore } + * + * this.stmt.executeUpdate("CREATE TRIGGER " + insertTrigName + " AFTER + * INSERT ON " + tableName + " FOR EACH ROW " + " BEGIN " + "END"); + * + * this.conn.setAutoCommit(false); + * + * String updateSQL = "INSERT INTO " + tableName + " (field1) VALUES + * ('abcdefg')"; int rowCount = this.stmt.executeUpdate(updateSQL, + * Statement.RETURN_GENERATED_KEYS); + * + * this.rs = stmt.getGeneratedKeys(); if (rs.next()) { + * System.out.println(rs.getInt(1)); int id = rs.getInt(1); //if + * (log.isDebugEnabled()) // log.debug("Retrieved ID = " + id); } //else { + * //log.error("Can't retrieve ID with getGeneratedKeys."); // Retrieve ID + * using a SELECT statement instead. // querySQL = "SELECT id from tab1 + * WHERE ..."; + * + * //if (log.isDebugEnabled()) // log.debug(querySQL); + * + * //rs = stmt.executeQuery(querySQL); this.rs = + * this.stmt.executeQuery("SELECT pkfield FROM " + tableName); } finally { + * this.conn.setAutoCommit(true); + * + * try { this.stmt.executeUpdate("DROP TRIGGER IF EXISTS " + + * insertTrigName); } catch (SQLException sqlEx) { // ignore } + * + * try { this.stmt.executeUpdate("DROP TRIGGER IF EXISTS " + + * updateTrigName); } catch (SQLException sqlEx) { // ignore } } } } + */ + + /** + * Tests that binary dates/times are encoded/decoded correctly. + * + * @throws Exception + * if the test fails. + * + * @deprecated because we need to use this particular constructor for the + * date class, as Calendar-constructed dates don't pass the + * .equals() test :( + */ + @Deprecated + public void testServerPrepStmtAndDate() throws Exception { + createTable("testServerPrepStmtAndDate", + "(`P_ID` int(10) NOT NULL default '0', `R_Date` date default NULL, UNIQUE KEY `P_ID` (`P_ID`), KEY `R_Date` (`R_Date`))"); + Date dt = new java.sql.Date(102, 1, 2); // Note, this represents the date 2002-02-02 + + PreparedStatement pStmt2 = this.conn.prepareStatement("INSERT INTO testServerPrepStmtAndDate (P_ID, R_Date) VALUES (171576, ?)"); + pStmt2.setDate(1, dt); + pStmt2.executeUpdate(); + pStmt2.close(); + + this.rs = this.stmt.executeQuery("SELECT R_Date FROM testServerPrepStmtAndDate"); + this.rs.next(); + + System.out.println("Date that was stored (as String) " + this.rs.getString(1)); // comes back as 2002-02-02 + + PreparedStatement pStmt = this.conn.prepareStatement("Select P_ID,R_Date from testServerPrepStmtAndDate Where R_Date = ? and P_ID = 171576"); + pStmt.setDate(1, dt); + + this.rs = pStmt.executeQuery(); + + assertTrue(this.rs.next()); + + assertEquals("171576", this.rs.getString(1)); + + assertEquals(dt, this.rs.getDate(2)); + } + + public void testServerPrepStmtDeadlock() throws Exception { + + Connection c = getConnectionWithProps((Properties) null); + + Thread testThread1 = new PrepareThread(c); + Thread testThread2 = new PrepareThread(c); + testThread1.start(); + testThread2.start(); + Thread.sleep(30000); + assertTrue(this.testServerPrepStmtDeadlockCounter >= 10); + } + + /** + * Tests PreparedStatement.setCharacterStream() to ensure it accepts > 4K + * streams + * + * @throws Exception + * if an error occurs. + */ + public void testSetCharacterStream() throws Exception { + try { + ((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(true); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS charStreamRegressTest"); + this.stmt.executeUpdate("CREATE TABLE charStreamRegressTest(field1 text)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO charStreamRegressTest VALUES (?)"); + + // char[] charBuf = new char[16384]; + char[] charBuf = new char[32]; + + for (int i = 0; i < charBuf.length; i++) { + charBuf[i] = 'A'; + } + + CharArrayReader reader = new CharArrayReader(charBuf); + + this.pstmt.setCharacterStream(1, reader, charBuf.length); + this.pstmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT LENGTH(field1) FROM charStreamRegressTest"); + + this.rs.next(); + + System.out.println("Character stream length: " + this.rs.getString(1)); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM charStreamRegressTest"); + + this.rs.next(); + + String result = this.rs.getString(1); + + assertTrue(result.length() == charBuf.length); + + this.stmt.execute("TRUNCATE TABLE charStreamRegressTest"); + + // Test that EOF is not thrown + reader = new CharArrayReader(charBuf); + this.pstmt.clearParameters(); + this.pstmt.setCharacterStream(1, reader, charBuf.length); + this.pstmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT LENGTH(field1) FROM charStreamRegressTest"); + + this.rs.next(); + + System.out.println("Character stream length: " + this.rs.getString(1)); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM charStreamRegressTest"); + + this.rs.next(); + + result = this.rs.getString(1); + + assertTrue("Retrieved value of length " + result.length() + " != length of inserted value " + charBuf.length, result.length() == charBuf.length); + + // Test single quotes inside identifers + this.stmt.executeUpdate("DROP TABLE IF EXISTS `charStream'RegressTest`"); + this.stmt.executeUpdate("CREATE TABLE `charStream'RegressTest`(field1 text)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO `charStream'RegressTest` VALUES (?)"); + + reader = new CharArrayReader(charBuf); + this.pstmt.setCharacterStream(1, reader, (charBuf.length * 2)); + this.pstmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM `charStream'RegressTest`"); + + this.rs.next(); + + result = this.rs.getString(1); + + assertTrue("Retrieved value of length " + result.length() + " != length of inserted value " + charBuf.length, result.length() == charBuf.length); + } finally { + ((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(false); + + if (this.rs != null) { + try { + this.rs.close(); + } catch (Exception ex) { + // ignore + } + + this.rs = null; + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS `charStream'RegressTest`"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS charStreamRegressTest"); + } + } + + /** + * Tests a bug where Statement.setFetchSize() does not work for values other + * than 0 or Integer.MIN_VALUE + * + * @throws Exception + * if any errors occur + */ + public void testSetFetchSize() throws Exception { + int oldFetchSize = this.stmt.getFetchSize(); + + try { + this.stmt.setFetchSize(10); + } finally { + this.stmt.setFetchSize(oldFetchSize); + } + } + + /** + * Tests fix for BUG#907 + * + * @throws Exception + * if an error occurs + */ + public void testSetMaxRows() throws Exception { + Statement maxRowsStmt = null; + + try { + maxRowsStmt = this.conn.createStatement(); + maxRowsStmt.setMaxRows(1); + this.rs = maxRowsStmt.executeQuery("SELECT 1"); + } finally { + if (maxRowsStmt != null) { + maxRowsStmt.close(); + } + } + } + + /** + * Tests for timestamp NPEs occuring in binary-format timestamps. + * + * @throws Exception + * + * @deprecated yes, we know we are using deprecated methods here :) + */ + @Deprecated + public void testTimestampNPE() throws Exception { + try { + Timestamp ts = new Timestamp(System.currentTimeMillis()); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTimestampNPE"); + this.stmt.executeUpdate("CREATE TABLE testTimestampNPE (field1 TIMESTAMP)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testTimestampNPE VALUES (?)"); + this.pstmt.setTimestamp(1, ts); + this.pstmt.executeUpdate(); + + this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testTimestampNPE"); + + this.rs = this.pstmt.executeQuery(); + + this.rs.next(); + + System.out.println(this.rs.getString(1)); + + this.rs.getDate(1); + + Timestamp rTs = this.rs.getTimestamp(1); + assertTrue("Retrieved year of " + rTs.getYear() + " does not match " + ts.getYear(), rTs.getYear() == ts.getYear()); + assertTrue("Retrieved month of " + rTs.getMonth() + " does not match " + ts.getMonth(), rTs.getMonth() == ts.getMonth()); + assertTrue("Retrieved date of " + rTs.getDate() + " does not match " + ts.getDate(), rTs.getDate() == ts.getDate()); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTimestampNPE"); + + } finally { + } + } + + public void testTruncationWithChar() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTruncationWithChar"); + this.stmt.executeUpdate("CREATE TABLE testTruncationWithChar (field1 char(2))"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testTruncationWithChar VALUES (?)"); + this.pstmt.setString(1, "00"); + this.pstmt.executeUpdate(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTruncationWithChar"); + } + } + + /** + * Tests fix for updatable streams being supported in updatable result sets. + * + * @throws Exception + * if the test fails. + */ + public void testUpdatableStream() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS updateStreamTest"); + this.stmt.executeUpdate("CREATE TABLE updateStreamTest (keyField INT NOT NULL AUTO_INCREMENT PRIMARY KEY, field1 BLOB)"); + + int streamLength = 16385; + byte[] streamData = new byte[streamLength]; + + /* create an updatable statement */ + Statement updStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + + /* fill the resultset with some values */ + ResultSet updRs = updStmt.executeQuery("SELECT * FROM updateStreamTest"); + + /* move to insertRow */ + updRs.moveToInsertRow(); + + /* update the table */ + updRs.updateBinaryStream("field1", new ByteArrayInputStream(streamData), streamLength); + + updRs.insertRow(); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS updateStreamTest"); + } + } + + /** + * Tests fix for BUG#15383 - PreparedStatement.setObject() serializes + * BigInteger as object, rather than sending as numeric value (and is thus + * not complementary to .getObject() on an UNSIGNED LONG type). + * + * @throws Exception + * if the test fails. + */ + public void testBug15383() throws Exception { + createTable("testBug15383", "(id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,value BIGINT UNSIGNED NULL DEFAULT 0,PRIMARY KEY(id))", "InnoDB"); + + this.stmt.executeUpdate("INSERT INTO testBug15383(value) VALUES(1)"); + + Statement updatableStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + + try { + this.rs = updatableStmt.executeQuery("SELECT * from testBug15383"); + + assertTrue(this.rs.next()); + + Object bigIntObj = this.rs.getObject("value"); + assertEquals("java.math.BigInteger", bigIntObj.getClass().getName()); + + this.rs.updateObject("value", new BigInteger("3")); + this.rs.updateRow(); + + assertEquals("3", this.rs.getString("value")); + } finally { + if (this.rs != null) { + ResultSet toClose = this.rs; + this.rs = null; + toClose.close(); + } + + if (updatableStmt != null) { + updatableStmt.close(); + } + } + } + + /** + * Tests fix for BUG#17099 - Statement.getGeneratedKeys() throws NPE when no + * query has been processed. + * + * @throws Exception + * if the test fails + */ + public void testBug17099() throws Exception { + PreparedStatement pStmt = this.conn.prepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS); + assertNotNull(pStmt.getGeneratedKeys()); + + if (versionMeetsMinimum(4, 1)) { + pStmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS); + assertNotNull(pStmt.getGeneratedKeys()); + } + } + + /** + * Tests fix for BUG#17587 - clearParameters() on a closed prepared + * statement causes NPE. + * + * @throws Exception + * if the test fails. + */ + public void testBug17587() throws Exception { + createTable("testBug17857", "(field1 int)"); + PreparedStatement pStmt = null; + + try { + pStmt = this.conn.prepareStatement("INSERT INTO testBug17857 VALUES (?)"); + pStmt.close(); + try { + pStmt.clearParameters(); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + pStmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("INSERT INTO testBug17857 VALUES (?)"); + pStmt.close(); + try { + pStmt.clearParameters(); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests fix for BUG#18740 - Data truncation and getWarnings() only returns + * last warning in set. + * + * @throws Exception + * if the test fails. + */ + public void testBug18740() throws Exception { + if (!versionMeetsMinimum(5, 0, 2)) { + createTable("testWarnings", "(field1 smallint(6), field2 varchar(6), UNIQUE KEY field1(field1))"); + + try { + this.stmt.executeUpdate( + "INSERT INTO testWarnings VALUES (10001, 'data1'), (10002, 'data2 foo'), (10003, 'data3'), (10004999, 'data4'), (10005, 'data5')"); + } catch (SQLException sqlEx) { + String sqlStateToCompare = "01004"; + + if (isJdbc4()) { + sqlStateToCompare = "22001"; + } + + assertEquals(sqlStateToCompare, sqlEx.getSQLState()); + assertEquals(sqlStateToCompare, sqlEx.getNextException().getSQLState()); + + SQLWarning sqlWarn = this.stmt.getWarnings(); + assertEquals("01000", sqlWarn.getSQLState()); + assertEquals("01000", sqlWarn.getNextWarning().getSQLState()); + } + } + } + + protected boolean isJdbc4() { + boolean isJdbc4; + + try { + Class.forName("java.sql.Wrapper"); + isJdbc4 = true; + } catch (Throwable t) { + isJdbc4 = false; + } + + return isJdbc4; + } + + /** + * Tests fix for BUG#19615, PreparedStatement.setObject(int, Object, int) + * doesn't respect scale of BigDecimals. + * + * @throws Exception + * if the test fails. + */ + public void testBug19615() throws Exception { + createTable("testBug19615", "(field1 DECIMAL(19, 12))"); + + BigDecimal dec = new BigDecimal("1.234567"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug19615 VALUES (?)"); + this.pstmt.setObject(1, dec, Types.DECIMAL); + this.pstmt.executeUpdate(); + this.pstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug19615"); + this.rs.next(); + assertEquals(dec, this.rs.getBigDecimal(1).setScale(6)); + this.rs.close(); + this.stmt.executeUpdate("TRUNCATE TABLE testBug19615"); + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("INSERT INTO testBug19615 VALUES (?)"); + this.pstmt.setObject(1, dec, Types.DECIMAL); + this.pstmt.executeUpdate(); + this.pstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug19615"); + this.rs.next(); + assertEquals(dec, this.rs.getBigDecimal(1).setScale(6)); + this.rs.close(); + } + + /** + * Tests fix for BUG#20029 - NPE thrown from executeBatch(). + * + * @throws Exception + */ + public void testBug20029() throws Exception { + createTable("testBug20029", ("(field1 int)")); + + long initialTimeout = 20; // may need to raise this depending on environment we try and do this automatically in this testcase + + for (int i = 0; i < 10; i++) { + final Connection toBeKilledConn = getConnectionWithProps(new Properties()); + final long timeout = initialTimeout; + PreparedStatement toBeKilledPstmt = null; + + try { + toBeKilledPstmt = ((com.mysql.jdbc.Connection) toBeKilledConn).clientPrepareStatement("INSERT INTO testBug20029 VALUES (?)"); + + for (int j = 0; j < 1000; j++) { + toBeKilledPstmt.setInt(1, j); + toBeKilledPstmt.addBatch(); + } + + Thread t = new Thread() { + @Override + public void run() { + try { + sleep(timeout); + toBeKilledConn.close(); + } catch (Throwable thr) { + + } + } + }; + + t.start(); + + try { + if (!toBeKilledConn.isClosed()) { + initialTimeout *= 2; + continue; + } + + toBeKilledPstmt.executeBatch(); + fail("Should've caught a SQLException for the statement being closed here"); + } catch (BatchUpdateException batchEx) { + assertEquals("08003", batchEx.getSQLState()); + break; + } catch (SQLException sqlEx) { + assertEquals("08003", sqlEx.getSQLState()); + break; + } + + fail("Connection didn't close while in the middle of PreparedStatement.executeBatch()"); + } finally { + if (toBeKilledPstmt != null) { + toBeKilledPstmt.close(); + } + + if (toBeKilledConn != null) { + toBeKilledConn.close(); + } + } + } + } + + /** + * Fixes BUG#20687 - Can't pool server-side prepared statements, exception + * raised when re-using them. + * + * @throws Exception + * if the test fails. + */ + public void testBug20687() throws Exception { + if (versionMeetsMinimum(5, 0)) { + createTable("testBug20687", "(field1 int)"); + Connection poolingConn = null; + + Properties props = new Properties(); + props.setProperty("cachePrepStmts", "true"); + props.setProperty("useServerPrepStmts", "true"); + PreparedStatement pstmt1 = null; + PreparedStatement pstmt2 = null; + + try { + poolingConn = getConnectionWithProps(props); + pstmt1 = poolingConn.prepareStatement("SELECT field1 FROM testBug20687"); + this.rs = pstmt1.executeQuery(); + pstmt1.close(); + + pstmt2 = poolingConn.prepareStatement("SELECT field1 FROM testBug20687"); + this.rs = pstmt2.executeQuery(); + assertTrue(pstmt1 == pstmt2); + pstmt2.close(); + } finally { + if (pstmt1 != null) { + pstmt1.close(); + } + + if (pstmt2 != null) { + pstmt2.close(); + } + + if (poolingConn != null) { + poolingConn.close(); + } + } + } + } + + public void testLikeWithBackslashes() throws Exception { + if (!versionMeetsMinimum(5, 0, 0)) { + return; + } + + Connection noBackslashEscapesConn = null; + + try { + Properties props = new Properties(); + props.setProperty("sessionVariables", "sql_mode=NO_BACKSLASH_ESCAPES"); + + noBackslashEscapesConn = getConnectionWithProps(props); + + createTable("X_TEST", + "(userName varchar(32) not null, ivalue integer, CNAME varchar(255), bvalue CHAR(1), svalue varchar(255), ACTIVE CHAR(1), primary key (userName))"); + + String insert_sql = "insert into X_TEST (ivalue, CNAME, bvalue, svalue, ACTIVE, userName) values (?, ?, ?, ?, ?, ?)"; + + this.pstmt = noBackslashEscapesConn.prepareStatement(insert_sql); + this.pstmt.setInt(1, 0); + this.pstmt.setString(2, "c:\\jetson"); + this.pstmt.setInt(3, 1); + this.pstmt.setString(4, "c:\\jetson"); + this.pstmt.setInt(5, 1); + this.pstmt.setString(6, "c:\\jetson"); + this.pstmt.execute(); + + String select_sql = "select user0_.userName as userName0_0_, user0_.ivalue as ivalue0_0_, user0_.CNAME as CNAME0_0_, user0_.bvalue as bvalue0_0_, user0_.svalue as svalue0_0_, user0_.ACTIVE as ACTIVE0_0_ from X_TEST user0_ where user0_.userName like ?"; + this.pstmt = noBackslashEscapesConn.prepareStatement(select_sql); + this.pstmt.setString(1, "c:\\j%"); + // if we comment out the previous line and uncomment the following, the like clause matches + // this.pstmt.setString(1,"c:\\\\j%"); + System.out.println("about to execute query " + select_sql); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + } finally { + if (noBackslashEscapesConn != null) { + noBackslashEscapesConn.close(); + } + } + } + + /** + * Tests fix for BUG#20650 - Statement.cancel() causes NullPointerException + * if underlying connection has been closed due to server failure. + * + * @throws Exception + * if the test fails. + */ + public void testBug20650() throws Exception { + Connection closedConn = null; + Statement cancelStmt = null; + + try { + closedConn = getConnectionWithProps((String) null); + cancelStmt = closedConn.createStatement(); + + closedConn.close(); + + cancelStmt.cancel(); + } finally { + if (cancelStmt != null) { + cancelStmt.close(); + } + + if (closedConn != null && !closedConn.isClosed()) { + closedConn.close(); + } + } + } + + /** + * Tests fix for BUG#20888 - escape of quotes in client-side prepared + * statements parsing not respected. + * + * @throws Exception + * if the test fails. + */ + public void testBug20888() throws Exception { + String s = "SELECT 'What do you think about D\\'Artanian''?', \"What do you think about D\\\"Artanian\"\"?\""; + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement(s); + + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + assertEquals(this.rs.getString(1), "What do you think about D'Artanian'?"); + assertEquals(this.rs.getString(2), "What do you think about D\"Artanian\"?"); + } + + /** + * Tests Bug#21207 - Driver throws NPE when tracing prepared statements that + * have been closed (in asSQL()). + * + * @throws Exception + * if the test fails + */ + public void testBug21207() throws Exception { + this.pstmt = this.conn.prepareStatement("SELECT 1"); + this.pstmt.close(); + this.pstmt.toString(); // this used to cause an NPE + } + + /** + * Tests BUG#21438, server-side PS fails when using jdbcCompliantTruncation. + * If either is set to FALSE (&useServerPrepStmts=false or + * &jdbcCompliantTruncation=false) test succedes. + * + * @throws Exception + * if the test fails. + */ + + @SuppressWarnings("deprecation") + public void testBug21438() throws Exception { + createTable("testBug21438", "(t_id int(10), test_date timestamp NOT NULL,primary key t_pk (t_id));"); + + assertEquals(1, this.stmt.executeUpdate("insert into testBug21438 values (1,NOW());")); + + if (this.versionMeetsMinimum(4, 1)) { + this.pstmt = ((com.mysql.jdbc.Connection) this.conn) + .serverPrepareStatement("UPDATE testBug21438 SET test_date=ADDDATE(?,INTERVAL 1 YEAR) WHERE t_id=1;"); + Timestamp ts = new Timestamp(System.currentTimeMillis()); + ts.setNanos(999999999); + + this.pstmt.setTimestamp(1, ts); + + assertEquals(1, this.pstmt.executeUpdate()); + + Timestamp future = (Timestamp) getSingleIndexedValueWithQuery(1, "SELECT test_date FROM testBug21438"); + assertEquals(future.getYear() - ts.getYear(), 1); + } + } + + /** + * Tests fix for BUG#22359 - Driver was using millis for + * Statement.setQueryTimeout() when spec says argument is seconds. + * + * @throws Exception + * if the test fails. + */ + public void testBug22359() throws Exception { + if (versionMeetsMinimum(5, 0)) { + Statement timeoutStmt = null; + + try { + timeoutStmt = this.conn.createStatement(); + timeoutStmt.setQueryTimeout(2); + + long begin = System.currentTimeMillis(); + + try { + timeoutStmt.execute("SELECT SLEEP(30)"); + fail("Query didn't time out"); + } catch (MySQLTimeoutException timeoutEx) { + long end = System.currentTimeMillis(); + + assertTrue((end - begin) > 1000); + } + } finally { + if (timeoutStmt != null) { + timeoutStmt.close(); + } + } + } + } + + /** + * Tests fix for BUG#22290 - Driver issues truncation on write exception + * when it shouldn't (due to sending big decimal incorrectly to server with + * server-side prepared statement). + * + * @throws Exception + * if the test fails. + */ + public void testBug22290() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testbug22290", "(`id` int(11) NOT NULL default '1',`cost` decimal(10,2) NOT NULL,PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8", "InnoDB"); + assertEquals(this.stmt.executeUpdate("INSERT INTO testbug22290 (`id`,`cost`) VALUES (1,'1.00')"), 1); + + Connection configuredConn = null; + + try { + Properties props = new Properties(); + props.setProperty("sessionVariables", "sql_mode='STRICT_TRANS_TABLES'"); + + configuredConn = getConnectionWithProps(props); + + this.pstmt = configuredConn.prepareStatement("update testbug22290 set cost = cost + ? where id = 1"); + this.pstmt.setBigDecimal(1, new BigDecimal("1.11")); + assertEquals(this.pstmt.executeUpdate(), 1); + + assertEquals(this.stmt.executeUpdate("UPDATE testbug22290 SET cost='1.00'"), 1); + this.pstmt = ((com.mysql.jdbc.Connection) configuredConn).clientPrepareStatement("update testbug22290 set cost = cost + ? where id = 1"); + this.pstmt.setBigDecimal(1, new BigDecimal("1.11")); + assertEquals(this.pstmt.executeUpdate(), 1); + } finally { + if (configuredConn != null) { + configuredConn.close(); + } + } + } + + public void testClientPreparedSetBoolean() throws Exception { + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT ?"); + this.pstmt.setBoolean(1, false); + assertEquals("SELECT 0", this.pstmt.toString().substring(this.pstmt.toString().indexOf("SELECT"))); + this.pstmt.setBoolean(1, true); + assertEquals("SELECT 1", this.pstmt.toString().substring(this.pstmt.toString().indexOf("SELECT"))); + } + + /** + * Tests fix for BUG#24360 .setFetchSize() breaks prepared SHOW and other + * commands. + * + * @throws Exception + * if the test fails + */ + public void testBug24360() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection c = null; + + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + + try { + c = getConnectionWithProps(props); + + this.pstmt = c.prepareStatement("SHOW PROCESSLIST"); + this.pstmt.setFetchSize(5); + this.pstmt.execute(); + } finally { + if (c != null) { + c.close(); + } + } + } + + /** + * Tests fix for BUG#24344 - useJDBCCompliantTimezoneShift with server-side + * prepared statements gives different behavior than when using client-side + * prepared statements. (this is now fixed if moving from server-side + * prepared statements to client-side prepared statements by setting + * "useSSPSCompatibleTimezoneShift" to "true", as the driver can't tell if + * this is a new deployment that never used server-side prepared statements, + * or if it is an existing deployment that is switching to client-side + * prepared statements from server-side prepared statements. + * + * @throws Exception + * if the test fails + */ + public void testBug24344() throws Exception { + + if (!versionMeetsMinimum(4, 1)) { + return; // need SSPS + } + + super.createTable("testBug24344", "(i INT AUTO_INCREMENT, t1 DATETIME, PRIMARY KEY (i)) ENGINE = MyISAM"); + + Connection conn2 = null; + + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("useJDBCCompliantTimezoneShift", "true"); + conn2 = super.getConnectionWithProps(props); + this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)"); + Calendar c = Calendar.getInstance(); + this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime())); + this.pstmt.execute(); + this.pstmt.close(); + conn2.close(); + + props.setProperty("useServerPrepStmts", "false"); + props.setProperty("useJDBCCompliantTimezoneShift", "true"); + props.setProperty("useSSPSCompatibleTimezoneShift", "true"); + + conn2 = super.getConnectionWithProps(props); + this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)"); + this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime())); + this.pstmt.execute(); + this.pstmt.close(); + conn2.close(); + + props.setProperty("useServerPrepStmts", "false"); + props.setProperty("useJDBCCompliantTimezoneShift", "false"); + props.setProperty("useSSPSCompatibleTimezoneShift", "false"); + conn2 = super.getConnectionWithProps(props); + this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)"); + this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime())); + this.pstmt.execute(); + this.pstmt.close(); + + Statement s = conn2.createStatement(); + this.rs = s.executeQuery("SELECT t1 FROM testBug24344 ORDER BY i ASC"); + + Timestamp[] dates = new Timestamp[3]; + + int i = 0; + + while (this.rs.next()) { + dates[i++] = this.rs.getTimestamp(1); + } + + assertEquals("Number of rows should be 3.", 3, i); + assertEquals(dates[0], dates[1]); + if (TimeZone.getDefault().getOffset(c.getTimeInMillis()) != 0) { + assertFalse(dates[1].equals(dates[2])); + } else { + assertTrue(dates[1].equals(dates[2])); + } + } finally { + if (conn2 != null) { + conn2.close(); + } + } + } + + /** + * Tests fix for BUG#25073 - rewriting batched statements leaks internal + * statement instances, and causes a memory leak. + * + * @throws Exception + * if the test fails. + */ + public void testBug25073() throws Exception { + Properties props = new Properties(); + props.setProperty("rewriteBatchedStatements", "true"); + Connection multiConn = getConnectionWithProps(props); + createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + Statement multiStmt = multiConn.createStatement(); + multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (1)"); + multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (2)"); + multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (3)"); + multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (4)"); + multiStmt.addBatch("UPDATE testBug25073 SET field1=5 WHERE field1=1"); + multiStmt.addBatch("UPDATE testBug25073 SET field1=6 WHERE field1=2 OR field1=3"); + + int beforeOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + multiStmt.executeBatch(); + + int afterOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + assertEquals(beforeOpenStatementCount, afterOpenStatementCount); + + createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + props.clear(); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("maxAllowedPacket", "1024"); + props.setProperty("dumpQueriesOnException", "true"); + props.setProperty("maxQuerySizeToLog", String.valueOf(1024 * 1024 * 2)); + multiConn = getConnectionWithProps(props); + multiStmt = multiConn.createStatement(); + + for (int i = 0; i < 1000; i++) { + multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (" + i + ")"); + } + + beforeOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + multiStmt.executeBatch(); + + afterOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + assertEquals(beforeOpenStatementCount, afterOpenStatementCount); + + createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + + props.clear(); + props.setProperty("useServerPrepStmts", "false"); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("dumpQueriesOnException", "true"); + props.setProperty("maxQuerySizeToLog", String.valueOf(1024 * 1024 * 2)); + multiConn = getConnectionWithProps(props); + PreparedStatement pStmt = multiConn.prepareStatement("INSERT INTO testBug25073(field1) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + + for (int i = 0; i < 1000; i++) { + pStmt.setInt(1, i); + pStmt.addBatch(); + } + + beforeOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + pStmt.executeBatch(); + + afterOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + assertEquals(beforeOpenStatementCount, afterOpenStatementCount); + + createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + props.setProperty("useServerPrepStmts", "false"); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("maxAllowedPacket", "1024"); + props.setProperty("dumpQueriesOnException", "true"); + props.setProperty("maxQuerySizeToLog", String.valueOf(1024 * 1024 * 2)); + multiConn = getConnectionWithProps(props); + pStmt = multiConn.prepareStatement("INSERT INTO testBug25073(field1) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + + for (int i = 0; i < 1000; i++) { + pStmt.setInt(1, i); + pStmt.addBatch(); + } + + beforeOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + pStmt.executeBatch(); + + afterOpenStatementCount = ((com.mysql.jdbc.Connection) multiConn).getActiveStatementCount(); + + assertEquals(beforeOpenStatementCount, afterOpenStatementCount); + } + + /** + * Tests fix for BUG#25009 - Results from updates not handled correctly in + * multi-statement queries. + * + * @throws Exception + * if the test fails. + */ + public void testBug25009() throws Exception { + if (!versionMeetsMinimum(4, 1)) { + return; + } + + Properties props = new Properties(); + props.setProperty("allowMultiQueries", "true"); + + Connection multiConn = getConnectionWithProps(props); + createTable("testBug25009", "(field1 INT)"); + + try { + Statement multiStmt = multiConn.createStatement(); + multiStmt.execute("SELECT 1;SET @a=1; SET @b=2; SET @c=3; INSERT INTO testBug25009 VALUES (1)"); + + assertEquals(-1, multiStmt.getUpdateCount()); + + this.rs = multiStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals(multiStmt.getMoreResults(), false); + + for (int i = 0; i < 3; i++) { + assertEquals(0, multiStmt.getUpdateCount()); + assertEquals(multiStmt.getMoreResults(), false); + } + + assertEquals(1, multiStmt.getUpdateCount()); + + this.rs = multiStmt.executeQuery("SELECT field1 FROM testBug25009"); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + } finally { + if (multiConn != null) { + multiConn.close(); + } + } + } + + /** + * Tests fix for BUG#25025 - Client-side prepared statement parser gets + * confused by in-line (slash-star) comments and therefore can't rewrite + * batched statements or reliably detect type of statements when they're + * used. + * + * @throws Exception + * if the test fails. + */ + public void testBug25025() throws Exception { + + Connection multiConn = null; + + createTable("testBug25025", "(field1 INT)"); + + try { + Properties props = new Properties(); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("useServerPrepStmts", "false"); + + multiConn = getConnectionWithProps(props); + + this.pstmt = multiConn + .prepareStatement("/* insert foo.bar.baz INSERT INTO foo VALUES (?,?,?,?) to trick parser */ INSERT into testBug25025 VALUES (?)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + + int[] counts = this.pstmt.executeBatch(); + + assertEquals(3, counts.length); + assertEquals(Statement.SUCCESS_NO_INFO, counts[0]); + assertEquals(Statement.SUCCESS_NO_INFO, counts[1]); + assertEquals(Statement.SUCCESS_NO_INFO, counts[2]); + assertEquals(true, ((com.mysql.jdbc.PreparedStatement) this.pstmt).canRewriteAsMultiValueInsertAtSqlLevel()); + } finally { + if (multiConn != null) { + multiConn.close(); + } + } + } + + public void testBustedGGKWithPSExecute() throws Exception { + createTable("sequence", "(sequence_name VARCHAR(32) NOT NULL PRIMARY KEY, next_val BIGINT NOT NULL)"); + + // Populate with the initial value + this.stmt.executeUpdate("INSERT INTO sequence VALUES ('test-sequence', 1234)"); + + // Atomic operation to increment and return next value + PreparedStatement pStmt = null; + + try { + pStmt = this.conn.prepareStatement("UPDATE sequence SET next_val=LAST_INSERT_ID(next_val + ?) WHERE sequence_name = ?", + Statement.RETURN_GENERATED_KEYS); + + pStmt.setInt(1, 4); + pStmt.setString(2, "test-sequence"); + pStmt.execute(); + + this.rs = pStmt.getGeneratedKeys(); + this.rs.next(); + assertEquals(1238, this.rs.getLong(1)); + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Tests fix for BUG#28256 - When connection is in read-only mode, queries + * that are parentheized incorrectly identified as DML. + * + * @throws Exception + */ + public void testBug28256() throws Exception { + try { + this.conn.setReadOnly(true); + this.stmt.execute("(SELECT 1) UNION (SELECT 2)"); + this.conn.prepareStatement("(SELECT 1) UNION (SELECT 2)").execute(); + if (versionMeetsMinimum(4, 1)) { + ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("(SELECT 1) UNION (SELECT 2)").execute(); + } + } finally { + this.conn.setReadOnly(false); + } + } + + /** + * Tests fix for BUG#28469 - PreparedStatement.getMetaData() for statements + * containing leading one-line comments is not returned correctly. + * + * As part of this fix, we also overhauled detection of DML for + * executeQuery() and SELECTs for executeUpdate() in plain and prepared + * statements to be aware of the same types of comments. + * + * @throws Exception + */ + public void testBug28469() throws Exception { + PreparedStatement commentStmt = null; + + try { + String[] statementsToTest = { "-- COMMENT\nSELECT 1", "# COMMENT\nSELECT 1", "/* comment */ SELECT 1" }; + + for (int i = 0; i < statementsToTest.length; i++) { + commentStmt = this.conn.prepareStatement(statementsToTest[i]); + + assertNotNull(commentStmt.getMetaData()); + + try { + commentStmt.executeUpdate(); + fail("Should not be able to call executeUpdate() on a SELECT statement!"); + } catch (SQLException sqlEx) { + // expected + } + + this.rs = commentStmt.executeQuery(); + this.rs.next(); + assertEquals(1, this.rs.getInt(1)); + } + + createTable("testBug28469", "(field1 INT)"); + + String[] updatesToTest = { "-- COMMENT\nUPDATE testBug28469 SET field1 = 2", "# COMMENT\nUPDATE testBug28469 SET field1 = 2", + "/* comment */ UPDATE testBug28469 SET field1 = 2" }; + + for (int i = 0; i < updatesToTest.length; i++) { + commentStmt = this.conn.prepareStatement(updatesToTest[i]); + + assertNull(commentStmt.getMetaData()); + + try { + this.rs = commentStmt.executeQuery(); + fail("Should not be able to call executeQuery() on a SELECT statement!"); + } catch (SQLException sqlEx) { + // expected + } + + try { + this.rs = this.stmt.executeQuery(updatesToTest[i]); + fail("Should not be able to call executeQuery() on a SELECT statement!"); + } catch (SQLException sqlEx) { + // expected + } + } + } finally { + if (commentStmt != null) { + commentStmt.close(); + } + } + } + + /** + * Tests error with slash-star comment at EOL + * + * @throws Exception + * if the test fails. + */ + public void testCommentParsing() throws Exception { + createTable("PERSON", "(NAME VARCHAR(32), PERID VARCHAR(32))"); + + this.pstmt = this.conn.prepareStatement("SELECT NAME AS name2749_0_, PERID AS perid2749_0_ FROM PERSON WHERE PERID=? /*FOR UPDATE*/"); + } + + /** + * Tests fix for BUG#28851 - parser in client-side prepared statements eats + * character following '/' if it's not a multi-line comment. + * + * @throws Exception + * if the test fails. + */ + public void testBug28851() throws Exception { + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement("SELECT 1/?"); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + + assertTrue(this.rs.next()); + + assertEquals(1, this.rs.getInt(1)); + + } + + /** + * Tests fix for BUG#28596 - parser in client-side prepared statements runs + * to end of statement, rather than end-of-line for '#' comments. + * + * Also added support for '--' single-line comments + * + * @throws Exception + * if the test fails. + */ + public void testBug28596() throws Exception { + String query = "SELECT #\n?, #\n? #?\r\n,-- abcdefg \n?"; + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement(query); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.setInt(3, 3); + + assertEquals(3, this.pstmt.getParameterMetaData().getParameterCount()); + this.rs = this.pstmt.executeQuery(); + + assertTrue(this.rs.next()); + + assertEquals(1, this.rs.getInt(1)); + assertEquals(2, this.rs.getInt(2)); + assertEquals(3, this.rs.getInt(3)); + } + + /** + * Tests fix for BUG#30550 - executeBatch() on an empty batch when there are + * no elements in the batch causes a divide-by-zero error when rewriting is + * enabled. + * + * @throws Exception + * if the test fails + */ + public void testBug30550() throws Exception { + createTable("testBug30550", "(field1 int)"); + + Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true"); + PreparedStatement batchPStmt = null; + Statement batchStmt = null; + + try { + batchStmt = rewriteConn.createStatement(); + assertEquals(0, batchStmt.executeBatch().length); + + batchStmt.addBatch("INSERT INTO testBug30550 VALUES (1)"); + int[] counts = batchStmt.executeBatch(); + assertEquals(1, counts.length); + assertEquals(1, counts[0]); + assertEquals(0, batchStmt.executeBatch().length); + + batchPStmt = rewriteConn.prepareStatement("INSERT INTO testBug30550 VALUES (?)"); + batchPStmt.setInt(1, 1); + assertEquals(0, batchPStmt.executeBatch().length); + batchPStmt.addBatch(); + counts = batchPStmt.executeBatch(); + assertEquals(1, counts.length); + assertEquals(1, counts[0]); + assertEquals(0, batchPStmt.executeBatch().length); + } finally { + if (batchPStmt != null) { + batchPStmt.close(); + } + + if (batchStmt != null) { + batchStmt.close(); + } + if (rewriteConn != null) { + rewriteConn.close(); + } + } + } + + /** + * Tests fix for Bug#27412 - cached metadata with + * PreparedStatement.execute() throws NullPointerException. + * + * @throws Exception + */ + public void testBug27412() throws Exception { + Properties props = new Properties(); + props.put("useServerPrepStmts", "false"); + props.put("cachePreparedStatements", "true"); + props.put("cacheResultSetMetadata", "true"); + Connection conn2 = getConnectionWithProps(props); + PreparedStatement pstm = conn2.prepareStatement("SELECT 1"); + try { + assertTrue(pstm.execute()); + } finally { + pstm.close(); + conn2.close(); + } + } + + public void testBustedGGKColumnNames() throws Exception { + createTable("testBustedGGKColumnNames", "(field1 int primary key auto_increment)"); + this.stmt.executeUpdate("INSERT INTO testBustedGGKColumnNames VALUES (null)", Statement.RETURN_GENERATED_KEYS); + assertEquals("GENERATED_KEY", this.stmt.getGeneratedKeys().getMetaData().getColumnName(1)); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBustedGGKColumnNames VALUES (null)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.executeUpdate(); + assertEquals("GENERATED_KEY", this.pstmt.getGeneratedKeys().getMetaData().getColumnName(1)); + + if (versionMeetsMinimum(4, 1, 0)) { + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("INSERT INTO testBustedGGKColumnNames VALUES (null)", + Statement.RETURN_GENERATED_KEYS); + this.pstmt.executeUpdate(); + assertEquals("GENERATED_KEY", this.pstmt.getGeneratedKeys().getMetaData().getColumnName(1)); + } + + } + + public void testLancesBitMappingBug() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("Bit_TabXXX", "( `MAX_VAL` BIT default NULL, `MIN_VAL` BIT default NULL, `NULL_VAL` BIT default NULL) DEFAULT CHARSET=latin1", "InnoDB"); + + // add Bit_In_MinXXX procedure + createProcedure("Bit_In_MinXXX", "(MIN_PARAM TINYINT(1)) begin update Bit_TabXXX set MIN_VAL=MIN_PARAM; end"); + + createProcedure("Bit_In_MaxXXX", "(MAX_PARAM TINYINT(1)) begin update Bit_TabXXX set MAX_VAL=MAX_PARAM; end"); + + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + String sPrepStmt = "{call Bit_In_MinXXX(?)}"; + this.pstmt = this.conn.prepareStatement(sPrepStmt); + this.pstmt.setObject(1, "true", java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("true", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, "false", java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("false", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, "1", java.sql.Types.BIT); // fails + this.pstmt.executeUpdate(); + assertEquals("true", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, "0", java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("false", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, Boolean.TRUE, java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("true", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, Boolean.FALSE, java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("false", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, new Boolean(true), java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("true", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, new Boolean(false), java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("false", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, new Byte("1"), java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("true", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + this.stmt.execute("TRUNCATE TABLE Bit_TabXXX"); + this.stmt.execute("insert into Bit_TabXXX values(null,0,null)"); + + this.pstmt.setObject(1, new Byte("0"), java.sql.Types.BIT); + this.pstmt.executeUpdate(); + assertEquals("false", getSingleIndexedValueWithQuery(1, "SELECT MIN_VAL FROM Bit_TabXXX").toString()); + } + + /** + * Tests fix for BUG#32577 - no way to store two timestamp/datetime values + * that happens over the DST switchover, as the hours end up being the same + * when sent as the literal that MySQL requires. + * + * Note that to get this scenario to work with MySQL (since it doesn't + * support per-value timezones), you need to configure your server (or + * session) to be in UTC, and tell the driver not to use the legacy + * date/time code by setting "useLegacyDatetimeCode" to "false". This will + * cause the driver to always convert to/from the server and client timezone + * consistently. + * + * @throws Exception + */ + public void testBug32577() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testBug32577", "(id INT, field_datetime DATETIME, field_timestamp TIMESTAMP)"); + Properties props = new Properties(); + props.setProperty("useLegacyDatetimeCode", "false"); + props.setProperty("sessionVariables", "time_zone='+0:00'"); + props.setProperty("serverTimezone", "UTC"); + + Connection nonLegacyConn = getConnectionWithProps(props); + + try { + long earlier = 1194154200000L; + long later = 1194157800000L; + + this.pstmt = nonLegacyConn.prepareStatement("INSERT INTO testBug32577 VALUES (?,?,?)"); + Timestamp ts = new Timestamp(earlier); + this.pstmt.setInt(1, 1); + this.pstmt.setTimestamp(2, ts); + this.pstmt.setTimestamp(3, ts); + this.pstmt.executeUpdate(); + + ts = new Timestamp(later); + this.pstmt.setInt(1, 2); + this.pstmt.setTimestamp(2, ts); + this.pstmt.setTimestamp(3, ts); + this.pstmt.executeUpdate(); + + this.rs = nonLegacyConn.createStatement() + .executeQuery("SELECT id, field_datetime, field_timestamp , UNIX_TIMESTAMP(field_datetime), UNIX_TIMESTAMP(field_timestamp) " + + "FROM testBug32577 ORDER BY id ASC"); + + this.rs.next(); + + //java.util.Date date1 = new Date(this.rs.getTimestamp(2).getTime()); + Timestamp ts1 = this.rs.getTimestamp(3); + long datetimeSeconds1 = this.rs.getLong(4) * 1000; + long timestampSeconds1 = this.rs.getLong(5) * 1000; + + this.rs.next(); + + //java.util.Date date2 = new Date(this.rs.getTimestamp(2).getTime()); + Timestamp ts2 = this.rs.getTimestamp(3); + long datetimeSeconds2 = this.rs.getLong(4) * 1000; + long timestampSeconds2 = this.rs.getLong(5) * 1000; + + assertEquals(later, datetimeSeconds2); + assertEquals(later, timestampSeconds2); + assertEquals(earlier, datetimeSeconds1); + assertEquals(earlier, timestampSeconds1); + + SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm z"); + sdf.setTimeZone(TimeZone.getTimeZone("America/New York")); + System.out.println(sdf.format(ts2)); + System.out.println(sdf.format(ts1)); + } finally { + if (nonLegacyConn != null) { + nonLegacyConn.close(); + } + } + } + + /** + * Tests fix for BUG#30508 - ResultSet returned by + * Statement.getGeneratedKeys() is not closed automatically when statement + * that created it is closed. + * + * @throws Exception + */ + public void testBug30508() throws Exception { + createTable("testBug30508", "(k INT PRIMARY KEY NOT NULL AUTO_INCREMENT, p VARCHAR(32))"); + try { + Statement ggkStatement = this.conn.createStatement(); + ggkStatement.executeUpdate("INSERT INTO testBug30508 (p) VALUES ('abc')", Statement.RETURN_GENERATED_KEYS); + + this.rs = ggkStatement.getGeneratedKeys(); + ggkStatement.close(); + + this.rs.next(); + fail("Should've had an exception here"); + } catch (SQLException sqlEx) { + assertEquals("S1000", sqlEx.getSQLState()); + } + + try { + this.pstmt = this.conn.prepareStatement("SELECT 1", Statement.RETURN_GENERATED_KEYS); + this.rs = this.pstmt.getGeneratedKeys(); + this.pstmt.close(); + this.rs.next(); + fail("Should've had an exception here"); + } catch (SQLException sqlEx) { + assertEquals("S1000", sqlEx.getSQLState()); + } + + if (versionMeetsMinimum(5, 0)) { + createProcedure("testBug30508", "() BEGIN SELECT 1; END"); + + try { + this.pstmt = this.conn.prepareCall("{CALL testBug30508()}"); + this.rs = this.pstmt.getGeneratedKeys(); + this.pstmt.close(); + this.rs.next(); + fail("Should've had an exception here"); + } catch (SQLException sqlEx) { + assertEquals("S1000", sqlEx.getSQLState()); + } + } + } + + public void testMoreLanceBugs() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("Bit_Tab", "( `MAX_VAL` BIT default NULL, `MIN_VAL` BIT default NULL, `NULL_VAL` BIT default NULL) DEFAULT CHARSET=latin1", "InnoDB"); + // this.stmt.execute("insert into Bit_Tab values(null,0,null)"); + createProcedure("Bit_Proc", "(out MAX_PARAM TINYINT, out MIN_PARAM TINYINT, out NULL_PARAM TINYINT) " + + "begin select MAX_VAL, MIN_VAL, NULL_VAL into MAX_PARAM, MIN_PARAM, NULL_PARAM from Bit_Tab; end "); + + Boolean minBooleanVal; + Boolean oRetVal; + String Min_Val_Query = "SELECT MIN_VAL from Bit_Tab"; + String Min_Insert = "insert into Bit_Tab values(1,0,null)"; + // System.out.println("Value to insert=" + extractVal(Min_Insert,1)); + CallableStatement cstmt; + + this.stmt.executeUpdate("delete from Bit_Tab"); + this.stmt.executeUpdate(Min_Insert); + cstmt = this.conn.prepareCall("{call Bit_Proc(?,?,?)}"); + + cstmt.registerOutParameter(1, java.sql.Types.BIT); + cstmt.registerOutParameter(2, java.sql.Types.BIT); + cstmt.registerOutParameter(3, java.sql.Types.BIT); + + cstmt.executeUpdate(); + + boolean bRetVal = cstmt.getBoolean(2); + oRetVal = new Boolean(bRetVal); + minBooleanVal = new Boolean("false"); + this.rs = this.stmt.executeQuery(Min_Val_Query); + assertEquals(minBooleanVal, oRetVal); + } + + public void testBug33823() { + new ResultSetInternalMethods() { + + public void buildIndexMapping() throws SQLException { + } + + public void clearNextResult() { + } + + public ResultSetInternalMethods copy() throws SQLException { + return null; + } + + public char getFirstCharOfQuery() { + return 0; + } + + public ResultSetInternalMethods getNextResultSet() { + return null; + } + + public Object getObjectStoredProc(int columnIndex, int desiredSqlType) throws SQLException { + return null; + } + + public Object getObjectStoredProc(int i, Map map, int desiredSqlType) throws SQLException { + return null; + } + + public Object getObjectStoredProc(String columnName, int desiredSqlType) throws SQLException { + return null; + } + + public Object getObjectStoredProc(String colName, Map map, int desiredSqlType) throws SQLException { + return null; + } + + public String getServerInfo() { + return null; + } + + public long getUpdateCount() { + return 0; + } + + public long getUpdateID() { + return 0; + } + + public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData) { + cachedMetaData.getFields(); + } + + public void initializeWithMetadata() throws SQLException { + } + + public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData) throws SQLException { + } + + public void realClose(boolean calledExplicitly) throws SQLException { + } + + public boolean isClosed() { + return false; + } + + public boolean reallyResult() { + return false; + } + + public void redefineFieldsForDBMD(Field[] metadataFields) { + } + + public void setFirstCharOfQuery(char firstCharUpperCase) { + } + + public void setOwningStatement(StatementImpl owningStatement) { + } + + public void setStatementUsedForFetchingRows(com.mysql.jdbc.PreparedStatement stmt) { + } + + public void setWrapperStatement(Statement wrapperStatement) { + } + + public boolean absolute(int row) throws SQLException { + return false; + } + + public void afterLast() throws SQLException { + } + + public void beforeFirst() throws SQLException { + } + + public void cancelRowUpdates() throws SQLException { + } + + public void clearWarnings() throws SQLException { + } + + public void close() throws SQLException { + } + + public void deleteRow() throws SQLException { + } + + public int findColumn(String columnName) throws SQLException { + return 0; + } + + public boolean first() throws SQLException { + return false; + } + + public Array getArray(int i) throws SQLException { + return null; + } + + public Array getArray(String colName) throws SQLException { + return null; + } + + public InputStream getAsciiStream(int columnIndex) throws SQLException { + return null; + } + + public InputStream getAsciiStream(String columnName) throws SQLException { + return null; + } + + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return null; + } + + public BigDecimal getBigDecimal(String columnName) throws SQLException { + return null; + } + + @Deprecated + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + return null; + } + + @Deprecated + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { + return null; + } + + public InputStream getBinaryStream(int columnIndex) throws SQLException { + return null; + } + + public InputStream getBinaryStream(String columnName) throws SQLException { + return null; + } + + public Blob getBlob(int i) throws SQLException { + return null; + } + + public Blob getBlob(String colName) throws SQLException { + return null; + } + + public boolean getBoolean(int columnIndex) throws SQLException { + return false; + } + + public boolean getBoolean(String columnName) throws SQLException { + return false; + } + + public byte getByte(int columnIndex) throws SQLException { + return 0; + } + + public byte getByte(String columnName) throws SQLException { + return 0; + } + + public byte[] getBytes(int columnIndex) throws SQLException { + return null; + } + + public byte[] getBytes(String columnName) throws SQLException { + return null; + } + + public Reader getCharacterStream(int columnIndex) throws SQLException { + return null; + } + + public Reader getCharacterStream(String columnName) throws SQLException { + return null; + } + + public Clob getClob(int i) throws SQLException { + return null; + } + + public Clob getClob(String colName) throws SQLException { + return null; + } + + public int getConcurrency() throws SQLException { + return 0; + } + + public String getCursorName() throws SQLException { + return null; + } + + public Date getDate(int columnIndex) throws SQLException { + return null; + } + + public Date getDate(String columnName) throws SQLException { + return null; + } + + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + public Date getDate(String columnName, Calendar cal) throws SQLException { + return null; + } + + public double getDouble(int columnIndex) throws SQLException { + return 0; + } + + public double getDouble(String columnName) throws SQLException { + return 0; + } + + public int getFetchDirection() throws SQLException { + return 0; + } + + public int getFetchSize() throws SQLException { + return 0; + } + + public float getFloat(int columnIndex) throws SQLException { + return 0; + } + + public float getFloat(String columnName) throws SQLException { + return 0; + } + + public int getInt(int columnIndex) throws SQLException { + return 0; + } + + public int getInt(String columnName) throws SQLException { + return 0; + } + + public long getLong(int columnIndex) throws SQLException { + return 0; + } + + public long getLong(String columnName) throws SQLException { + return 0; + } + + public ResultSetMetaData getMetaData() throws SQLException { + return null; + } + + public Object getObject(int columnIndex) throws SQLException { + return null; + } + + public Object getObject(String columnName) throws SQLException { + return null; + } + + public Object getObject(int arg0, Map> arg1) throws SQLException { + return null; + } + + public Object getObject(String arg0, Map> arg1) throws SQLException { + return null; + } + + public Ref getRef(int i) throws SQLException { + return null; + } + + public Ref getRef(String colName) throws SQLException { + return null; + } + + public int getRow() throws SQLException { + return 0; + } + + public short getShort(int columnIndex) throws SQLException { + return 0; + } + + public short getShort(String columnName) throws SQLException { + return 0; + } + + public Statement getStatement() throws SQLException { + return null; + } + + public String getString(int columnIndex) throws SQLException { + return null; + } + + public String getString(String columnName) throws SQLException { + return null; + } + + public Time getTime(int columnIndex) throws SQLException { + return null; + } + + public Time getTime(String columnName) throws SQLException { + return null; + } + + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + public Time getTime(String columnName, Calendar cal) throws SQLException { + return null; + } + + public Timestamp getTimestamp(int columnIndex) throws SQLException { + return null; + } + + public Timestamp getTimestamp(String columnName) throws SQLException { + return null; + } + + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { + return null; + } + + public int getType() throws SQLException { + return 0; + } + + public URL getURL(int columnIndex) throws SQLException { + return null; + } + + public URL getURL(String columnName) throws SQLException { + return null; + } + + @Deprecated + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + return null; + } + + @Deprecated + public InputStream getUnicodeStream(String columnName) throws SQLException { + return null; + } + + public SQLWarning getWarnings() throws SQLException { + return null; + } + + public void insertRow() throws SQLException { + } + + public boolean isAfterLast() throws SQLException { + return false; + } + + public boolean isBeforeFirst() throws SQLException { + return false; + } + + public boolean isFirst() throws SQLException { + return false; + } + + public boolean isLast() throws SQLException { + return false; + } + + public boolean last() throws SQLException { + return false; + } + + public void moveToCurrentRow() throws SQLException { + } + + public void moveToInsertRow() throws SQLException { + } + + public boolean next() throws SQLException { + return false; + } + + public boolean previous() throws SQLException { + return false; + } + + public void refreshRow() throws SQLException { + } + + public boolean relative(int rows) throws SQLException { + return false; + } + + public boolean rowDeleted() throws SQLException { + return false; + } + + public boolean rowInserted() throws SQLException { + return false; + } + + public boolean rowUpdated() throws SQLException { + return false; + } + + public void setFetchDirection(int direction) throws SQLException { + } + + public void setFetchSize(int rows) throws SQLException { + } + + public void updateArray(int columnIndex, Array x) throws SQLException { + } + + public void updateArray(String columnName, Array x) throws SQLException { + } + + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + } + + public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException { + } + + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + + } + + public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { + } + + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + } + + public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException { + } + + public void updateBlob(int columnIndex, Blob x) throws SQLException { + } + + public void updateBlob(String columnName, Blob x) throws SQLException { + } + + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + } + + public void updateBoolean(String columnName, boolean x) throws SQLException { + } + + public void updateByte(int columnIndex, byte x) throws SQLException { + } + + public void updateByte(String columnName, byte x) throws SQLException { + } + + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + } + + public void updateBytes(String columnName, byte[] x) throws SQLException { + } + + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + } + + public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException { + } + + public void updateClob(int columnIndex, Clob x) throws SQLException { + } + + public void updateClob(String columnName, Clob x) throws SQLException { + } + + public void updateDate(int columnIndex, Date x) throws SQLException { + } + + public void updateDate(String columnName, Date x) throws SQLException { + } + + public void updateDouble(int columnIndex, double x) throws SQLException { + } + + public void updateDouble(String columnName, double x) throws SQLException { + } + + public void updateFloat(int columnIndex, float x) throws SQLException { + } + + public void updateFloat(String columnName, float x) throws SQLException { + } + + public void updateInt(int columnIndex, int x) throws SQLException { + } + + public void updateInt(String columnName, int x) throws SQLException { + } + + public void updateLong(int columnIndex, long x) throws SQLException { + } + + public void updateLong(String columnName, long x) throws SQLException { + } + + public void updateNull(int columnIndex) throws SQLException { + } + + public void updateNull(String columnName) throws SQLException { + } + + public void updateObject(int columnIndex, Object x) throws SQLException { + } + + public void updateObject(String columnName, Object x) throws SQLException { + } + + public void updateObject(int columnIndex, Object x, int scale) throws SQLException { + } + + public void updateObject(String columnName, Object x, int scale) throws SQLException { + } + + public void updateRef(int columnIndex, Ref x) throws SQLException { + } + + public void updateRef(String columnName, Ref x) throws SQLException { + } + + public void updateRow() throws SQLException { + } + + public void updateShort(int columnIndex, short x) throws SQLException { + } + + public void updateShort(String columnName, short x) throws SQLException { + } + + public void updateString(int columnIndex, String x) throws SQLException { + } + + public void updateString(String columnName, String x) throws SQLException { + } + + public void updateTime(int columnIndex, Time x) throws SQLException { + } + + public void updateTime(String columnName, Time x) throws SQLException { + } + + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + } + + public void updateTimestamp(String columnName, Timestamp x) throws SQLException { + } + + public boolean wasNull() throws SQLException { + return false; + } + + public int getBytesSize() throws SQLException { + return 0; + } + }; + } + + /** + * Tests fix for BUG#34093 - Statements with batched values do not return + * correct values for getGeneratedKeys() when "rewriteBatchedStatements" is + * set to "true", and the statement has an "ON DUPLICATE KEY UPDATE" clause. + * + * @throws Exception + * if the test fails. + */ + public void testBug34093() throws Exception { + Connection rewriteConn = null; + + rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true"); + + checkBug34093(rewriteConn); + + rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true,useServerPrepStmts=true"); + + checkBug34093(rewriteConn); + } + + private void checkBug34093(Connection rewriteConn) throws Exception { + try { + String ddl = "(autoIncId INT NOT NULL PRIMARY KEY AUTO_INCREMENT, uniqueTextKey VARCHAR(255), UNIQUE KEY (uniqueTextKey(100)))"; + + String[] sequence = { "c", "a", "d", "b" }; + String sql = "insert into testBug30493 (uniqueTextKey) values (?) on duplicate key UPDATE autoIncId = last_insert_id( autoIncId )"; + String tablePrimeSql = "INSERT INTO testBug30493 (uniqueTextKey) VALUES ('a'), ('b'), ('c'), ('d')"; + + // setup the rewritten and non-written statements + Statement stmts[] = new Statement[2]; + PreparedStatement pstmts[] = new PreparedStatement[2]; + stmts[0] = this.conn.createStatement(); + stmts[1] = rewriteConn.createStatement(); + pstmts[0] = this.conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + pstmts[1] = rewriteConn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + for (int i = 0; i < sequence.length; ++i) { + String sqlLiteral = sql.replaceFirst("\\?", "'" + sequence[i] + "'"); + stmts[0].addBatch(sqlLiteral); + stmts[1].addBatch(sqlLiteral); + pstmts[0].setString(1, sequence[i]); + pstmts[0].addBatch(); + pstmts[1].setString(1, sequence[i]); + pstmts[1].addBatch(); + } + + // run the test once for Statement, and once for PreparedStatement + Statement stmtSets[][] = new Statement[2][]; + stmtSets[0] = stmts; + stmtSets[1] = pstmts; + + for (int stmtSet = 0; stmtSet < 2; ++stmtSet) { + Statement testStmts[] = stmtSets[stmtSet]; + createTable("testBug30493", ddl); + this.stmt.executeUpdate(tablePrimeSql); + + int nonRwUpdateCounts[] = testStmts[0].executeBatch(); + + ResultSet nonRewrittenRsKeys = testStmts[0].getGeneratedKeys(); + + createTable("testBug30493", ddl); + this.stmt.executeUpdate(tablePrimeSql); + int expectedUpdateCount = versionMeetsMinimum(5, 1, 0) ? 2 : 1; + + // behavior changed by fix of Bug#46675, affects servers starting from 5.5.16 and 5.6.3 + if (versionMeetsMinimum(5, 5, 16)) { + expectedUpdateCount = 1; + } + + int rwUpdateCounts[] = testStmts[1].executeBatch(); + ResultSet rewrittenRsKeys = testStmts[1].getGeneratedKeys(); + for (int i = 0; i < 4; ++i) { + assertEquals(expectedUpdateCount, nonRwUpdateCounts[i]); + assertEquals(expectedUpdateCount, rwUpdateCounts[i]); + } + + assertResultSetLength(nonRewrittenRsKeys, 4); + assertResultSetLength(rewrittenRsKeys, 4); + + assertResultSetsEqual(nonRewrittenRsKeys, rewrittenRsKeys); + } + } finally { + if (rewriteConn != null) { + rewriteConn.close(); + } + } + } + + public void testBug34093_nonbatch() throws Exception { + Connection rewriteConn = null; + + try { + String ddl = "(autoIncId INT NOT NULL PRIMARY KEY AUTO_INCREMENT, uniqueTextKey VARCHAR(255) UNIQUE KEY)"; + + String sql = "insert into testBug30493 (uniqueTextKey) values ('c') on duplicate key UPDATE autoIncId = last_insert_id( autoIncId )"; + String tablePrimeSql = "INSERT INTO testBug30493 (uniqueTextKey) VALUES ('a'), ('b'), ('c'), ('d')"; + + try { + createTable("testBug30493", ddl); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max key length") != -1) { + createTable("testBug30493", "(autoIncId INT NOT NULL PRIMARY KEY AUTO_INCREMENT, uniqueTextKey VARCHAR(180) UNIQUE KEY)"); + } + } + this.stmt.executeUpdate(tablePrimeSql); + + Statement stmt1 = this.conn.createStatement(); + stmt1.execute(sql, Statement.RETURN_GENERATED_KEYS); + int expectedUpdateCount = versionMeetsMinimum(5, 1, 0) ? 2 : 1; + + // behavior changed by fix of Bug#46675, affects servers starting from 5.5.16 and 5.6.3 + if (versionMeetsMinimum(5, 5, 16)) { + expectedUpdateCount = 1; + } + + assertEquals(expectedUpdateCount, stmt1.getUpdateCount()); + ResultSet stmtKeys = stmt1.getGeneratedKeys(); + assertResultSetLength(stmtKeys, 1); + + try { + createTable("testBug30493", ddl); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max key length") != -1) { + createTable("testBug30493", "(autoIncId INT NOT NULL PRIMARY KEY AUTO_INCREMENT, uniqueTextKey VARCHAR(180) UNIQUE KEY)"); + } + } + this.stmt.executeUpdate(tablePrimeSql); + + this.pstmt = this.conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + this.pstmt.execute(); + assertEquals(expectedUpdateCount, this.pstmt.getUpdateCount()); + ResultSet pstmtKeys = this.pstmt.getGeneratedKeys(); + assertResultSetLength(pstmtKeys, 1); + + assertResultSetsEqual(stmtKeys, pstmtKeys); + } finally { + if (rewriteConn != null) { + rewriteConn.close(); + } + } + } + + public void testBug34518() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection fetchConn = getConnectionWithProps("useCursorFetch=true"); + Statement fetchStmt = fetchConn.createStatement(); + + int stmtCount = ((com.mysql.jdbc.Connection) fetchConn).getActiveStatementCount(); + + fetchStmt.setFetchSize(100); + this.rs = fetchStmt.executeQuery("SELECT 1"); + + assertEquals(((com.mysql.jdbc.Connection) fetchConn).getActiveStatementCount(), stmtCount + 1); + this.rs.close(); + assertEquals(((com.mysql.jdbc.Connection) fetchConn).getActiveStatementCount(), stmtCount); + } + + public void testBug35170() throws Exception { + Statement stt = null; + + try { + stt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + stt.setFetchSize(Integer.MIN_VALUE); + this.rs = stt.executeQuery("select 1"); + this.rs.next(); + while (!this.rs.isAfterLast()) { + this.rs.getString(1); + this.rs.next(); + } + } finally { + if (stt != null) { + stt.close(); + } + } + + } + + /* + * public void testBug35307() throws Exception { createTable("testBug35307", + * "(`id` int(11) unsigned NOT NULL auto_increment," + + * "`field` varchar(20) NOT NULL," + "`date` datetime NOT NULL," + + * "PRIMARY KEY (`id`)" + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"); + * + * this.stmt.executeUpdate("INSERT INTO testBug35307 (field) values ('works')" + * ); } + */ + + public void testBug35666() throws Exception { + Connection loggingConn = getConnectionWithProps("logSlowQueries=true"); + this.pstmt = ((com.mysql.jdbc.Connection) loggingConn).serverPrepareStatement("SELECT SLEEP(4)"); + this.pstmt.execute(); + } + + public void testDeadlockBatchBehavior() throws Exception { + try { + createTable("t1", "(id INTEGER, x INTEGER)", "INNODB"); + createTable("t2", "(id INTEGER, x INTEGER)", "INNODB"); + this.stmt.executeUpdate("INSERT INTO t1 VALUES (0, 0)"); + + this.conn.setAutoCommit(false); + this.rs = this.conn.createStatement().executeQuery("SELECT * FROM t1 WHERE id=0 FOR UPDATE"); + + final Connection deadlockConn = getConnectionWithProps("includeInnodbStatusInDeadlockExceptions=true"); + deadlockConn.setAutoCommit(false); + + final Statement deadlockStmt = deadlockConn.createStatement(); + deadlockStmt.executeUpdate("INSERT INTO t2 VALUES (1, 0)"); + this.rs = deadlockStmt.executeQuery("SELECT * FROM t2 WHERE id=0 FOR UPDATE"); + + new Thread() { + @Override + public void run() { + try { + deadlockStmt.addBatch("INSERT INTO t2 VALUES (1, 0)"); + deadlockStmt.addBatch("INSERT INTO t2 VALUES (2, 0)"); + deadlockStmt.addBatch("UPDATE t1 SET x=2 WHERE id=0"); + deadlockStmt.executeBatch(); + } catch (SQLException sqlEx) { + sqlEx.printStackTrace(); + try { + deadlockConn.rollback(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + }.run(); + + this.stmt.executeUpdate("INSERT INTO t1 VALUES (0, 0)"); + + } catch (BatchUpdateException sqlEx) { + int[] updateCounts = sqlEx.getUpdateCounts(); + for (int i = 0; i < updateCounts.length; i++) { + System.out.println(updateCounts[i]); + } + } finally { + this.conn.rollback(); + this.conn.setAutoCommit(true); + } + } + + public void testBug39352() throws Exception { + Connection affectedRowsConn = getConnectionWithProps("useAffectedRows=true"); + + try { + + createTable("bug39352", "(id INT PRIMARY KEY, data VARCHAR(100))"); + assertEquals(1, this.stmt.executeUpdate("INSERT INTO bug39352 (id,data) values (1,'a')")); + int rowsAffected = this.stmt.executeUpdate("INSERT INTO bug39352 (id, data) VALUES(2, 'bb') ON DUPLICATE KEY UPDATE data=values(data)"); + assertEquals("First UPD failed", 1, rowsAffected); + + rowsAffected = affectedRowsConn.createStatement() + .executeUpdate("INSERT INTO bug39352 (id, data) VALUES(2, 'bbb') ON DUPLICATE KEY UPDATE data=values(data)"); + assertEquals("2nd UPD failed", 2, rowsAffected); + + rowsAffected = affectedRowsConn.createStatement() + .executeUpdate("INSERT INTO bug39352 (id, data) VALUES(2, 'bbb') ON DUPLICATE KEY UPDATE data=values(data)"); + assertEquals("3rd UPD failed", 0, rowsAffected); + + } finally { + affectedRowsConn.close(); + } + } + + public void testBug38747() throws Exception { + try { + this.conn.setReadOnly(true); + this.pstmt = this.conn.prepareStatement("SELECT 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + this.pstmt.setFetchSize(Integer.MIN_VALUE); + + this.rs = this.pstmt.executeQuery(); + + while (this.rs.next()) { + } + + this.rs.close(); + this.pstmt.close(); + + } finally { + this.conn.setReadOnly(false); + } + } + + public void testBug39956() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + ResultSet enginesRs = this.conn.createStatement().executeQuery("SHOW ENGINES"); + + while (enginesRs.next()) { + if ("YES".equalsIgnoreCase(enginesRs.getString("Support")) || "DEFAULT".equalsIgnoreCase(enginesRs.getString("Support"))) { + + String engineName = enginesRs.getString("Engine"); + + if ("CSV".equalsIgnoreCase(engineName) || "BLACKHOLE".equalsIgnoreCase(engineName) || "FEDERATED".equalsIgnoreCase(engineName) + || "MRG_MYISAM".equalsIgnoreCase(engineName) || "PARTITION".equalsIgnoreCase(engineName) || "EXAMPLE".equalsIgnoreCase(engineName) + || "PERFORMANCE_SCHEMA".equalsIgnoreCase(engineName) || engineName.endsWith("_SCHEMA")) { + continue; // not supported + } + + if ("ARCHIVE".equalsIgnoreCase(engineName) && !versionMeetsMinimum(5, 1, 6)) { + continue; + } + + String tableName = "testBug39956_" + engineName; + + Connection twoConn = getConnectionWithProps("sessionVariables=auto_increment_increment=2"); + + try { + for (int i = 0; i < 2; i++) { + createTable(tableName, "(k int primary key auto_increment, p varchar(4)) ENGINE=" + engineName); + + ((com.mysql.jdbc.Connection) twoConn).setRewriteBatchedStatements(i == 1); + + this.pstmt = twoConn.prepareStatement("INSERT INTO " + tableName + " (p) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setString(1, "a"); + this.pstmt.addBatch(); + this.pstmt.setString(1, "b"); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + this.rs = this.pstmt.getGeneratedKeys(); + + this.rs.next(); + assertEquals("For engine " + engineName + ((i == 1) ? " rewritten " : " plain "), 1, this.rs.getInt(1)); + this.rs.next(); + assertEquals("For engine " + engineName + ((i == 1) ? " rewritten " : " plain "), 3, this.rs.getInt(1)); + + createTable(tableName, "(k int primary key auto_increment, p varchar(4)) ENGINE=" + engineName); + Statement twoStmt = twoConn.createStatement(); + for (int j = 0; j < 10; j++) { + twoStmt.addBatch("INSERT INTO " + tableName + " (p) VALUES ('" + j + "')"); + } + + twoStmt.executeBatch(); // No getGeneratedKeys() support in JDBC spec, but we allow it...might have to rewrite test if/when we don't + this.rs = twoStmt.getGeneratedKeys(); + + int key = 1; + + for (int j = 0; j < 10; j++) { + this.rs.next(); + assertEquals("For engine " + engineName + ((i == 1) ? " rewritten " : " plain "), key, this.rs.getInt(1)); + key += 2; + } + } + } finally { + if (twoConn != null) { + twoConn.close(); + } + } + } + } + } + + public void testBug34185() throws Exception { + this.rs = this.stmt.executeQuery("SELECT 1"); + + try { + this.stmt.getGeneratedKeys(); + fail("Expected exception"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + + this.pstmt = this.conn.prepareStatement("SELECT 1"); + + try { + this.pstmt.execute(); + this.pstmt.getGeneratedKeys(); + fail("Expected exception"); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState()); + } + } + + public void testBug41161() throws Exception { + createTable("testBug41161", "(a int, b int)"); + + Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true"); + + try { + this.pstmt = rewriteConn.prepareStatement("INSERT INTO testBug41161 (a, b) VALUES (?, ?, ?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 1); + + try { + this.pstmt.addBatch(); + fail("Should have thrown an exception"); + } catch (SQLException sqlEx) { + assertEquals("07001", sqlEx.getSQLState()); + } + + this.pstmt.executeBatch(); // NPE when this bug exists + } finally { + rewriteConn.close(); + } + } + + /** + * Ensures that cases listed in Bug#41448 actually work - we don't think + * there's a bug here right now + */ + + public void testBug41448() throws Exception { + createTable("testBug41448", "(pk INT PRIMARY KEY AUTO_INCREMENT, field1 VARCHAR(4))"); + + this.stmt.executeUpdate("INSERT INTO testBug41448 (field1) VALUES ('abc')", Statement.RETURN_GENERATED_KEYS); + this.stmt.getGeneratedKeys(); + + this.stmt.executeUpdate("INSERT INTO testBug41448 (field1) VALUES ('def')", new int[] { 1 }); + this.stmt.getGeneratedKeys(); + + this.stmt.executeUpdate("INSERT INTO testBug41448 (field1) VALUES ('ghi')", new String[] { "pk" }); + this.stmt.getGeneratedKeys(); + + this.stmt.executeUpdate("INSERT INTO testBug41448 (field1) VALUES ('ghi')"); + + try { + this.stmt.getGeneratedKeys(); + fail("Expected a SQLException here"); + } catch (SQLException sqlEx) { + // expected + } + + this.stmt.execute("INSERT INTO testBug41448 (field1) VALUES ('jkl')", Statement.RETURN_GENERATED_KEYS); + this.stmt.getGeneratedKeys(); + + this.stmt.execute("INSERT INTO testBug41448 (field1) VALUES ('mno')", new int[] { 1 }); + this.stmt.getGeneratedKeys(); + + this.stmt.execute("INSERT INTO testBug41448 (field1) VALUES ('pqr')", new String[] { "pk" }); + this.stmt.getGeneratedKeys(); + + this.stmt.execute("INSERT INTO testBug41448 (field1) VALUES ('stu')"); + + try { + this.stmt.getGeneratedKeys(); + fail("Expected a SQLException here"); + } catch (SQLException sqlEx) { + // expected + } + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug41448 (field1) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setString(1, "abc"); + this.pstmt.executeUpdate(); + this.pstmt.getGeneratedKeys(); + this.pstmt.execute(); + this.pstmt.getGeneratedKeys(); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug41448 (field1) VALUES (?)", new int[] { 1 }); + this.pstmt.setString(1, "abc"); + this.pstmt.executeUpdate(); + this.pstmt.getGeneratedKeys(); + this.pstmt.execute(); + this.pstmt.getGeneratedKeys(); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug41448 (field1) VALUES (?)", new String[] { "pk" }); + this.pstmt.setString(1, "abc"); + this.pstmt.executeUpdate(); + this.pstmt.getGeneratedKeys(); + this.pstmt.execute(); + this.pstmt.getGeneratedKeys(); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug41448 (field1) VALUES (?)"); + this.pstmt.setString(1, "abc"); + this.pstmt.executeUpdate(); + try { + this.pstmt.getGeneratedKeys(); + fail("Expected a SQLException here"); + } catch (SQLException sqlEx) { + // expected + } + + this.pstmt.execute(); + + try { + this.pstmt.getGeneratedKeys(); + fail("Expected a SQLException here"); + } catch (SQLException sqlEx) { + // expected + } + } + + public void testBug48172() throws Exception { + createTable("testBatchInsert", "(a INT PRIMARY KEY AUTO_INCREMENT)"); + Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true,dumpQueriesOnException=true"); + assertEquals("0", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + + this.pstmt = rewriteConn.prepareStatement("INSERT INTO testBatchInsert VALUES (?)"); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + assertEquals("1", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + this.pstmt = rewriteConn.prepareStatement("INSERT INTO `testBatchInsert`VALUES (?)"); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + assertEquals("2", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + + this.pstmt = rewriteConn.prepareStatement("INSERT INTO testBatchInsert VALUES(?)"); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + assertEquals("3", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + + this.pstmt = rewriteConn.prepareStatement("INSERT INTO testBatchInsert VALUES\n(?)"); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.setNull(1, java.sql.Types.INTEGER); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + assertEquals("4", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + + } + + /** + * Tests fix for Bug#41532 - regression in performance for batched inserts + * when using ON DUPLICATE KEY UPDATE + */ + public void testBug41532() throws Exception { + createTable("testBug41532", "(ID INTEGER, S1 VARCHAR(100), S2 VARCHAR(100), S3 VARCHAR(100), D1 DATETIME, D2 DATETIME, D3 DATETIME, " + + "N1 DECIMAL(28,6), N2 DECIMAL(28,6), N3 DECIMAL(28,6), UNIQUE KEY UNIQUE_KEY_TEST_DUPLICATE (ID) )"); + + int numTests = 5000; + Connection rewriteConn = getConnectionWithProps("useSSL=false,rewriteBatchedStatements=true,dumpQueriesOnException=true"); + + assertEquals("0", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + long batchedTime = timeBatch(rewriteConn, numTests); + assertEquals("1", getSingleIndexedValueWithQuery(rewriteConn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + + this.stmt.executeUpdate("TRUNCATE TABLE testBug41532"); + + assertEquals("0", getSingleIndexedValueWithQuery(this.conn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + long unbatchedTime = timeBatch(this.conn, numTests); + assertEquals(String.valueOf(numTests), getSingleIndexedValueWithQuery(this.conn, 2, "SHOW SESSION STATUS LIKE 'Com_insert'").toString()); + assertTrue(batchedTime < unbatchedTime); + + rewriteConn = getConnectionWithProps("useSSL=false,rewriteBatchedStatements=true,useCursorFetch=true,defaultFetchSize=10000"); + timeBatch(rewriteConn, numTests); + } + + private long timeBatch(Connection c, int numberOfRows) throws SQLException { + this.pstmt = c.prepareStatement("INSERT INTO testBug41532(ID, S1, S2, S3, D1, D2, D3, N1, N2, N3) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + + " ON DUPLICATE KEY UPDATE S1 = VALUES(S1), S2 = VALUES(S2), S3 = VALUES(S3), D1 = VALUES(D1), D2 =" + + " VALUES(D2), D3 = VALUES(D3), N1 = N1 + VALUES(N1), N2 = N2 + VALUES(N2), N2 = N2 + VALUES(N2)"); + c.setAutoCommit(false); + c.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + Date d1 = new Date(currentTimeMillis()); + Date d2 = new Date(currentTimeMillis() + 1000000); + Date d3 = new Date(currentTimeMillis() + 1250000); + + for (int i = 0; i < numberOfRows; i++) { + this.pstmt.setObject(1, new Integer(i), Types.INTEGER); + this.pstmt.setObject(2, String.valueOf(i), Types.VARCHAR); + this.pstmt.setObject(3, String.valueOf(i * 0.1), Types.VARCHAR); + this.pstmt.setObject(4, String.valueOf(i / 3), Types.VARCHAR); + this.pstmt.setObject(5, new Timestamp(d1.getTime()), Types.TIMESTAMP); + this.pstmt.setObject(6, new Timestamp(d2.getTime()), Types.TIMESTAMP); + this.pstmt.setObject(7, new Timestamp(d3.getTime()), Types.TIMESTAMP); + this.pstmt.setObject(8, new BigDecimal(i + 0.1), Types.DECIMAL); + this.pstmt.setObject(9, new BigDecimal(i * 0.1), Types.DECIMAL); + this.pstmt.setObject(10, new BigDecimal(i / 3), Types.DECIMAL); + this.pstmt.addBatch(); + } + long startTime = currentTimeMillis(); + this.pstmt.executeBatch(); + c.commit(); + long stopTime = currentTimeMillis(); + + this.rs = this.conn.createStatement().executeQuery("SELECT COUNT(*) FROM testBug41532"); + assertTrue(this.rs.next()); + assertEquals(numberOfRows, this.rs.getInt(1)); + + return stopTime - startTime; + } + + /** + * Tests fix for Bug#44056 - Statement.getGeneratedKeys() retains result set + * instances until statement is closed. + */ + + public void testBug44056() throws Exception { + createTable("testBug44056", "(pk int primary key not null auto_increment)"); + Statement newStmt = this.conn.createStatement(); + + try { + newStmt.executeUpdate("INSERT INTO testBug44056 VALUES (null)", Statement.RETURN_GENERATED_KEYS); + checkOpenResultsFor44056(newStmt); + this.pstmt = this.conn.prepareStatement("INSERT INTO testBug44056 VALUES (null)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.executeUpdate(); + checkOpenResultsFor44056(this.pstmt); + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("INSERT INTO testBug44056 VALUES (null)", + Statement.RETURN_GENERATED_KEYS); + this.pstmt.executeUpdate(); + checkOpenResultsFor44056(this.pstmt); + } finally { + newStmt.close(); + } + } + + private void checkOpenResultsFor44056(Statement newStmt) throws SQLException { + this.rs = newStmt.getGeneratedKeys(); + assertEquals(0, ((com.mysql.jdbc.Statement) newStmt).getOpenResultSetCount()); + this.rs.close(); + assertEquals(0, ((com.mysql.jdbc.Statement) newStmt).getOpenResultSetCount()); + } + + /** + * Bug #41730 - SQL Injection when using U+00A5 and SJIS/Windows-31J + */ + public void testBug41730() throws Exception { + try { + "".getBytes("sjis"); + } catch (UnsupportedEncodingException ex) { + return; // test doesn't work on this platform + } + + Connection conn2 = null; + PreparedStatement pstmt2 = null; + try { + conn2 = getConnectionWithProps("characterEncoding=sjis"); + pstmt2 = conn2.prepareStatement("select ?"); + pstmt2.setString(1, "\u00A5'"); + // this will throw an exception with a syntax error if it fails + this.rs = pstmt2.executeQuery(); + } finally { + try { + if (pstmt2 != null) { + pstmt2.close(); + } + } catch (SQLException ex) { + } + try { + if (conn2 != null) { + conn2.close(); + } + } catch (SQLException ex) { + } + } + } + + public void testBug43196() throws Exception { + createTable("`bug43196`", + "(`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, `a` bigint(20) unsigned NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1;"); + + Connection conn1 = null; + + try { + assertEquals(1, this.stmt.executeUpdate("INSERT INTO bug43196 (a) VALUES (1)", Statement.RETURN_GENERATED_KEYS)); + + this.rs = this.stmt.getGeneratedKeys(); + + if (this.rs.next()) { + + Object id = this.rs.getObject(1);// use long + + assertEquals(Long.class, id.getClass()); + } + + this.rs.close(); + + this.rs = this.stmt.executeQuery("select id from bug43196"); + + if (this.rs.next()) { + Object id = this.rs.getObject(1);// use BigInteger + + assertEquals(BigInteger.class, id.getClass()); + } + + this.rs.close(); + + // insert a id > Long.MAX_VALUE(9223372036854775807) + + assertEquals(1, this.stmt.executeUpdate("insert into bug43196(id,a) values(18446744073709551200,1)", Statement.RETURN_GENERATED_KEYS)); + + this.rs = this.stmt.getGeneratedKeys(); + + this.rs.first(); + + assertTrue("No rows returned", this.rs.isFirst()); + assertEquals("18446744073709551200", this.rs.getObject(1).toString()); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + + /** + * Bug #42253 - multiple escaped quotes cause exception from + * EscapeProcessor. + */ + public void testBug42253() throws Exception { + this.rs = this.stmt.executeQuery("select '\\'\\'','{t\\'}'"); + this.rs.next(); + assertEquals("''", this.rs.getString(1)); + assertEquals("{t'}", this.rs.getString(2)); + } + + /** + * Bug #41566 - Quotes within comments not correctly ignored by escape + * parser + */ + public void testBug41566() throws Exception { + this.rs = this.stmt.executeQuery("-- this should't change the literal\n select '{1}'"); + this.rs.next(); + assertEquals("{1}", this.rs.getString(1)); + } + + /* + * Bug #40439 - Error rewriting batched statement if table name ends with + * "values". + */ + public void testBug40439() throws Exception { + Connection conn2 = null; + try { + createTable("testBug40439VALUES", "(x int)"); + conn2 = getConnectionWithProps("rewriteBatchedStatements=true"); + PreparedStatement ps = conn2.prepareStatement("insert into testBug40439VALUES (x) values (?)"); + ps.setInt(1, 1); + ps.addBatch(); + ps.setInt(1, 2); + ps.addBatch(); + ps.executeBatch(); + } finally { + if (conn2 != null) { + try { + conn2.close(); + } catch (SQLException ex) { + } + } + } + } + + public static class Bug39426Interceptor extends BaseStatementInterceptor { + public static List vals = new ArrayList(); + String prevSql; + + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + + if (interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + String asSql = interceptedStatement.toString(); + int firstColon = asSql.indexOf(":"); + asSql = asSql.substring(firstColon + 2); + + if (asSql.equals(this.prevSql)) { + throw new RuntimeException("Previous statement matched current: " + sql); + } + this.prevSql = asSql; + ParameterBindings b = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).getParameterBindings(); + vals.add(new Integer(b.getInt(1))); + } + return null; + } + } + + /** + * Bug #39426 - executeBatch passes most recent PreparedStatement params to + * StatementInterceptor + */ + public void testBug39426() throws Exception { + Connection c = null; + try { + createTable("testBug39426", "(x int)"); + c = getConnectionWithProps("statementInterceptors=testsuite.regression.StatementRegressionTest$Bug39426Interceptor,useServerPrepStmts=false"); + PreparedStatement ps = c.prepareStatement("insert into testBug39426 values (?)"); + ps.setInt(1, 1); + ps.addBatch(); + ps.setInt(1, 2); + ps.addBatch(); + ps.setInt(1, 3); + ps.addBatch(); + ps.executeBatch(); + List vals = Bug39426Interceptor.vals; + assertEquals(new Integer(1), vals.get(0)); + assertEquals(new Integer(2), vals.get(1)); + assertEquals(new Integer(3), vals.get(2)); + } finally { + if (c != null) { + c.close(); + } + } + } + + public void testBugDupeKeySingle() throws Exception { + createTable("testBugDupeKeySingle", "(field1 int not null primary key)"); + Connection conn2 = null; + try { + conn2 = getConnectionWithProps("rewriteBatchedStatements=true"); + + this.pstmt = conn2.prepareStatement("INSERT INTO testBugDupeKeySingle VALUES (?) ON DUPLICATE KEY UPDATE field1=VALUES(field1)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + + // this should be a syntax error + this.pstmt = conn2.prepareStatement("INSERT INTO testBugDupeKeySingle VALUES (?) ON DUPLICATE KEY UPDATE"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + try { + this.pstmt.executeBatch(); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_SYNTAX_ERROR, sqlEx.getSQLState()); + } + + this.pstmt = conn2.prepareStatement("INSERT INTO testBugDupeKeySingle VALUES (?)"); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.setInt(1, 4); + this.pstmt.executeBatch(); + } finally { + if (conn2 != null) { + conn2.close(); + } + } + } + + /** + * Bug #37458 - MySQL 5.1 returns generated keys in ascending order + */ + public void testBug37458() throws Exception { + int ids[] = { 13, 1, 8 }; + String vals[] = { "c", "a", "b" }; + createTable("testBug37458", "(id int not null auto_increment, val varchar(100), primary key (id), unique (val))"); + this.stmt.executeUpdate("insert into testBug37458 values (1, 'a'), (8, 'b'), (13, 'c')"); + this.pstmt = this.conn.prepareStatement("insert into testBug37458 (val) values (?) on duplicate key update id = last_insert_id(id)", + Statement.RETURN_GENERATED_KEYS); + for (int i = 0; i < ids.length; ++i) { + this.pstmt.setString(1, vals[i]); + this.pstmt.addBatch(); + } + this.pstmt.executeBatch(); + ResultSet keys = this.pstmt.getGeneratedKeys(); + for (int i = 0; i < ids.length; ++i) { + assertTrue(keys.next()); + assertEquals(ids[i], keys.getInt(1)); + } + } + + public void testBug34555() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; // no KILL QUERY prior to this + } + + createTable("testBug34555", "(field1 int)", "INNODB"); + this.stmt.executeUpdate("INSERT INTO testBug34555 VALUES (0)"); + + final Connection lockerConn = getConnectionWithProps(""); + lockerConn.setAutoCommit(false); + lockerConn.createStatement().execute("SELECT * FROM testBug34555 WHERE field1=0 FOR UPDATE"); + + this.conn.setAutoCommit(false); + + this.pstmt = this.conn.prepareStatement("UPDATE testBug34555 SET field1=1 WHERE field1=?"); + this.pstmt.setQueryTimeout(1); + this.pstmt.setInt(1, 0); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + + try { + this.pstmt.executeBatch(); + } catch (BatchUpdateException batchEx) { + assertTrue(batchEx.getMessage().startsWith("Statement cancelled")); + } finally { + this.conn.setAutoCommit(true); + lockerConn.commit(); + } + } + + public void testBug46788() throws Exception { + createTable("testBug46788", "(modified varchar(32), id varchar(32))"); + + Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true"); + + this.pstmt = rewriteConn.prepareStatement("insert into testBug46788 (modified,id) values (?,?) ON DUPLICATE KEY UPDATE modified=?"); + + this.pstmt.setString(1, "theID"); + this.pstmt.setString(2, "Hello_world_"); + this.pstmt.setString(3, "Hello_world_"); + + for (int i = 0; i < 10; i++) { + this.pstmt.addBatch(); + } + + this.pstmt.executeBatch(); + } + + public void testBug31193() throws Exception { + createTable("bug31193", "(sometime datetime, junk text)"); + Connection fetchConn = getConnectionWithProps("useCursorFetch=true"); + Statement fetchStmt = fetchConn.createStatement(); + + fetchStmt.setFetchSize(10000); + + assertEquals(1, fetchStmt.executeUpdate("INSERT INTO bug31193 (sometime) values ('2007-01-01 12:34:56.7')")); + this.rs = fetchStmt.executeQuery("SELECT * FROM bug31193"); + this.rs.next(); + String badDatetime = this.rs.getString("sometime"); + + this.rs = fetchStmt.executeQuery("SELECT sometime FROM bug31193"); + this.rs.next(); + String goodDatetime = this.rs.getString("sometime"); + assertEquals(goodDatetime, badDatetime); + } + + public void testBug51776() throws Exception { + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("socketFactory", "testsuite.UnreliableSocketFactory"); + + NonRegisteringDriver d = new NonRegisteringDriver(); + Properties parsed = d.parseURL(BaseTestCase.dbUrl, null); + String db = parsed.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + String port = parsed.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + String host = getPortFreeHostname(props, d); + + UnreliableSocketFactory.flushAllStaticData(); + UnreliableSocketFactory.mapHost("first", host); + + Connection testConn = getConnectionWithProps("jdbc:mysql://first:" + port + "/" + db, props); + testConn.setAutoCommit(false); + testConn.createStatement().execute("SELECT 1"); + UnreliableSocketFactory.downHost("first"); + try { + testConn.rollback(); + fail("Should receive SQLException on rollback()."); + } catch (SQLException e) { + + } + + } + + public void testBug51666() throws Exception { + Connection testConn = getConnectionWithProps("statementInterceptors=" + TestBug51666StatementInterceptor.class.getName()); + createTable("testStatementInterceptorCount", "(field1 int)"); + this.stmt.executeUpdate("INSERT INTO testStatementInterceptorCount VALUES (0)"); + ResultSet testRs = testConn.createStatement().executeQuery("SHOW SESSION STATUS LIKE 'Com_select'"); + testRs.next(); + int s = testRs.getInt(2); + this.rs = testConn.createStatement().executeQuery("SELECT 1"); + testRs = testConn.createStatement().executeQuery("SHOW SESSION STATUS LIKE 'Com_select'"); + testRs.next(); + assertEquals(s + 1, testRs.getInt(2)); + + } + + public static class TestBug51666StatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection conn) + throws SQLException { + if (sql.equals("SELECT 1")) { + java.sql.Statement test = conn.createStatement(); + return (ResultSetInternalMethods) test.executeQuery("/* execute this, not the original */ SELECT 1"); + } + return null; + } + } + + public void testReversalOfScanFlags() throws Exception { + createTable("testReversalOfScanFlags", "(field1 int)"); + this.stmt.executeUpdate("INSERT INTO testReversalOfScanFlags VALUES (1),(2),(3)"); + + Connection scanningConn = getConnectionWithProps("statementInterceptors=" + ScanDetectingInterceptor.class.getName()); + + try { + this.rs = scanningConn.createStatement().executeQuery("SELECT field1 FROM testReversalOfScanFlags"); + assertTrue(ScanDetectingInterceptor.hasSeenScan); + assertFalse(ScanDetectingInterceptor.hasSeenBadIndex); + } finally { + scanningConn.close(); + } + + } + + public static class ScanDetectingInterceptor extends BaseStatementInterceptor { + static boolean hasSeenScan = false; + static boolean hasSeenBadIndex = false; + + @Override + public ResultSetInternalMethods postProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, ResultSetInternalMethods originalResultSet, + com.mysql.jdbc.Connection connection, int warningCount, boolean noIndexUsed, boolean noGoodIndexUsed, SQLException statementException) + throws SQLException { + if (noIndexUsed) { + hasSeenScan = true; + } + + if (noGoodIndexUsed) { + hasSeenBadIndex = true; + } + + return null; + } + } + + /** + * Tests fix for Bug#51704, rewritten batched statements don't honor escape + * processing flag of Statement that they are created for + */ + public void testBug51704() throws Exception { + createTable("testBug51704", "(field1 TIMESTAMP)"); + Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true"); + Statement rewriteStmt = rewriteConn.createStatement(); + + try { + rewriteStmt.setEscapeProcessing(false); + + for (int i = 0; i < 20; i++) { + rewriteStmt.addBatch("INSERT INTO testBug51704 VALUES ({tsp '2002-11-12 10:00:00'})"); + } + + rewriteStmt.executeBatch(); // this should pass, because mysqld doesn't validate any escape sequences, + // it just strips them, where our escape processor validates them + + Statement batchStmt = this.conn.createStatement(); + batchStmt.setEscapeProcessing(false); + batchStmt.addBatch("INSERT INTO testBug51704 VALUES ({tsp '2002-11-12 10:00:00'})"); + batchStmt.executeBatch(); // same here + } finally { + rewriteConn.close(); + } + } + + public void testBug54175() throws Exception { + if (!versionMeetsMinimum(5, 5)) { + return; + } + + Connection utf8conn = getConnectionWithProps("characterEncoding=utf8"); + + createTable("testBug54175", "(a VARCHAR(10)) CHARACTER SET utf8mb4"); + this.stmt.execute("INSERT INTO testBug54175 VALUES(0xF0AFA6B2)"); + this.rs = utf8conn.createStatement().executeQuery("SELECT * FROM testBug54175"); + assertTrue(this.rs.next()); + assertEquals(55422, this.rs.getString(1).charAt(0)); + } + + /** + * Tests fix for Bug#58728, NPE in com.mysql.jdbc.jdbc2.optional.StatementWrappe.getResultSet() + * ((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this); + * when rs is null + */ + public void testBug58728() throws Exception { + createTable("testbug58728", "(Id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, txt VARCHAR(50))", "InnoDB"); + this.stmt.executeUpdate("INSERT INTO testbug58728 VALUES (NULL, 'Text 1'), (NULL, 'Text 2')"); + + MysqlConnectionPoolDataSource pds = new MysqlConnectionPoolDataSource(); + pds.setUrl(dbUrl); + Statement stmt1 = pds.getPooledConnection().getConnection().createStatement(); + stmt1.executeUpdate("UPDATE testbug58728 SET txt = 'New text' WHERE Id > 0"); + ResultSet rs1 = stmt1.getResultSet(); + stmt1.close(); + if (rs1 != null) { + rs1.close(); + } + } + + public void testBug61501() throws Exception { + createTable("testBug61501", "(id int)"); + this.stmt.executeUpdate("INSERT INTO testBug61501 VALUES (1)"); + String sql = "SELECT id FROM testBug61501 where id=1"; + this.pstmt = this.conn.prepareStatement(sql); + this.rs = this.pstmt.executeQuery(); + this.pstmt.cancel(); + this.pstmt.close(); + + this.pstmt = this.conn.prepareStatement(sql); + this.rs = this.pstmt.executeQuery(); + + this.stmt.cancel(); + this.rs = this.stmt.executeQuery(sql); + this.stmt.cancel(); + this.stmt.execute(sql); + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement(sql); + this.pstmt.execute(); + this.pstmt.cancel(); + this.pstmt.execute(); + + sql = "INSERT INTO testBug61501 VALUES (2)"; + this.pstmt = this.conn.prepareStatement(sql); + this.pstmt.execute(); + assertEquals(1, this.pstmt.getUpdateCount()); + this.pstmt.cancel(); + this.pstmt.close(); + + this.pstmt = this.conn.prepareStatement(sql); + assertEquals(1, this.pstmt.executeUpdate()); + + this.stmt.cancel(); + assertEquals(1, this.stmt.executeUpdate(sql)); + this.stmt.cancel(); + this.stmt.execute(sql); + assertEquals(1, this.stmt.getUpdateCount()); + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement(sql); + this.pstmt.execute(); + assertEquals(1, this.pstmt.getUpdateCount()); + this.pstmt.cancel(); + this.pstmt.close(); + + this.pstmt = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement(sql); + assertEquals(1, this.pstmt.executeUpdate()); + + this.pstmt.cancel(); + this.pstmt.addBatch(); + this.pstmt.addBatch(); + this.pstmt.addBatch(); + int[] counts = this.pstmt.executeBatch(); + + for (int i = 0; i < counts.length; i++) { + assertEquals(1, counts[i]); + } + + this.pstmt = this.conn.prepareStatement(sql); + this.pstmt.cancel(); + this.pstmt.addBatch(); + this.pstmt.addBatch(); + this.pstmt.addBatch(); + counts = this.pstmt.executeBatch(); + + for (int i = 0; i < counts.length; i++) { + assertEquals(1, counts[i]); + } + + this.stmt.cancel(); + this.stmt.addBatch(sql); + this.stmt.addBatch(sql); + this.stmt.addBatch(sql); + + counts = this.stmt.executeBatch(); + + for (int i = 0; i < counts.length; i++) { + assertEquals(1, counts[i]); + } + + } + + public void testbug61866() throws Exception { + + createProcedure("WARN_PROCEDURE", "() BEGIN DECLARE l_done INT; SELECT 1 INTO l_done FROM DUAL WHERE 1=2; END"); + this.pstmt = this.conn.prepareCall("{CALL WARN_PROCEDURE()}"); + this.pstmt.execute(); + assertTrue("No warning when expected", + this.pstmt.getWarnings().toString().contentEquals("java.sql.SQLWarning: No data - zero rows fetched, selected, or processed")); + this.pstmt.clearWarnings(); + assertNull("Warning when not expected", this.pstmt.getWarnings()); + } + + public void testbug12565726() throws Exception { + // Not putting the space between VALUES() and ON DUPLICATE KEY UPDATE + // causes C/J a) enter rewriting the query altrhough it has ON UPDATE + // and b) to generate the wrong query with multiple ON DUPLICATE KEY + + Properties props = new Properties(); + props.put("rewriteBatchedStatements", "true"); + props.put("useServerPrepStmts", "false"); + props.put("enablePacketDebug", "true"); + this.conn = getConnectionWithProps(props); + this.stmt = this.conn.createStatement(); + + try { + createTable("testbug12565726", "(id int primary key, txt1 varchar(32))"); + this.stmt.executeUpdate("INSERT INTO testbug12565726 (id, txt1) VALUES (1, 'something')"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testbug12565726 (id, txt1) VALUES (?, ?)ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)+10"); + + this.pstmt.setInt(1, 1); + this.pstmt.setString(2, "something else"); + this.pstmt.addBatch(); + + this.pstmt.setInt(1, 2); + this.pstmt.setString(2, "hope it is not error again!"); + this.pstmt.addBatch(); + + this.pstmt.executeBatch(); + + } finally { + } + + } + + public void testBug36478() throws Exception { + + createTable("testBug36478", "(`limit` varchar(255) not null primary key, id_limit INT, limit1 INT, maxlimit2 INT)"); + + this.stmt.execute("INSERT INTO testBug36478 VALUES ('bahblah',1,1,1)"); + this.stmt.execute("INSERT INTO testBug36478 VALUES ('bahblah2',2,2,2)"); + this.pstmt = this.conn.prepareStatement("select 1 FROM testBug36478"); + + this.pstmt.setMaxRows(1); + this.rs = this.pstmt.executeQuery(); + this.rs.first(); + assertTrue(this.rs.isFirst()); + assertTrue(this.rs.isLast()); + + this.pstmt = this.conn.prepareStatement("select `limit`, id_limit, limit1, maxlimit2 FROM testBug36478"); + this.pstmt.setMaxRows(0); + this.rs = this.pstmt.executeQuery(); + this.rs.first(); + assertTrue(this.rs.isFirst()); + assertFalse(this.rs.isLast()); + + //SSPS + Connection _conn = null; + PreparedStatement s = null; + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + + _conn = getConnectionWithProps(props); + s = _conn.prepareStatement("select 1 FROM testBug36478"); + + s.setMaxRows(1); + ResultSet _rs = s.executeQuery(); + _rs.first(); + assertTrue(_rs.isFirst()); + assertTrue(_rs.isLast()); + + s = _conn.prepareStatement("select `limit`, id_limit, limit1, maxlimit2 FROM testBug36478"); + s.setMaxRows(0); + _rs = s.executeQuery(); + _rs.first(); + assertTrue(_rs.isFirst()); + assertFalse(_rs.isLast()); + + } finally { + if (s != null) { + s.close(); + } + if (_conn != null) { + _conn.close(); + } + } + + } + + /** + * Tests fix for BUG#40279 - Timestamp values get truncated when passed as prepared statement parameters + * (and duplicate BUG#60584 - prepared statements truncate milliseconds) + * + * [13 Sep 2012 21:06] Mark Matthews + * This was fixed with http://bazaar.launchpad.net/~mysql/connectorj/5.1/revision/1107 in 2011, + * it supports MySQL-5.6.4 or later. + * + * But that fix did not cover useLegacyDatetimeCode=true case. + * + * @throws Exception + */ + public void testBug40279() throws Exception { + if (!versionMeetsMinimum(5, 6, 4)) { + return; + } + + createTable("testBug40279", "(f1 int, f2 timestamp(6))"); + + Timestamp ts = new Timestamp(1300791248001L); + + Connection ps_conn_legacy = null; + Connection ps_conn_nolegacy = null; + + Connection ssps_conn_legacy = null; + Connection ssps_conn_nolegacy = null; + + try { + Properties props = new Properties(); + props.setProperty("serverTimezone", "UTC"); + props.setProperty("useLegacyDatetimeCode", "true"); + props.setProperty("useServerPrepStmts", "false"); + ps_conn_legacy = getConnectionWithProps(props); + + props.setProperty("useLegacyDatetimeCode", "false"); + ps_conn_nolegacy = getConnectionWithProps(props); + + props.setProperty("useLegacyDatetimeCode", "true"); + props.setProperty("useServerPrepStmts", "true"); + ssps_conn_legacy = getConnectionWithProps(props); + + props.setProperty("useLegacyDatetimeCode", "false"); + ssps_conn_nolegacy = getConnectionWithProps(props); + + this.pstmt = ps_conn_legacy.prepareStatement("INSERT INTO testBug40279(f1, f2) VALUES (?, ?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setTimestamp(2, ts); + this.pstmt.execute(); + this.pstmt.close(); + + this.pstmt = ps_conn_nolegacy.prepareStatement("INSERT INTO testBug40279(f1, f2) VALUES (?, ?)"); + this.pstmt.setInt(1, 2); + this.pstmt.setTimestamp(2, ts); + this.pstmt.execute(); + this.pstmt.close(); + + this.pstmt = ssps_conn_legacy.prepareStatement("INSERT INTO testBug40279(f1, f2) VALUES (?, ?)"); + this.pstmt.setInt(1, 3); + this.pstmt.setTimestamp(2, ts); + this.pstmt.execute(); + this.pstmt.close(); + + this.pstmt = ssps_conn_nolegacy.prepareStatement("INSERT INTO testBug40279(f1, f2) VALUES (?, ?)"); + this.pstmt.setInt(1, 4); + this.pstmt.setTimestamp(2, ts); + this.pstmt.execute(); + this.pstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT f2 FROM testBug40279"); + while (this.rs.next()) { + assertEquals(ts.getNanos(), this.rs.getTimestamp("f2").getNanos()); + } + + } finally { + if (ps_conn_legacy != null) { + ps_conn_legacy.close(); + } + if (ps_conn_nolegacy != null) { + ps_conn_nolegacy.close(); + } + if (ssps_conn_legacy != null) { + ssps_conn_legacy.close(); + } + if (ssps_conn_nolegacy != null) { + ssps_conn_nolegacy.close(); + } + } + + } + + /** + * Tests fix for BUG#35653 - executeQuery() in Statement.java let "TRUNCATE" queries being executed. + * "RENAME" is also filtered now. + * + * @throws Exception + */ + public void testBug35653() throws Exception { + createTable("testBug35653", "(f1 int)"); + try { + this.rs = this.stmt.executeQuery("TRUNCATE testBug35653"); + fail("executeQuery() shouldn't allow TRUNCATE"); + } catch (SQLException e) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT == e.getSQLState()); + } + + try { + this.rs = this.stmt.executeQuery("RENAME TABLE testBug35653 TO testBug35653_new"); + fail("executeQuery() shouldn't allow RENAME"); + } catch (SQLException e) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT == e.getSQLState()); + } finally { + dropTable("testBug35653_new"); + } + } + + /** + * Tests fix for BUG#64805 - StatementImpl$CancelTask occasionally throws NullPointerExceptions. + * + * @throws Exception + */ + public void testBug64805() throws Exception { + + try { + this.stmt.setQueryTimeout(5); + this.rs = this.stmt.executeQuery("select sleep(5)"); + } catch (NullPointerException e) { + e.printStackTrace(); + fail(); + } catch (Exception e) { + if (e instanceof MySQLTimeoutException) { + // expected behavior in slow environment + } else { + throw e; + } + } + + } + + /** + * WL#4897 - Add EXPLAIN INSERT/UPDATE/DELETE + * + * Added support for EXPLAIN INSERT/REPLACE/UPDATE/DELETE. Connector/J must issue a warning containing the execution + * plan for slow queries when connection properties logSlowQueries=true and explainSlowQueries=true are used. + * + * @throws SQLException + */ + public void testExecutionPlanForSlowQueries() throws Exception { + // once slow query (with execution plan) warning is sent to System.err, we capture messages sent here to check proper operation. + final class TestHandler { + // System.err diversion handling + PrintStream systemErrBackup = null; + ByteArrayOutputStream systemErrDetour = null; + + // Connection handling + Connection testConn = null; + + TestHandler() { + this.systemErrBackup = System.err; + this.systemErrDetour = new ByteArrayOutputStream(8192); + System.setErr(new PrintStream(this.systemErrDetour)); + } + + boolean containsSlowQueryMsg(String lookFor) { + String errMsg = this.systemErrDetour.toString(); + boolean found = false; + + if (errMsg.indexOf("Slow query explain results for '" + lookFor + "'") != -1) { + found = true; + } + this.systemErrDetour.reset(); + // print message in original OutputStream. + this.systemErrBackup.print(errMsg); + return found; + } + + void undoSystemErrDiversion() throws IOException { + this.systemErrBackup.print(this.systemErrDetour.toString()); + this.systemErrDetour.close(); + System.setErr(this.systemErrBackup); + this.systemErrDetour = null; + this.systemErrBackup = null; + } + + @SuppressWarnings("synthetic-access") + Connection getNewConnectionForSlowQueries() throws SQLException { + releaseConnectionResources(); + this.testConn = getConnectionWithProps("logSlowQueries=true,explainSlowQueries=true"); + Statement st = this.testConn.createStatement(); + // execute several fast queries to unlock slow query analysis and lower query execution time mean + for (int i = 0; i < 25; i++) { + st.execute("SELECT 1"); + } + return this.testConn; + } + + void releaseConnectionResources() throws SQLException { + if (this.testConn != null) { + this.testConn.close(); + this.testConn = null; + } + } + } + + TestHandler testHandler = new TestHandler(); + Statement testStatement = null; + + try { + if (versionMeetsMinimum(5, 6, 3)) { + createTable("testWL4897", "(f1 INT NOT NULL PRIMARY KEY, f2 CHAR(50))"); + + // when executed in the following sequence, each one of these queries take approximately 1 sec. + final String[] slowQueries = { "INSERT INTO testWL4897 VALUES (SLEEP(0.5) + 1, 'MySQL'), (SLEEP(0.5) + 2, 'Connector/J')", + "SELECT * FROM testWL4897 WHERE f1 + SLEEP(0.5) = f1", + "REPLACE INTO testWL4897 VALUES (SLEEP(0.33) + 2, 'Database'), (SLEEP(0.33) + 3, 'Connector'), (SLEEP(0.33) + 4, 'Java')", + "UPDATE testWL4897 SET f1 = f1 * 10 + SLEEP(0.25)", "DELETE FROM testWL4897 WHERE f1 + SLEEP(0.25) = f1" }; + + for (String query : slowQueries) { + testStatement = testHandler.getNewConnectionForSlowQueries().createStatement(); + testStatement.execute(query); + assertTrue("A slow query explain results warning should have been issued for: '" + query + "'.", testHandler.containsSlowQueryMsg(query)); + testStatement.close(); + } + } else { + // only SELECT is qualified to log slow query explain results warning + final String query = "SELECT SLEEP(1)"; + + testStatement = testHandler.getNewConnectionForSlowQueries().createStatement(); + testStatement.execute(query); + assertTrue("A slow query explain results warning should have been issued for: '" + query + "'.", testHandler.containsSlowQueryMsg(query)); + testStatement.close(); + } + + } finally { + testHandler.releaseConnectionResources(); + testHandler.undoSystemErrDiversion(); + } + } + + /** + * Tests fix for BUG#68562 - Combination rewriteBatchedStatements and useAffectedRows not working as expected + * + * @throws Exception + * if the test fails. + */ + public void testBug68562() throws Exception { + testBug68562BatchWithSize(1); + testBug68562BatchWithSize(3); + } + + private void testBug68562BatchWithSize(int batchSize) throws Exception { + + // 5.1 server returns wrong values for found_rows because Bug#46675 was fixed only for 5.5+ + if (!versionMeetsMinimum(5, 5)) { + return; + } + + createTable("testBug68562_found", "(id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255) NOT NULL, version VARCHAR(255)) ENGINE=InnoDB;"); + createTable("testBug68562_affected", "(id INTEGER NOT NULL PRIMARY KEY, name VARCHAR(255) NOT NULL, version VARCHAR(255)) ENGINE=InnoDB;"); + + // insert the records (no update) + int[] foundRows = testBug68562ExecuteBatch(batchSize, false, false, false); + for (int foundRow : foundRows) { + assertEquals(1, foundRow); + } + int[] affectedRows = testBug68562ExecuteBatch(batchSize, true, false, false); + for (int affectedRow : affectedRows) { + assertEquals(1, affectedRow); + } + + // update the inserted records with same values + foundRows = testBug68562ExecuteBatch(batchSize, false, false, false); + for (int foundRow : foundRows) { + assertEquals(1, foundRow); + } + affectedRows = testBug68562ExecuteBatch(batchSize, true, false, false); + for (int affectedRow : affectedRows) { + assertEquals(0, affectedRow); + } + + // update the inserted records with same values REWRITING THE BATCHED STATEMENTS + foundRows = testBug68562ExecuteBatch(batchSize, false, true, false); + for (int foundRow : foundRows) { + assertEquals(batchSize > 1 ? Statement.SUCCESS_NO_INFO : batchSize, foundRow); + } + affectedRows = testBug68562ExecuteBatch(batchSize, true, true, false); + for (int affectedRow : affectedRows) { + assertEquals(0, affectedRow); + } + + // update the inserted records with NEW values REWRITING THE BATCHED STATEMENTS + foundRows = testBug68562ExecuteBatch(batchSize, false, true, true); + for (int foundRow : foundRows) { + assertEquals(batchSize > 1 ? Statement.SUCCESS_NO_INFO : 2 * batchSize, foundRow); + } + affectedRows = testBug68562ExecuteBatch(batchSize, true, true, true); + for (int affectedRow : affectedRows) { + assertEquals(batchSize > 1 ? Statement.SUCCESS_NO_INFO : 2 * batchSize, affectedRow); + } + } + + private int[] testBug68562ExecuteBatch(int batchSize, boolean useAffectedRows, boolean rewriteBatchedStatements, boolean realUpdate) + throws ClassNotFoundException, SQLException { + + String tableName = "testBug68562"; + + Properties properties = new Properties(); + if (useAffectedRows) { + properties.put("useAffectedRows", "true"); + tableName += "_affected"; + } else { + tableName += "_found"; + } + if (rewriteBatchedStatements) { + properties.put("rewriteBatchedStatements", "true"); + } + Connection connection = getConnectionWithProps(properties); + + PreparedStatement statement = connection + .prepareStatement("INSERT INTO " + tableName + "(id, name, version) VALUES(?,?,?) ON DUPLICATE KEY UPDATE version = " + + (realUpdate ? "CONCAT(VALUES(version),'updated'), name = CONCAT(VALUES(name),'updated')" : "VALUES(version), name = VALUES(name)")); + for (int i = 0; i < batchSize; i++) { + statement.setInt(1, i); + statement.setString(2, "name" + i); + statement.setString(3, "version" + i); + statement.addBatch(); + } + + int[] affectedRows = statement.executeBatch(); + + statement.close(); + connection.close(); + + return affectedRows; + + } + + /** + * Tests fix for BUG#55340 - initializeResultsMetadataFromCache fails on second call to stored proc + * + * @throws Exception + * if the test fails. + */ + public void testBug55340() throws Exception { + Connection testConnCacheRSMD = getConnectionWithProps("cacheResultSetMetadata=true"); + ResultSetMetaData rsmd; + + createTable("testBug55340", "(col1 INT, col2 CHAR(10))"); + createProcedure("testBug55340", "() BEGIN SELECT * FROM testBug55340; END"); + + assertEquals(this.stmt.executeUpdate("INSERT INTO testBug55340 (col1, col2) VALUES (1, 'one'), (2, 'two'), (3, 'three')"), 3); + + for (Connection testConn : new Connection[] { this.conn, testConnCacheRSMD }) { + String testDesc = testConn == testConnCacheRSMD ? "Conn. with 'cacheResultSetMetadata=true'" : "Default connection"; + + // bug occurs in 2nd call only + for (int i = 1; i <= 2; i++) { + for (PreparedStatement testStmt : new PreparedStatement[] { testConn.prepareStatement("SELECT * FROM testBug55340"), + testConn.prepareCall("CALL testBug55340()") }) { + + assertTrue(testStmt.execute()); + this.rs = testStmt.getResultSet(); + assertResultSetLength(this.rs, 3); + + rsmd = this.rs.getMetaData(); + assertEquals("(" + i + ") " + testDesc + " - " + testStmt.getClass().getSimpleName() + ":RSMetaData - wrong column count.", 2, + rsmd.getColumnCount()); + assertEquals("(" + i + ") " + testDesc + " - " + testStmt.getClass().getSimpleName() + ":RSMetaData - wrong column(1) type.", + Integer.class.getName(), rsmd.getColumnClassName(1)); + assertEquals("(" + i + ") " + testDesc + " - " + testStmt.getClass().getSimpleName() + ":RSMetaData - wrong column(2) type.", + String.class.getName(), rsmd.getColumnClassName(2)); + + testStmt.close(); + } + } + } + + testConnCacheRSMD.close(); + } + + /** + * Tests fix for BUG#71396 - setMaxRows (SQL_SELECT_LIMIT) from one query used in later queries (sometimes) + * + * @throws Exception + * if the test fails. + */ + public void testBug71396() throws Exception { + final String queryLimitClause = "SELECT * FROM testBug71396 LIMIT 2"; + final String queryLimitClauseInJoin = "SELECT * FROM testBug71396 A JOIN (SELECT * FROM testBug71396 LIMIT 2) B ON A.c != B.c"; + final String queryLimitInQuotes = "SELECT * FROM testBug71396 WHERE c != 'Unlimited'"; + final String queryLimitInComment = "SELECT * FROM testBug71396 -- Unlimited"; + final String queryNoLimit = "SELECT * FROM testBug71396"; + + final String[] queries = new String[] { queryLimitClause, queryLimitClauseInJoin, queryLimitInQuotes, queryLimitInComment, queryNoLimit }; + + Connection testConn; + Statement testStmt; + ResultSet testRS; + PreparedStatement testPStmtSet[]; + + createTable("testBug71396", "(c VARCHAR(5))"); + this.stmt.execute("INSERT INTO testBug71396 VALUES ('One'), ('Two'), ('Three')"); + + /* + * Case 1: Statement.executeQuery() and Statement.execute() with plain Connection. + */ + testConn = getConnectionWithProps(""); + + // safety check + testBug71396StatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // initialize Statement with a given maxRow value, keep open until end of the case + testStmt = testBug71396StatementInit(testConn, 1); + + // check results count using the same Statement[maxRows = 1] for all queries + testBug71396StatementMultiCheck(testStmt, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new Statement[default maxRows] per query + testBug71396StatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // recheck results count reusing the first Statement[maxRows = 1] for all queries - confirm maxRows wasn't lost + testBug71396StatementMultiCheck(testStmt, queries, new int[] { 1, 1, 1, 1, 1 }); + + testStmt.close(); + testConn.close(); + + /* + * Case 2: PreparedStatement.executeQuery() and PreparedStatement.execute() with plain Connection. + */ + testConn = getConnectionWithProps(""); + + // safety check + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // initialize Statement with a given maxRow value, keep open until end of the case + testStmt = testBug71396StatementInit(testConn, 1); + + // initialize a set of PreparedStatements with a given maxRow value, keep open until end of the case + testPStmtSet = testBug71396PrepStatementInit(testConn, queries, 1); + + // check results count using same Connection and one PreparedStatement[maxRows = 1] per query + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new PreparedStatement[default maxRows] per query + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // check results count reusing the first PreparedStatement[maxRows = 1] per query - confirm maxRows wasn't lost + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + testBug71396PrepStatementClose(testPStmtSet); + testStmt.close(); + testConn.close(); + + /* + * Case 3: PreparedStatement.executeQuery() and PreparedStatement.execute() with + * Connection[useServerPrepStmts=true]. + */ + testConn = getConnectionWithProps("useServerPrepStmts=true"); + + // safety check + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // initialize Statement with a given maxRow value, keep open until end of the case. + testStmt = testBug71396StatementInit(testConn, 1); + + // initialize a set of PreparedStatements with a given maxRow value, keep open until end of the case + testPStmtSet = testBug71396PrepStatementInit(testConn, queries, 1); + + // check results count using same Connection and one PreparedStatement[maxRows = 1] per query + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new PreparedStatement[default maxRows] per query + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 4, 3, 3, 3 }); + + // check results count reusing the first PreparedStatement[maxRows = 1] per query - confirm maxRows wasn't lost + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + testBug71396PrepStatementClose(testPStmtSet); + testStmt.close(); + testConn.close(); + + /* + * Case 4: Statement.executeQuery() and Statement.execute() with Connection[maxRows=2]. + */ + testConn = getConnectionWithProps("maxRows=2"); + + // safety check + testBug71396StatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // initialize Statement with a given maxRow value, keep open until end of the case + testStmt = testBug71396StatementInit(testConn, 1); + + // check results count using the same Statement[maxRows = 1] for all queries + testBug71396StatementMultiCheck(testStmt, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new Statement[default maxRows] per query + testBug71396StatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // recheck results count reusing the first Statement[maxRows = 1] for all queries - confirm maxRows wasn't lost + testBug71396StatementMultiCheck(testStmt, queries, new int[] { 1, 1, 1, 1, 1 }); + + testStmt.close(); + testConn.close(); + + /* + * Case 5: PreparedStatement.executeQuery() and PreparedStatement.execute() with Connection[maxRows=2]. + */ + testConn = getConnectionWithProps("maxRows=2"); + + // safety check + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // initialize Statement with a given maxRow value, keep open until end of the case + testStmt = testBug71396StatementInit(testConn, 1); + + // initialize a set of PreparedStatements with a given maxRow value, keep open until end of the case + testPStmtSet = testBug71396PrepStatementInit(testConn, queries, 1); + + // check results count using same Connection and one PreparedStatement[maxRows = 1] per query + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new PreparedStatement[default maxRows] per query + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // check results count reusing the first PreparedStatement[maxRows = 1] per query - confirm maxRows wasn't lost + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + testBug71396PrepStatementClose(testPStmtSet); + testStmt.close(); + testConn.close(); + + /* + * Case 6: PreparedStatement.executeQuery() and PreparedStatement.execute() with + * Connection[useServerPrepStmts=true;maxRows=2]. + */ + testConn = getConnectionWithProps("maxRows=2,useServerPrepStmts=true"); + + // safety check + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // initialize Statement with a given maxRow value, keep open until end of the case + testStmt = testBug71396StatementInit(testConn, 1); + + // initialize a set of PreparedStatements with a given maxRow value, keep open until end of the case + testPStmtSet = testBug71396PrepStatementInit(testConn, queries, 1); + + // check results count using same Connection and one PreparedStatement[maxRows = 1] per query + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + // check results count using same Connection and one new PreparedStatement[default maxRows] per query + testBug71396PrepStatementMultiCheck(testConn, queries, new int[] { 2, 2, 2, 2, 2 }); + + // check results count reusing the first PreparedStatement[maxRows = 1] per query - confirm maxRows wasn't lost + testBug71396PrepStatementMultiCheck(testPStmtSet, queries, new int[] { 1, 1, 1, 1, 1 }); + + testBug71396PrepStatementClose(testPStmtSet); + testStmt.close(); + testConn.close(); + + /* + * Case 7: Multiple combinations between maxRows connection prop, Statement.setMaxRows() and LIMIT clause. + * Covers some cases not tested previously. + */ + testBug71396MultiSettingsCheck("", -1, 1, 1); + testBug71396MultiSettingsCheck("", -1, 2, 2); + testBug71396MultiSettingsCheck("", 1, 1, 1); + testBug71396MultiSettingsCheck("", 1, 2, 1); + testBug71396MultiSettingsCheck("", 2, 1, 1); + testBug71396MultiSettingsCheck("", 2, 2, 2); + + testBug71396MultiSettingsCheck("maxRows=1", -1, 1, 1); + testBug71396MultiSettingsCheck("maxRows=1", -1, 2, 1); + testBug71396MultiSettingsCheck("maxRows=1", 1, 1, 1); + testBug71396MultiSettingsCheck("maxRows=1", 1, 2, 1); + testBug71396MultiSettingsCheck("maxRows=1", 2, 1, 1); + testBug71396MultiSettingsCheck("maxRows=1", 2, 2, 2); + + testBug71396MultiSettingsCheck("maxRows=2", -1, 1, 1); + testBug71396MultiSettingsCheck("maxRows=2", -1, 2, 2); + testBug71396MultiSettingsCheck("maxRows=2", 1, 1, 1); + testBug71396MultiSettingsCheck("maxRows=2", 1, 2, 1); + testBug71396MultiSettingsCheck("maxRows=2", 2, 1, 1); + testBug71396MultiSettingsCheck("maxRows=2", 2, 2, 2); + + // Case 8: New session bue to user change + createUser("'testBug71396User'@'%'", "IDENTIFIED BY 'testBug71396User'"); + this.stmt.execute("GRANT SELECT ON *.* TO 'testBug71396User'@'%'"); + + testConn = getConnectionWithProps(""); + testStmt = testBug71396StatementInit(testConn, 5); + + ((MySQLConnection) testConn).changeUser("testBug71396User", "testBug71396User"); + + Statement testStmtTmp = testConn.createStatement(); + testRS = testStmtTmp.executeQuery("SELECT CURRENT_USER(), @@SESSION.SQL_SELECT_LIMIT"); + assertTrue(testRS.next()); + assertEquals("testBug71396User@%", testRS.getString(1)); + assertTrue(String.format("expected:higher than<%d> but was:<%s>", Integer.MAX_VALUE, testRS.getBigDecimal(2)), + testRS.getBigDecimal(2).compareTo(new BigDecimal(Integer.MAX_VALUE)) == 1); + testRS.close(); + testStmtTmp.close(); + + testRS = testStmt.executeQuery("SELECT CURRENT_USER(), @@SESSION.SQL_SELECT_LIMIT"); + assertTrue(testRS.next()); + assertEquals("testBug71396User@%", testRS.getString(1)); + assertEquals(new BigDecimal(5), testRS.getBigDecimal(2)); + testRS.close(); + + testStmt.close(); + testConn.close(); + + // Case 9: New session due to reconnection + testConn = getConnectionWithProps(""); + testStmt = testBug71396StatementInit(testConn, 5); + + ((MySQLConnection) testConn).createNewIO(true); // true or false argument is irrelevant for this test case + + testStmtTmp = testConn.createStatement(); + testRS = testStmtTmp.executeQuery("SELECT @@SESSION.SQL_SELECT_LIMIT"); + assertTrue(testRS.next()); + assertTrue(String.format("expected:higher than<%d> but was:<%s>", Integer.MAX_VALUE, testRS.getBigDecimal(1)), + testRS.getBigDecimal(1).compareTo(new BigDecimal(Integer.MAX_VALUE)) == 1); + testRS.close(); + testStmtTmp.close(); + + testRS = testStmt.executeQuery("SELECT @@SESSION.SQL_SELECT_LIMIT"); + assertTrue(testRS.next()); + assertEquals(new BigDecimal(5), testRS.getBigDecimal(1)); + testRS.close(); + + testStmt.close(); + testConn.close(); + } + + /** + * Initializes and returns a Statement with maxRows defined. Tests the SQL_SELECT_LIMIT defined. Executing this + * query also forces this limit to be defined at session level. + */ + private Statement testBug71396StatementInit(Connection testConn, int maxRows) throws SQLException { + ResultSet testRS; + Statement testStmt = testConn.createStatement(); + + testStmt.setMaxRows(maxRows); + // while consulting SQL_SELECT_LIMIT setting also forces limit to be applied into current session + testRS = testStmt.executeQuery("SELECT @@SESSION.SQL_SELECT_LIMIT"); + testRS.next(); + assertEquals("Wrong @@SESSION.SQL_SELECT_LIMIT", maxRows, testRS.getInt(1)); + + return testStmt; + } + + /** + * Executes a set of queries using a Statement (newly created) and tests if the results count is the expected. + */ + private void testBug71396StatementMultiCheck(Connection testConn, String[] queries, int[] expRowCount) throws SQLException { + if (queries.length != expRowCount.length) { + fail("Bad arguments!"); + } + Statement testStmt = testConn.createStatement(); + testBug71396StatementMultiCheck(testStmt, queries, expRowCount); + testStmt.close(); + } + + /** + * Executes a set of queries using a Statement and tests if the results count is the expected. + */ + private void testBug71396StatementMultiCheck(Statement testStmt, String[] queries, int[] expRowCount) throws SQLException { + if (queries.length != expRowCount.length) { + fail("Bad arguments!"); + } + for (int i = 0; i < queries.length; i++) { + testBug71396StatementCheck(testStmt, queries[i], expRowCount[i]); + } + } + + /** + * Executes one query using a Statement and tests if the results count is the expected. + */ + private void testBug71396StatementCheck(Statement testStmt, String query, int expRowCount) throws SQLException { + ResultSet testRS; + + testRS = testStmt.executeQuery(query); + assertTrue(testRS.last()); + assertEquals(String.format("Wrong number of rows for query '%s'", query), expRowCount, testRS.getRow()); + testRS.close(); + + testStmt.execute(query); + testRS = testStmt.getResultSet(); + assertTrue(testRS.last()); + assertEquals(String.format("Wrong number of rows for query '%s'", query), expRowCount, testRS.getRow()); + testRS.close(); + } + + /** + * Initializes and returns an array of PreparedStatements, with maxRows defined, for a set of queries. + */ + private PreparedStatement[] testBug71396PrepStatementInit(Connection testConn, String[] queries, int maxRows) throws SQLException { + PreparedStatement[] testPStmt = new PreparedStatement[queries.length]; + + for (int i = 0; i < queries.length; i++) { + testPStmt[i] = testConn.prepareStatement(queries[i]); + if (maxRows > 0) { + testPStmt[i].setMaxRows(maxRows); + } + } + return testPStmt; + } + + /** + * Closes all PreparedStatements in the array. + */ + private void testBug71396PrepStatementClose(PreparedStatement[] testPStmt) throws SQLException { + for (Statement testStmt : testPStmt) { + testStmt.close(); + } + } + + /** + * Executes a set of queries using newly created PreparedStatements and tests if the results count is the expected. + */ + private void testBug71396PrepStatementMultiCheck(Connection testConn, String[] queries, int[] expRowCount) throws SQLException { + if (queries.length != expRowCount.length) { + fail("Bad arguments!"); + } + for (int i = 0; i < queries.length; i++) { + testBug71396PrepStatementCheck(testConn, queries[i], expRowCount[i], -1); + } + } + + /** + * Executes a set of queries using the given PreparedStatements and tests if the results count is the expected. + */ + private void testBug71396PrepStatementMultiCheck(PreparedStatement[] testPStmt, String[] queries, int[] expRowCount) throws SQLException { + if (testPStmt.length != queries.length || testPStmt.length != expRowCount.length) { + fail("Bad arguments!"); + } + for (int i = 0; i < queries.length; i++) { + testBug71396PrepStatementCheck(testPStmt[i], queries[i], expRowCount[i]); + } + } + + /** + * Executes one query using a newly created PreparedStatement, setting its maxRows limit, and tests if the results + * count is the expected. + */ + private void testBug71396PrepStatementCheck(Connection testConn, String query, int expRowCount, int maxRows) throws SQLException { + PreparedStatement chkPStmt; + + chkPStmt = testConn.prepareStatement(query); + if (maxRows > 0) { + chkPStmt.setMaxRows(maxRows); + } + testBug71396PrepStatementCheck(chkPStmt, query, expRowCount); + chkPStmt.close(); + } + + /** + * Executes one query using a PreparedStatement and tests if the results count is the expected. + */ + private void testBug71396PrepStatementCheck(PreparedStatement testPStmt, String query, int expRowCount) throws SQLException { + ResultSet testRS; + + testRS = testPStmt.executeQuery(); + assertTrue(testRS.last()); + assertEquals(String.format("Wrong number of rows for query '%s'", query), expRowCount, testRS.getRow()); + testRS.close(); + + testPStmt.execute(); + testRS = testPStmt.getResultSet(); + assertTrue(testRS.last()); + assertEquals(String.format("Wrong number of rows for query '%s'", query), expRowCount, testRS.getRow()); + testRS.close(); + } + + /** + * Executes a query containing the clause LIMIT with a Statement and a PreparedStatement, using a combination of + * Connection properties, maxRows value and limit clause value, and tests if the results count is the expected. + */ + private void testBug71396MultiSettingsCheck(String connProps, int maxRows, int limitClause, int expRowCount) throws SQLException { + Connection testConn = getConnectionWithProps(connProps); + + Statement testStmt = testConn.createStatement(); + if (maxRows > 0) { + testStmt.setMaxRows(maxRows); + } + testStmt.execute("SELECT 1"); // force limit to be applied into current session + + testBug71396StatementCheck(testStmt, String.format("SELECT * FROM testBug71396 LIMIT %d", limitClause), expRowCount); + testBug71396PrepStatementCheck(testConn, String.format("SELECT * FROM testBug71396 LIMIT %d", limitClause), expRowCount, maxRows); + + testStmt.close(); + testConn.close(); + } + + /** + * Tests fix for 18091639 - STRINGINDEXOUTOFBOUNDSEXCEPTION IN PREPAREDSTATEMENT.SETTIMESTAMP WITH 5.6.15 + * + * @throws Exception + * if the test fails. + */ + public void testBug18091639() throws SQLException { + String str = TimeUtil.formatNanos(1, true, false); + assertEquals("000000001", str); + + str = TimeUtil.formatNanos(1, true, true); + assertEquals("0", str); + + str = TimeUtil.formatNanos(1999, true, false); + assertEquals("000001999", str); + + str = TimeUtil.formatNanos(1999, true, true); + assertEquals("000001", str); + + str = TimeUtil.formatNanos(1000000010, true, false); + assertEquals("00000001", str); + + str = TimeUtil.formatNanos(1000000010, true, true); + assertEquals("0", str); + } + + /** + * Tests fix for Bug#66947 (16004987) - Calling ServerPreparedStatement.close() twiche corrupts cached statements + * + * @throws Exception + */ + public void testBug66947() throws Exception { + + Connection con = null; + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("cachePrepStmts", "true"); + props.setProperty("prepStmtCacheSize", "2"); + + con = getConnectionWithProps(props); + + PreparedStatement ps1_1; + PreparedStatement ps1_2; + + String query = "Select 'a' from dual"; + + ps1_1 = con.prepareStatement(query); + ps1_1.execute(); + ps1_1.close(); + + ps1_2 = con.prepareStatement(query); + assertSame("SSPS should be taken from cache but is not the same.", ps1_1, ps1_2); + ps1_2.execute(); + ps1_2.close(); + ps1_2.close(); + + ps1_1 = con.prepareStatement(query); + assertNotSame("SSPS should not be taken from cache but is the same.", ps1_2, ps1_1); + ps1_1.execute(); + ps1_1.close(); + ps1_1.close(); + + // check that removeEldestEntry doesn't remove elements twice + PreparedStatement ps2_1; + PreparedStatement ps2_2; + PreparedStatement ps3_1; + PreparedStatement ps3_2; + + ps1_1 = con.prepareStatement("Select 'b' from dual"); + ps1_1.execute(); + ps1_1.close(); + ps2_1 = con.prepareStatement("Select 'c' from dual"); + ps2_1.execute(); + ps2_1.close(); + ps3_1 = con.prepareStatement("Select 'd' from dual"); + ps3_1.execute(); + ps3_1.close(); + + ps1_2 = con.prepareStatement("Select 'b' from dual"); + assertNotSame("SSPS should not be taken from cache but is the same.", ps1_1, ps1_2); + + ps2_2 = con.prepareStatement("Select 'c' from dual"); + assertSame("SSPS should be taken from cache but is not the same.", ps2_1, ps2_2); + + ps3_2 = con.prepareStatement("Select 'd' from dual"); + assertSame("SSPS should be taken from cache but is not the same.", ps3_1, ps3_2); + + } finally { + if (con != null) { + con.close(); + } + } + + } + + /** + * Tests fix for Bug#71672 - Every SQL statement is checked if it contains "ON DUPLICATE KEY UPDATE" or not + * + * @throws Exception + * if the test fails. + */ + public void testBug71672() throws SQLException { + boolean lastTest = false; + int testStep = 0; + + Connection testConn = null; + Statement testStmt = null; + PreparedStatement testPStmt = null; + ResultSet testRS = null; + int[] res = null; + + int[] expectedUpdCount = null; + int[][] expectedGenKeys = null; + int[][] expectedGenKeysBatchStmt = null; + int[] expectedGenKeysMultiQueries = null; + int[] expectedUpdCountBatchPStmt = null; + int[] expectedGenKeysBatchPStmt = null; + + final String tableDDL = "(id INT AUTO_INCREMENT PRIMARY KEY, ch CHAR(1) UNIQUE KEY, ct INT)"; + + // WARNING: MySQL 5.1 behaves differently in the way the affected rows and generated keys (from AUTO_INCREMENT + // columns) are computed. Specific expected date set must be accounted. + + // *** CONTROL DATA SET 1: queries for both Statement and PreparedStatement + final String[] queries = new String[] { "INSERT INTO testBug71672 (ch, ct) VALUES ('A', 100), ('C', 100), ('D', 100)", + "INSERT INTO testBug71672 (ch, ct) VALUES ('B', 2), ('C', 3), ('D', 4), ('E', 5) ON DUPLICATE KEY UPDATE ct = -1 * (ABS(ct) + VALUES(ct))", + "INSERT INTO testBug71672 (ch, ct) VALUES ('F', 100) ON DUPLICATE KEY UPDATE ct = -1 * (ABS(ct) + VALUES(ct))", + "INSERT INTO testBug71672 (ch, ct) VALUES ('B', 2), ('F', 6) ON DUPLICATE KEY UPDATE ct = -1 * (ABS(ct) + VALUES(ct))", + "INSERT INTO testBug71672 (ch, ct) VALUES ('G', 100)" }; // rewriteBatchedStatements needs > 4 queries + + // expected update counts per query: + final int[] expectedUpdCountDef = new int[] { 3, 6, 1, 4, 1 }; + // expected generated keys per query: + final int[][] expectedGenKeysForChkODKU = new int[][] { { 1, 2, 3 }, { 4 }, { 8 }, { 8 }, { 11 } }; + final int[][] expectedGenKeysForNoChkODKU = new int[][] { { 1, 2, 3 }, { 4, 5, 6, 7, 8, 9 }, { 8 }, { 8, 9, 10, 11 }, { 11 } }; + final int[][] expectedGenKeysForBatchStmtRW = new int[][] { { 1 }, { 4 }, { 8 }, { 8 }, { 11 } }; + + // expected update counts per query (MySQL 5.1): + final int[] expectedUpdCountDef51 = new int[] { 3, 8, 1, 6, 1 }; + // expected generated keys per query (MySQL 5.1): + final int[][] expectedGenKeysForChkODKU51 = new int[][] { { 1, 2, 3 }, { 4 }, { 6 }, { 6 }, { 7 } }; + final int[][] expectedGenKeysForNoChkODKU51 = new int[][] { { 1, 2, 3 }, { 4, 5, 6, 7, 8, 9, 10, 11 }, { 6 }, { 6, 7, 8, 9, 10, 11 }, { 7 } }; + final int[][] expectedGenKeysForBatchStmtRW51 = new int[][] { { 1 }, { 4 }, { 6 }, { 6 }, { 7 } }; + + // *** CONTROL DATA SET 2: query and params for batch PrepatedStatement + final String queryBatchPStmt = "INSERT INTO testBug71672 (ch, ct) VALUES (?, ?) ON DUPLICATE KEY UPDATE ct = -1 * (ABS(ct) + VALUES(ct))"; + final String[] paramsBatchPStmt = new String[] { "A100", "C100", "D100", "B2", "C3", "D4", "E5", "F100", "B2", "F6", "G100" }; + + // expected update counts per param: + final int[] expectedUpdCountBatchPStmtNoRW = new int[] { 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1 }; + final int sni = Statement.SUCCESS_NO_INFO; + final int[] expectedUpdCountBatchPStmtRW = new int[] { sni, sni, sni, sni, sni, sni, sni, sni, sni, sni, sni }; + // expected generated keys: + final int[] expectedGenKeysForBatchPStmtChkODKU = new int[] { 1, 2, 3, 4, 2, 3, 7, 8, 4, 8, 11 }; + final int[] expectedGenKeysForBatchPStmtNoChkODKU = new int[] { 1, 2, 3, 4, 2, 3, 3, 4, 7, 8, 4, 5, 8, 9, 11 }; + final int[] expectedGenKeysForBatchPStmtRW = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + // expected update counts per param: + final int[] expectedUpdCountBatchPStmtNoRW51 = new int[] { 1, 1, 1, 1, 3, 3, 1, 1, 3, 3, 1 }; + // expected generated keys: + final int[] expectedGenKeysForBatchPStmtChkODKU51 = new int[] { 1, 2, 3, 4, 2, 3, 5, 6, 4, 6, 7 }; + final int[] expectedGenKeysForBatchPStmtNoChkODKU51 = new int[] { 1, 2, 3, 4, 2, 3, 4, 3, 4, 5, 5, 6, 4, 5, 6, 6, 7, 8, 7 }; + final int[] expectedGenKeysForBatchPStmtRW51 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + // Test multiple connection props + do { + switch (++testStep) { + case 1: + testConn = getConnectionWithProps(""); + expectedUpdCount = versionMeetsMinimum(5, 5) ? expectedUpdCountDef : expectedUpdCountDef51; + expectedGenKeys = versionMeetsMinimum(5, 5) ? expectedGenKeysForChkODKU : expectedGenKeysForChkODKU51; + expectedGenKeysBatchStmt = expectedGenKeys; + expectedUpdCountBatchPStmt = versionMeetsMinimum(5, 5) ? expectedUpdCountBatchPStmtNoRW : expectedUpdCountBatchPStmtNoRW51; + expectedGenKeysBatchPStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchPStmtChkODKU : expectedGenKeysForBatchPStmtChkODKU51; + break; + case 2: + testConn = getConnectionWithProps("dontCheckOnDuplicateKeyUpdateInSQL=true"); + expectedUpdCount = versionMeetsMinimum(5, 5) ? expectedUpdCountDef : expectedUpdCountDef51; + expectedGenKeys = versionMeetsMinimum(5, 5) ? expectedGenKeysForNoChkODKU : expectedGenKeysForNoChkODKU51; + expectedGenKeysBatchStmt = expectedGenKeys; + expectedUpdCountBatchPStmt = versionMeetsMinimum(5, 5) ? expectedUpdCountBatchPStmtNoRW : expectedUpdCountBatchPStmtNoRW51; + expectedGenKeysBatchPStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchPStmtNoChkODKU : expectedGenKeysForBatchPStmtNoChkODKU51; + break; + case 3: + testConn = getConnectionWithProps("rewriteBatchedStatements=true"); + expectedUpdCount = versionMeetsMinimum(5, 5) ? expectedUpdCountDef : expectedUpdCountDef51; + expectedGenKeys = versionMeetsMinimum(5, 5) ? expectedGenKeysForChkODKU : expectedGenKeysForChkODKU51; + expectedGenKeysBatchStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchStmtRW : expectedGenKeysForBatchStmtRW51; + expectedUpdCountBatchPStmt = expectedUpdCountBatchPStmtRW; + expectedGenKeysBatchPStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchPStmtRW : expectedGenKeysForBatchPStmtRW51; + break; + case 4: + // dontCheckOnDuplicateKeyUpdateInSQL=true is canceled by rewriteBatchedStatements=true + testConn = getConnectionWithProps("rewriteBatchedStatements=true,dontCheckOnDuplicateKeyUpdateInSQL=true"); + expectedUpdCount = versionMeetsMinimum(5, 5) ? expectedUpdCountDef : expectedUpdCountDef51; + expectedGenKeys = versionMeetsMinimum(5, 5) ? expectedGenKeysForChkODKU : expectedGenKeysForChkODKU51; + expectedGenKeysBatchStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchStmtRW : expectedGenKeysForBatchStmtRW51; + expectedUpdCountBatchPStmt = expectedUpdCountBatchPStmtRW; + expectedGenKeysBatchPStmt = versionMeetsMinimum(5, 5) ? expectedGenKeysForBatchPStmtRW : expectedGenKeysForBatchPStmtRW51; + lastTest = true; + break; + } + + // A. Test Statement.execute() results + createTable("testBug71672", tableDDL); + for (int i = 0; i < queries.length; i++) { + testBug71672Statement(testStep, testConn, queries[i], -1, expectedGenKeys[i]); + } + dropTable("testBug71672"); + + // B. Test Statement.executeUpdate() results + createTable("testBug71672", tableDDL); + for (int i = 0; i < queries.length; i++) { + testBug71672Statement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); + } + dropTable("testBug71672"); + + // C. Test Statement.executeBatch() results + createTable("testBug71672", tableDDL); + testStmt = testConn.createStatement(); + for (String query : queries) { + testStmt.addBatch(query); + } + res = testStmt.executeBatch(); + assertEquals(testStep + ". Satement.executeBatch() result", expectedUpdCount.length, res.length); + for (int i = 0; i < expectedUpdCount.length; i++) { + assertEquals(testStep + "." + i + ". Satement.executeBatch() result", expectedUpdCount[i], res[i]); + } + testRS = testStmt.getGeneratedKeys(); + for (int i = 0; i < expectedGenKeysBatchStmt.length; i++) { + for (int j = 0; j < expectedGenKeysBatchStmt[i].length; j++) { + assertTrue(testStep + ". Row expected in generated keys ResultSet", testRS.next()); + assertEquals(testStep + ".[" + i + "][" + j + "]. Wrong generated key", expectedGenKeysBatchStmt[i][j], testRS.getInt(1)); + } + } + assertFalse(testStep + ". No more rows expected in generated keys ResultSet", testRS.next()); + testRS.close(); + testStmt.close(); + dropTable("testBug71672"); + + // D. Test PreparedStatement.execute() results + createTable("testBug71672", tableDDL); + for (int i = 0; i < queries.length; i++) { + testBug71672PreparedStatement(testStep, testConn, queries[i], -1, expectedGenKeys[i]); + } + dropTable("testBug71672"); + + // E. Test PreparedStatement.executeUpdate() results + createTable("testBug71672", tableDDL); + for (int i = 0; i < queries.length; i++) { + testBug71672PreparedStatement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]); + } + dropTable("testBug71672"); + + // F. Test PreparedStatement.executeBatch() results + createTable("testBug71672", tableDDL); + testPStmt = testConn.prepareStatement(queryBatchPStmt, Statement.RETURN_GENERATED_KEYS); + for (String param : paramsBatchPStmt) { + testPStmt.setString(1, param.substring(0, 1)); + testPStmt.setInt(2, Integer.parseInt(param.substring(1))); + testPStmt.addBatch(); + } + res = testPStmt.executeBatch(); + assertEquals(testStep + ". PreparedSatement.executeBatch() result", expectedUpdCountBatchPStmt.length, res.length); + for (int i = 0; i < expectedUpdCountBatchPStmt.length; i++) { + assertEquals(testStep + "." + i + ". PreparedSatement.executeBatch() result", expectedUpdCountBatchPStmt[i], res[i]); + } + testRS = testPStmt.getGeneratedKeys(); + for (int i = 0; i < expectedGenKeysBatchPStmt.length; i++) { + assertTrue(testStep + ". Row expected in generated keys ResultSet", testRS.next()); + assertEquals(testStep + ".[" + i + "]. Wrong generated key", expectedGenKeysBatchPStmt[i], testRS.getInt(1)); + } + assertFalse(testStep + ". No more rows expected in generated keys ResultSet", testRS.next()); + testRS.close(); + testPStmt.close(); + dropTable("testBug71672"); + + testConn.close(); + } while (!lastTest); + + // Test connection prop allowMultiQueries=true + // (behaves as if only first query has been executed) + lastTest = false; + String allQueries = ""; + for (String q : queries) { + allQueries += q + ";"; + } + do { + switch (++testStep) { + case 5: + testConn = getConnectionWithProps("allowMultiQueries=true"); + expectedGenKeysMultiQueries = new int[] { 1 }; + break; + case 6: + testConn = getConnectionWithProps("allowMultiQueries=true,dontCheckOnDuplicateKeyUpdateInSQL=true"); + expectedGenKeysMultiQueries = new int[] { 1, 2, 3 }; + lastTest = true; + break; + } + + // A. Test Statement.execute() results + createTable("testBug71672", tableDDL); + testBug71672Statement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); + dropTable("testBug71672"); + + // B. Test Statement.executeUpdate() results + createTable("testBug71672", tableDDL); + testBug71672Statement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); + dropTable("testBug71672"); + + // C. Test PreparedStatement.execute() results + createTable("testBug71672", tableDDL); + testBug71672PreparedStatement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries); + dropTable("testBug71672"); + + // D. Test PreparedStatement.executeUpdate() results + createTable("testBug71672", tableDDL); + testBug71672PreparedStatement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries); + dropTable("testBug71672"); + + testConn.close(); + } while (!lastTest); + } + + /** + * Check the update count and returned keys for an INSERT query using a Statement object. If expectedUpdateCount < 0 then runs Statement.execute() otherwise + * Statement.executeUpdate(). + */ + public void testBug71672Statement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) throws SQLException { + Statement testStmt = testConn.createStatement(); + + if (expectedUpdateCount < 0) { + assertFalse(testStep + ". Stmt.execute() result", testStmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + } else { + assertEquals(testStep + ". Stmt.executeUpdate() result", expectedUpdateCount, testStmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + } + + ResultSet testRS = testStmt.getGeneratedKeys(); + for (int k : expectedKeys) { + assertTrue(testStep + ". Row expected in generated keys ResultSet", testRS.next()); + assertEquals(testStep + ". Wrong generated key", k, testRS.getInt(1)); + } + assertFalse(testStep + ". No more rows expected in generated keys ResultSet", testRS.next()); + testRS.close(); + testStmt.close(); + } + + /** + * Check the update count and returned keys for an INSERT query using a PreparedStatement object. If expectedUpdateCount < 0 then runs + * PreparedStatement.execute() otherwise PreparedStatement.executeUpdate(). + */ + public void testBug71672PreparedStatement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) + throws SQLException { + PreparedStatement testPStmt = testConn.prepareStatement(query); + + if (expectedUpdateCount < 0) { + assertFalse(testStep + ". PrepStmt.execute() result", testPStmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + } else { + assertEquals(testStep + ". PrepStmt.executeUpdate() result", expectedUpdateCount, testPStmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + } + + ResultSet testRS = testPStmt.getGeneratedKeys(); + for (int k : expectedKeys) { + assertTrue(testStep + ". Row expected in generated keys ResultSet", testRS.next()); + assertEquals(testStep + ". Wrong generated key", k, testRS.getInt(1)); + } + assertFalse(testStep + ". No more rows expected in generated keys ResultSet", testRS.next()); + testRS.close(); + testPStmt.close(); + } + + /** + * Tests fix for BUG#71923 - Incorrect generated keys if ON DUPLICATE KEY UPDATE not exact + * + * @throws Exception + * if the test fails. + */ + public void testBug71923() throws Exception { + final String tableDDL = "(id INT AUTO_INCREMENT PRIMARY KEY, ch CHAR(1) UNIQUE KEY, ct INT, dt VARCHAR(100))"; + final String defaultQuery = "Insert into testBug71923 (ch, ct) values ('A', 1), ('B', 2)"; + final String[] testQueriesPositiveMatches = new String[] { + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON DUPLICATE KEY UPDATE ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON DUPLICATE KEY UPDATE ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) /*! ON DUPLICATE */ KEY /*!UPDATE*/ ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON/* ON */DUPLICATE /* DUPLICATE */KEY/* KEY *//* KEY */ UPDATE /* UPDATE */ ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON -- new line\n DUPLICATE KEY UPDATE ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON DUPLICATE # new line\n KEY UPDATE ct = ABS(ct) + VALUES(ct)", + "INSERT INTO testBug71923 (ch, ct) VALUES ('B', 2), ('C', 3) ON/* comment */DUPLICATE# new line\nKEY-- new line\nUPDATE ct = ABS(ct) + VALUES(ct)" }; + final String[] testQueriesNegativeMatches = new String[] { + "INSERT INTO testBug71923 (ch, ct, dt) VALUES ('C', 3, NULL), ('D', 4, NULL) /* ON DUPLICATE KEY UPDATE */", + "INSERT INTO testBug71923 (ch, ct, dt) VALUES ('C', 3, NULL), ('D', 4, NULL) -- ON DUPLICATE KEY UPDATE", + "INSERT INTO testBug71923 (ch, ct, dt) VALUES ('C', 3, NULL), ('D', 4, NULL) # ON DUPLICATE KEY UPDATE", + "INSERT INTO testBug71923 (ch, ct, dt) VALUES ('C', 3, NULL), ('D', 4, 'ON DUPLICATE KEY UPDATE')" }; + + int c = 0; + for (String query : testQueriesPositiveMatches) { + c++; + + // A. test Statement.execute() + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertFalse(this.stmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.stmt.getGeneratedKeys(); + assertTrue(c + ".A Statement.execute() - generated keys row expected", this.rs.next()); + assertEquals(c + ".A Statement.execute() - wrong generated key value", 3, this.rs.getInt(1)); + assertFalse(c + ".A Statement.execute() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + + // B. test Statement.executeUpdate + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertEquals(versionMeetsMinimum(5, 5) ? 3 : 4, this.stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.stmt.getGeneratedKeys(); + assertTrue(c + ".B Statement.executeUpdate() - generated keys row expected", this.rs.next()); + assertEquals(c + ".B Statement.executeUpdate() - wrong generated key value", 3, this.rs.getInt(1)); + assertFalse(c + ".B Statement.executeUpdate() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + // prepare statement for next tet cases + this.pstmt = this.conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + + // C. test PreparedStatment.execute() + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertFalse(this.pstmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.pstmt.getGeneratedKeys(); + assertTrue(c + ".C PreparedStatment.execute() - generated keys row expected", this.rs.next()); + assertEquals(c + ".C PreparedStatment.execute() - wrong generated key value", 3, this.rs.getInt(1)); + assertFalse(c + ".C PreparedStatment.execute() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + + // D. test PreparedStatment.executeUpdate + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertEquals(versionMeetsMinimum(5, 5) ? 3 : 4, this.pstmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.pstmt.getGeneratedKeys(); + assertTrue(c + ".D PreparedStatment.executeUpdate() - generated keys row expected", this.rs.next()); + assertEquals(c + ".D PreparedStatment.executeUpdate() - wrong generated key value", 3, this.rs.getInt(1)); + assertFalse(c + ".D PreparedStatment.executeUpdate() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + } + + c = 0; + for (String query : testQueriesNegativeMatches) { + c++; + + // E. test Statement.execute() + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertFalse(this.stmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.stmt.getGeneratedKeys(); + assertTrue(c + ".E Statement.execute() - generated keys 1st row expected", this.rs.next()); + assertEquals(c + ".E Statement.execute() - wrong 1st generated key value", 3, this.rs.getInt(1)); + assertTrue(c + ".E Statement.execute() - generated keys 2nd row expected", this.rs.next()); + assertEquals(c + ".E Statement.execute() - wrong 2nd generated key value", 4, this.rs.getInt(1)); + assertFalse(c + ".E Statement.execute() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + + // F. test Statement.executeUpdate + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertEquals(2, this.stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.stmt.getGeneratedKeys(); + assertTrue(c + ".F Statement.execute() - generated keys 1st row expected", this.rs.next()); + assertEquals(c + ".F Statement.execute() - wrong 1st generated key value", 3, this.rs.getInt(1)); + assertTrue(c + ".F Statement.execute() - generated keys 2nd row expected", this.rs.next()); + assertEquals(c + ".F Statement.execute() - wrong 2nd generated key value", 4, this.rs.getInt(1)); + assertFalse(c + ".F Statement.execute() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + // prepare statement for next tet cases + this.pstmt = this.conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); + + // G. test PreparedStatment.execute() + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertFalse(this.pstmt.execute(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.pstmt.getGeneratedKeys(); + assertTrue(c + ".G PreparedStatment.execute() - generated keys 1st row expected", this.rs.next()); + assertEquals(c + ".G PreparedStatment.execute() - wrong 1st generated key value", 3, this.rs.getInt(1)); + assertTrue(c + ".G PreparedStatment.execute() - generated keys 2nd row expected", this.rs.next()); + assertEquals(c + ".G PreparedStatment.execute() - wrong 2nd generated key value", 4, this.rs.getInt(1)); + assertFalse(c + ".G PreparedStatment.execute() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + + // H. test PreparedStatment.executeUpdate + createTable("testBug71923", tableDDL); + assertEquals(2, this.stmt.executeUpdate(defaultQuery)); + + assertEquals(2, this.pstmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS)); + this.rs = this.pstmt.getGeneratedKeys(); + assertTrue(c + ".H PreparedStatment.executeUpdate() - generated keys 1st row expected", this.rs.next()); + assertEquals(c + ".H PreparedStatment.executeUpdate() - wrong 1st generated key value", 3, this.rs.getInt(1)); + assertTrue(c + ".H PreparedStatment.executeUpdate() - generated keys 2nd row expected", this.rs.next()); + assertEquals(c + ".H PreparedStatment.executeUpdate() - wrong 2nd generated key value", 4, this.rs.getInt(1)); + assertFalse(c + ".H PreparedStatment.executeUpdate() - no more generated keys rows expected", this.rs.next()); + this.rs.close(); + + dropTable("testBug71923"); + } + } + + /** + * Tests fix for BUG#73163 - IndexOutOfBoundsException thrown preparing statement. + * + * This bug occurs only if running with Java6+. Duplicated in testsuite.regression.StatementRegressionTest.jdbc4.testBug73163(). + * + * @throws Exception + * if the test fails. + */ + public void testBug73163() throws Exception { + try { + this.stmt = this.conn.prepareStatement("LOAD DATA INFILE ? INTO TABLE testBug73163"); + } catch (SQLException e) { + if (e.getCause() instanceof IndexOutOfBoundsException && Util.isJdbc4()) { + fail("IOOBE thrown in Java6+ while preparing a LOAD DATA statement with placeholders."); + } else { + throw e; + } + } + } + + /** + * Tests fix for BUG#74998 - readRemainingMultiPackets not computed correctly for rows larger than 16 MB. + * + * This bug is observed only when a multipacket uses packets 127 and 128. It happens due to the transition from positive to negative values in a signed byte + * numeric value (127 + 1 == -128). + * + * The test case forces a multipacket to use packets 127, 128 and 129, where packet 129 is 0-length, this being another boundary case. + * Query (*1) generates the following MySQL protocol packets from the server: + * - Packets 1 to 4 contain protocol control data and results metadata info. (*2) + * - Packets 5 to 126 contain each row "X". (*3) + * - Packets 127 to 129 contain row "Y..." as a multipacket (size("Y...") = 32*1024*1024-15 requires 3 packets). (*4) + * - Packet 130 contains row "Z". (*5) + * + * @throws Exception + * if the test fails. + */ + public void testBug74998() throws Exception { + int maxAllowedPacketAtServer = Integer.parseInt(((MySQLConnection) this.conn).getServerVariable("max_allowed_packet")); + int maxAllowedPacketMinimumForTest = 32 * 1024 * 1024; + if (maxAllowedPacketAtServer < maxAllowedPacketMinimumForTest) { + fail("You need to increase max_allowed_packet to at least " + maxAllowedPacketMinimumForTest + " before running this test!"); + } + + createTable("testBug74998", "(id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, data LONGBLOB)"); // (*2) + + StringBuilder query = new StringBuilder("INSERT INTO testBug74998 (data) VALUES ('X')"); + for (int i = 0; i < 121; i++) { + query.append(",('X')"); + } + assertEquals(122, this.stmt.executeUpdate(query.toString())); // (*3) + + int lengthOfRowForMultiPacket = maxAllowedPacketMinimumForTest - 15; // 32MB - 15Bytes causes an empty packet at the end of the multipacket sequence + + this.stmt.executeUpdate("INSERT INTO testBug74998 (data) VALUES (REPEAT('Y', " + lengthOfRowForMultiPacket + "))"); // (*4) + this.stmt.executeUpdate("INSERT INTO testBug74998 (data) VALUES ('Z')"); // (*5) + + try { + this.rs = this.stmt.executeQuery("SELECT id, data FROM testBug74998 ORDER BY id"); // (*1) + } catch (CommunicationsException e) { + if (e.getCause() instanceof IOException && "Packets received out of order".compareTo(e.getCause().getMessage()) == 0) { + fail("Failed to correctly fetch all data from communications layer due to wrong processing of muli-packet number."); + } else { + throw e; + } + } + + // safety check + for (int i = 1; i <= 122; i++) { + assertTrue(this.rs.next()); + assertEquals(i, this.rs.getInt(1)); + assertEquals("X", this.rs.getString(2)); + } + assertTrue(this.rs.next()); + assertEquals(123, this.rs.getInt(1)); + assertEquals("YYYYY", this.rs.getString(2).substring(0, 5)); + assertEquals("YYYYY", this.rs.getString(2).substring(lengthOfRowForMultiPacket - 5)); + assertTrue(this.rs.next()); + assertEquals(124, this.rs.getInt(1)); + assertEquals("Z", this.rs.getString(2)); + assertFalse(this.rs.next()); + } + + /** + * Tests fix for BUG#54095 - Unnecessary call in newSetTimestampInternal. + * + * This bug was fixed as a consequence of the patch for Bug#71084. + * + * @throws Exception + * if the test fails. + */ + public void testBug54095() throws Exception { + Connection testConn = getConnectionWithProps("useLegacyDatetimeCode=false"); + + Calendar testCal = Calendar.getInstance(); + java.util.Date origDate = testCal.getTime(); + + PreparedStatement testPstmt = testConn.prepareStatement("SELECT ?"); + testPstmt.setTimestamp(1, new Timestamp(0), testCal); + assertEquals("Calendar object shouldn't have changed after PreparedStatement.setTimestamp().", origDate, testCal.getTime()); + + ResultSet testRs = testPstmt.executeQuery(); + testRs.next(); + assertEquals("Calendar object shouldn't have changed after PreparedStatement.executeQuery().", origDate, testCal.getTime()); + + testRs.getTimestamp(1, testCal); + assertEquals("Calendar object shouldn't have changed after ResultSet.getTimestamp().", origDate, testCal.getTime()); + + testRs.close(); + testPstmt.close(); + testConn.close(); + } + + /** + * Tests fix for BUG#50348 - mysql connector/j 5.1.10 render the wrong value for dateTime column in GMT DB. + * + * With the right time zone settings in server and client, and using the property 'useTimezone=true', time shifts are computed in the opposite direction of + * those that are computed otherwise. + * + * This issue is observed when the server is configured with time zone 'GMT' and the client other than 'GMT'. However, if the server's time zone is one + * equivalent to 'GMT' but under a different identifier, say "UTC" or "GMT+00", the wrong behavior isn't observed anymore. + */ + public void testBug50348() throws Exception { + final TimeZone defaultTZ = TimeZone.getDefault(); + + final Properties testConnProps = new Properties(); + testConnProps.setProperty("useTimezone", "true"); + testConnProps.setProperty("cacheDefaultTimezone", "false"); + + Connection testConn = null; + + try { + TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); // ~~ CST (UTC-06) + final SimpleDateFormat tsFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + final Timestamp timestamp = new Timestamp(tsFormat.parse("2015-01-01 10:00:00").getTime()); + final SimpleDateFormat tFormat = new SimpleDateFormat("HH:mm:ss"); + final Time time = new Time(tFormat.parse("10:00:00").getTime()); + + // Test a number of time zones that coincide with 'GMT' on the some specifip point in time. + for (String tz : new String[] { "Europe/Lisbon", "UTC", "GMT+00", "GMT" }) { + // Europe/Lisbon ~~ WET (UTC) on 2015-01-01; ~~ CET (UTC+01) on 1970-01-01 + System.out.println("\nServer time zone: " + tz); + System.out.println("---------------------------------------------------"); + + testConnProps.setProperty("serverTimezone", tz); + testConn = getConnectionWithProps(testConnProps); + + checkResultSetForTestBug50348(testConn, "2015-01-01 04:00:00.0", tz.equals("Europe/Lisbon") ? "03:00:00" : "04:00:00"); + checkPreparedStatementForTestBug50348(testConn, timestamp, time, "2015-01-01 16:00:00", tz.equals("Europe/Lisbon") ? "17:00:00" : "16:00:00"); + + testConn.close(); + } + + // Cycle through a wide range of generic 'GMT+/-hh:mm' and assert the expected time shift for a specific point in time. + for (int tzOffset = -15; tzOffset <= 15; tzOffset++) { // cover a wider range than standard + for (int tzSubOffset : new int[] { 0, 30 }) { + final StringBuilder tz = new StringBuilder("GMT"); + tz.append(tzOffset < 0 ? "-" : "+").append(String.format("%02d", Math.abs(tzOffset))); + tz.append(String.format(":%02d", tzSubOffset)); + + System.out.println("\nServer time zone: " + tz.toString()); + System.out.println("---------------------------------------------------"); + testConnProps.setProperty("serverTimezone", tz.toString()); + testConn = getConnectionWithProps(testConnProps); + + final int diffTzOffset = tzOffset + 6; // CST offset = -6 hours + final Calendar cal = Calendar.getInstance(); + + cal.setTime(tsFormat.parse("2015-01-01 10:00:00")); + cal.add(Calendar.HOUR, -diffTzOffset); + cal.add(Calendar.MINUTE, tzOffset < 0 ? tzSubOffset : -tzSubOffset); + String expectedTimestampFromRS = tsFormat.format(cal.getTime()) + ".0"; + cal.setTime(tFormat.parse("10:00:00")); + cal.add(Calendar.HOUR, -diffTzOffset); + cal.add(Calendar.MINUTE, tzOffset < 0 ? tzSubOffset : -tzSubOffset); + String expectedTimeFromRS = tFormat.format(cal.getTime()); + checkResultSetForTestBug50348(testConn, expectedTimestampFromRS, expectedTimeFromRS); + + cal.setTime(tsFormat.parse("2015-01-01 10:00:00")); + cal.add(Calendar.HOUR, diffTzOffset); + cal.add(Calendar.MINUTE, tzOffset < 0 ? -tzSubOffset : tzSubOffset); + String expectedTimestampFromPS = tsFormat.format(cal.getTime()); + cal.setTime(tFormat.parse("10:00:00")); + cal.add(Calendar.HOUR, diffTzOffset); + cal.add(Calendar.MINUTE, tzOffset < 0 ? -tzSubOffset : tzSubOffset); + String expectedTimeFromPS = tFormat.format(cal.getTime()); + checkPreparedStatementForTestBug50348(testConn, timestamp, time, expectedTimestampFromPS, expectedTimeFromPS); + + testConn.close(); + } + } + } finally { + TimeZone.setDefault(defaultTZ); + + if (testConn != null) { + testConn.close(); + } + } + } + + private void checkResultSetForTestBug50348(Connection testConn, String expectedTimestamp, String expectedTime) throws SQLException { + this.rs = testConn.createStatement().executeQuery("SELECT '2015-01-01 10:00:00', '10:00:00'"); + this.rs.next(); + String timestampAsString = this.rs.getTimestamp(1).toString(); + String timeAsString = this.rs.getTime(2).toString(); + String alert = expectedTimestamp.equals(timestampAsString) && expectedTime.equals(timeAsString) ? "" : " <-- (!)"; + System.out.printf("[RS] expected: '%s' | '%s'%n", expectedTimestamp, expectedTime); + System.out.printf(" actual: '%s' | '%s' %s%n", timestampAsString, timeAsString, alert); + assertEquals(expectedTimestamp, timestampAsString); + assertEquals(expectedTime, timeAsString); + } + + private void checkPreparedStatementForTestBug50348(Connection testConn, Timestamp timestamp, Time time, String expectedTimestamp, String expectedTime) + throws SQLException { + PreparedStatement testPstmt = testConn.prepareStatement("SELECT ?, ?"); + testPstmt.setTimestamp(1, timestamp); + testPstmt.setTime(2, time); + + this.rs = testPstmt.executeQuery(); + this.rs.next(); + String timestampAsString = new String(this.rs.getBytes(1)); + String timeAsString = new String(this.rs.getBytes(2)); + String alert = expectedTimestamp.equals(timestampAsString) && expectedTime.equals(timeAsString) ? "" : " <-- (!)"; + System.out.printf("[PS] expected: '%s' | '%s'%n", expectedTimestamp, expectedTime); + System.out.printf(" actual: '%s' | '%s' %s%n", timestampAsString, timeAsString, alert); + assertEquals(expectedTimestamp, timestampAsString); + assertEquals(expectedTime, timeAsString); + } + + /** + * Tests fix for Bug#77449 - Add 'truncateFractionalSeconds=true|false' property (contribution). + * + * The property actually added was 'sendFractionalSeconds' and works as the opposite of the proposed one. + */ + public void testBug77449() throws Exception { + if (!versionMeetsMinimum(5, 6, 4)) { + return; + } + + Timestamp originalTs = new Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse("2014-12-31 23:59:59.999").getTime()); + Timestamp roundedTs = new Timestamp(originalTs.getTime() + 1); + Timestamp truncatedTs = new Timestamp(originalTs.getTime() - 999); + + assertEquals("2014-12-31 23:59:59.999", originalTs.toString()); + assertEquals("2014-12-31 23:59:59.0", TimeUtil.truncateFractionalSeconds(originalTs).toString()); + + createTable("testBug77449", "(id INT PRIMARY KEY, ts_short TIMESTAMP, ts_long TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6))"); + createProcedure("testBug77449", "(ts_short TIMESTAMP, ts_long TIMESTAMP(6)) BEGIN SELECT ts_short, ts_long; END"); + + for (int tst = 0; tst < 8; tst++) { + boolean useLegacyDatetimeCode = (tst & 0x1) != 0; + boolean useServerPrepStmts = (tst & 0x2) != 0; + boolean sendFractionalSeconds = (tst & 0x4) != 0; + + String testCase = String.format("Case: %d [ %s | %s | %s ]", tst, useLegacyDatetimeCode ? "useLegDTCode" : "-", + useServerPrepStmts ? "useSSPS" : "-", sendFractionalSeconds ? "sendFracSecs" : "-"); + + Properties props = new Properties(); + props.setProperty("statementInterceptors", TestBug77449StatementInterceptor.class.getName()); + props.setProperty("useLegacyDatetimeCode", Boolean.toString(useLegacyDatetimeCode)); + props.setProperty("useServerPrepStmts", Boolean.toString(useServerPrepStmts)); + props.setProperty("sendFractionalSeconds", Boolean.toString(sendFractionalSeconds)); + + Connection testConn = getConnectionWithProps(props); + + // Send timestamps as Strings, using Statement -> no truncation occurs. + Statement testStmt = testConn.createStatement(); + testStmt.executeUpdate("INSERT INTO testBug77449 VALUES (1, '2014-12-31 23:59:59.999', '2014-12-31 23:59:59.999')/* no_ts_trunk */"); + testStmt.close(); + + // Send timestamps using PreparedStatement -> truncation occurs according to 'sendFractionalSeconds' value. + PreparedStatement testPStmt = testConn.prepareStatement("INSERT INTO testBug77449 VALUES (2, ?, ?)"); + testPStmt.setTimestamp(1, originalTs); + testPStmt.setTimestamp(2, originalTs); + assertEquals(testCase, 1, testPStmt.executeUpdate()); + testPStmt.close(); + + // Send timestamps using UpdatableResultSet -> truncation occurs according to 'sendFractionalSeconds' value. + testStmt = testConn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + testStmt.executeUpdate("INSERT INTO testBug77449 VALUES (3, NOW(), NOW())/* no_ts_trunk */"); // insert dummy row + this.rs = testStmt.executeQuery("SELECT * FROM testBug77449 WHERE id = 3"); + assertTrue(testCase, this.rs.next()); + this.rs.updateTimestamp("ts_short", originalTs); + this.rs.updateTimestamp("ts_long", originalTs); + this.rs.updateRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt("id", 4); + this.rs.updateTimestamp("ts_short", originalTs); + this.rs.updateTimestamp("ts_long", originalTs); + this.rs.insertRow(); + + // Assert values from previous inserts/updates. + // 1st row: from Statement sent as String, no subject to TZ conversions. + this.rs = this.stmt.executeQuery("SELECT * FROM testBug77449 WHERE id = 1"); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, 1, this.rs.getInt(1)); + assertEquals(testCase, roundedTs, this.rs.getTimestamp(2)); + assertEquals(testCase, originalTs, this.rs.getTimestamp(3)); + // 2nd row: from PreparedStatement; 3rd row: from UpdatableResultSet.updateRow(); 4th row: from UpdatableResultSet.insertRow() + this.rs = testStmt.executeQuery("SELECT * FROM testBug77449 WHERE id >= 2"); + for (int i = 2; i <= 4; i++) { + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, i, this.rs.getInt(1)); + assertEquals(testCase, sendFractionalSeconds ? roundedTs : truncatedTs, this.rs.getTimestamp(2)); + assertEquals(testCase, sendFractionalSeconds ? originalTs : truncatedTs, this.rs.getTimestamp(3)); + } + + this.stmt.execute("DELETE FROM testBug77449"); + + // Compare Connector/J with client truncation -> truncation occurs according to 'sendFractionalSeconds' value. + testPStmt = testConn.prepareStatement("SELECT ? = ?"); + testPStmt.setTimestamp(1, originalTs); + testPStmt.setTimestamp(2, truncatedTs); + this.rs = testPStmt.executeQuery(); + assertTrue(testCase, this.rs.next()); + if (sendFractionalSeconds) { + assertFalse(testCase, this.rs.getBoolean(1)); + } else { + assertTrue(testCase, this.rs.getBoolean(1)); + } + testPStmt.close(); + + // Send timestamps using CallableStatement -> truncation occurs according to 'sendFractionalSeconds' value. + CallableStatement cstmt = testConn.prepareCall("{call testBug77449(?, ?)}"); + cstmt.setTimestamp("ts_short", originalTs); + cstmt.setTimestamp("ts_long", originalTs); + cstmt.execute(); + this.rs = cstmt.getResultSet(); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, sendFractionalSeconds ? roundedTs : truncatedTs, this.rs.getTimestamp(1)); + assertEquals(testCase, sendFractionalSeconds ? originalTs : truncatedTs, this.rs.getTimestamp(2)); + + testConn.close(); + } + } + + public static class TestBug77449StatementInterceptor extends BaseStatementInterceptor { + private boolean sendFracSecs = false; + + @Override + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + this.sendFracSecs = Boolean.parseBoolean(props.getProperty("sendFractionalSeconds")); + super.init(conn, props); + } + + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (!(interceptedStatement instanceof ServerPreparedStatement)) { + String query = sql; + if (query == null && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + query = interceptedStatement.toString(); + query = query.substring(query.indexOf(':') + 2); + } + + if (query != null + && ((query.startsWith("INSERT") || query.startsWith("UPDATE") || query.startsWith("CALL")) && !query.contains("no_ts_trunk"))) { + if (this.sendFracSecs ^ query.contains(".999")) { + fail("Wrong TIMESTAMP trunctation in query [" + query + "]"); + } + } + } + return super.preProcess(sql, interceptedStatement, connection); + } + } + + /** + * Tests fix for BUG#77681 - rewrite replace sql like insert when rewriteBatchedStatements=true (contribution) + * + * When using 'rewriteBatchedStatements=true' we rewrite several batched statements into one single query by extending its VALUES clause. Although INSERT + * REPLACE have the same syntax, this wasn't happening for REPLACE statements. + * + * This tests the number of queries actually sent to server when rewriteBatchedStatements is used and not by using a StatementInterceptor. The test is + * repeated for server side prepared statements. Without the fix, this test fails while checking the number of expected REPLACE queries. + */ + public void testBug77681() throws Exception { + createTable("testBug77681", "(id INT, txt VARCHAR(50), PRIMARY KEY (id))"); + + Properties props = new Properties(); + props.setProperty("statementInterceptors", TestBug77681StatementInterceptor.class.getName()); + + for (int tst = 0; tst < 4; tst++) { + props.setProperty("useServerPrepStmts", Boolean.toString((tst & 0x1) != 0)); + props.setProperty("rewriteBatchedStatements", Boolean.toString((tst & 0x2) != 0)); + Connection testConn = getConnectionWithProps(props); + + PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testBug77681 VALUES (?, ?)"); + testPstmt.setInt(1, 1); + testPstmt.setString(2, "one"); + testPstmt.addBatch(); + testPstmt.setInt(1, 2); + testPstmt.setString(2, "two"); + testPstmt.addBatch(); + testPstmt.setInt(1, 3); + testPstmt.setString(2, "three"); + testPstmt.addBatch(); + testPstmt.setInt(1, 4); + testPstmt.setString(2, "four"); + testPstmt.addBatch(); + testPstmt.setInt(1, 5); + testPstmt.setString(2, "five"); + testPstmt.addBatch(); + testPstmt.executeBatch(); + testPstmt.close(); + + testPstmt = testConn.prepareStatement("REPLACE INTO testBug77681 VALUES (?, ?)"); + testPstmt.setInt(1, 2); + testPstmt.setString(2, "TWO"); + testPstmt.addBatch(); + testPstmt.setInt(1, 4); + testPstmt.setString(2, "FOUR"); + testPstmt.addBatch(); + testPstmt.setInt(1, 6); + testPstmt.setString(2, "SIX"); + testPstmt.addBatch(); + testPstmt.executeBatch(); + testPstmt.close(); + + Statement testStmt = testConn.createStatement(); + testStmt.clearBatch(); + testStmt.addBatch("INSERT INTO testBug77681 VALUES (7, 'seven')"); + testStmt.addBatch("INSERT INTO testBug77681 VALUES (8, 'eight')"); + testStmt.addBatch("INSERT INTO testBug77681 VALUES (9, 'nine')"); + testStmt.addBatch("INSERT INTO testBug77681 VALUES (10, 'ten')"); + testStmt.addBatch("INSERT INTO testBug77681 VALUES (11, 'eleven')"); + testStmt.executeBatch(); + + testStmt.clearBatch(); + testStmt.addBatch("REPLACE INTO testBug77681 VALUES (8, 'EIGHT')"); + testStmt.addBatch("REPLACE INTO testBug77681 VALUES (10, 'TEN')"); + testStmt.addBatch("REPLACE INTO testBug77681 VALUES (12, 'TWELVE')"); + testStmt.addBatch("REPLACE INTO testBug77681 VALUES (14, 'FOURTEEN')"); + testStmt.addBatch("REPLACE INTO testBug77681 VALUES (16, 'SIXTEEN')"); + testStmt.executeBatch(); + + this.stmt.executeUpdate("DELETE FROM testBug77681"); + } + } + + public static class TestBug77681StatementInterceptor extends BaseStatementInterceptor { + private static final char[] expectedNonRWBS = new char[] { 'I', 'I', 'I', 'I', 'I', 'R', 'R', 'R', 'I', 'I', 'I', 'I', 'I', 'R', 'R', 'R', 'R', 'R' }; + private static final char[] expectedRWBS = new char[] { 'I', 'R', 'I', 'R' }; + + private char[] expected; + private int execCounter = 0; + + @Override + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + super.init(conn, props); + System.out.println("\nuseServerPrepStmts: " + props.getProperty("useServerPrepStmts") + " | rewriteBatchedStatements: " + + props.getProperty("rewriteBatchedStatements")); + System.out.println("--------------------------------------------------------------------------------"); + this.expected = Boolean.parseBoolean(props.getProperty("rewriteBatchedStatements")) ? expectedRWBS : expectedNonRWBS; + } + + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + String query = sql; + if (query == null && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + query = interceptedStatement.toString(); + query = query.substring(query.indexOf(':') + 2); + } + if (query != null && query.indexOf("testBug77681") != -1) { + System.out.println(this.execCounter + " --> " + query); + if (this.execCounter > this.expected.length) { + fail("Failed to rewrite statements"); + } + assertEquals("Wrong statement at execution number " + this.execCounter, this.expected[this.execCounter++], query.charAt(0)); + } + return super.preProcess(sql, interceptedStatement, connection); + } + } + + /** + * Tests fix for Bug#21876798 - CONNECTOR/J WITH MYSQL FABRIC AND SPRING PRODUCES PROXY ERROR. + * + * Although this is a Fabric related bug we are able reproduce it using a couple of multi-host connections. + */ + public void testBug21876798() throws Exception { + createTable("testBug21876798", "(tst INT, val INT)"); + + for (int tst = 0; tst < 4; tst++) { + boolean useServerPrepStmts = (tst & 0x1) != 0; + boolean rewriteBatchedStatements = (tst & 0x2) != 0; + + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", Boolean.toString(useServerPrepStmts)); + props.setProperty("rewriteBatchedStatements", Boolean.toString(rewriteBatchedStatements)); + + String testCase = String.format("Case: %d [ %s | %s ]", tst, useServerPrepStmts ? "useSPS" : "-", + rewriteBatchedStatements ? "rwBatchedStmts" : "-"); + + Connection highLevelConn = getLoadBalancedConnection(props); + assertTrue(testCase, highLevelConn.getClass().getName().startsWith("com.sun.proxy") || highLevelConn.getClass().getName().startsWith("$Proxy")); + + Connection lowLevelConn = getMasterSlaveReplicationConnection(props); + // This simulates the behavior from Fabric connections that are causing the problem. + ((ReplicationConnection) lowLevelConn).setProxy((MySQLConnection) highLevelConn); + + // Insert data. We need at least 4 rows to force rewriting batch statements. + this.pstmt = lowLevelConn.prepareStatement("INSERT INTO testBug21876798 VALUES (?, ?)"); + for (int i = 1; i <= 4; i++) { + this.pstmt.setInt(1, tst); + this.pstmt.setInt(2, i); + this.pstmt.addBatch(); + } + this.pstmt.executeBatch(); + + // Check if data was inserted correctly. + this.rs = this.stmt.executeQuery("SELECT val FROM testBug21876798 WHERE tst = " + tst); + for (int i = 1; i <= 4; i++) { + assertTrue(testCase + "/Row#" + i, this.rs.next()); + assertEquals(testCase + "/Row#" + i, i, this.rs.getInt(1)); + } + assertFalse(testCase, this.rs.next()); + + // Update data. We need at least 4 rows to force rewriting batch statements. + this.pstmt = lowLevelConn.prepareStatement("UPDATE testBug21876798 SET val = ? WHERE tst = ? AND val = ?"); + for (int i = 1; i <= 4; i++) { + this.pstmt.setInt(1, -i); + this.pstmt.setInt(2, tst); + this.pstmt.setInt(3, i); + this.pstmt.addBatch(); + } + this.pstmt.executeBatch(); + + // Check if data was updated correctly. + this.rs = this.stmt.executeQuery("SELECT val FROM testBug21876798 WHERE tst = " + tst); + for (int i = 1; i <= 4; i++) { + assertTrue(testCase + "/Row#" + i, this.rs.next()); + assertEquals(testCase + "/Row#" + i, -i, this.rs.getInt(1)); + } + assertFalse(testCase, this.rs.next()); + + lowLevelConn.close(); + highLevelConn.close(); + } + } + + /** + * Tests fix for Bug#78961 - Can't call MySQL procedure with InOut parameters in Fabric environment. + * + * Although this is a Fabric related bug we are able reproduce it using a couple of multi-host connections. + */ + public void testBug78961() throws Exception { + createProcedure("testBug78961", "(IN c1 FLOAT, IN c2 FLOAT, OUT h FLOAT, INOUT t FLOAT) BEGIN SET h = SQRT(c1 * c1 + c2 * c2); SET t = t + h; END;"); + + Connection highLevelConn = getLoadBalancedConnection(null); + assertTrue(highLevelConn.getClass().getName().startsWith("com.sun.proxy") || highLevelConn.getClass().getName().startsWith("$Proxy")); + + Connection lowLevelConn = getMasterSlaveReplicationConnection(null); + // This simulates the behavior from Fabric connections that are causing the problem. + ((ReplicationConnection) lowLevelConn).setProxy((MySQLConnection) highLevelConn); + + CallableStatement cstmt = lowLevelConn.prepareCall("{CALL testBug78961 (?, ?, ?, ?)}"); + cstmt.setFloat(1, 3.0f); + cstmt.setFloat(2, 4.0f); + cstmt.setFloat(4, 5.0f); + cstmt.registerOutParameter(3, Types.FLOAT); + cstmt.registerOutParameter(4, Types.FLOAT); + cstmt.execute(); + + assertEquals(5.0f, cstmt.getFloat(3)); + assertEquals(10.0f, cstmt.getFloat(4)); + } + + /** + * Test Bug#75956 - Inserting timestamps using a server PreparedStatement and useLegacyDatetimeCode=false + */ + public void testBug75956() throws Exception { + createTable("bug75956", "(id int not null primary key auto_increment, dt1 datetime, dt2 datetime)"); + Connection sspsConn = getConnectionWithProps("useCursorFetch=true,useLegacyDatetimeCode=false"); + this.pstmt = sspsConn.prepareStatement("insert into bug75956 (dt1, dt2) values (?, ?)"); + this.pstmt.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + this.pstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis())); + this.pstmt.addBatch(); + this.pstmt.clearParameters(); + this.pstmt.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + this.pstmt.setTimestamp(2, null); + this.pstmt.addBatch(); + this.pstmt.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + this.pstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis())); + this.pstmt.addBatch(); + this.pstmt.executeBatch(); + this.pstmt.close(); + this.rs = sspsConn.createStatement().executeQuery("select count(*) from bug75956 where dt2 is NULL"); + this.rs.next(); + assertEquals(1, this.rs.getInt(1)); + sspsConn.close(); + } + + /** + * Tests fix for Bug#71131 - Poor error message in CallableStatement.java. + */ + public void testBug71131() throws Exception { + createProcedure("testBug71131", "(IN r DOUBLE, OUT p DOUBLE) BEGIN SET p = 2 * r * PI(); END"); + final CallableStatement cstmt = this.conn.prepareCall("{ CALL testBug71131 (?, 5) }"); + assertThrows(SQLException.class, "Parameter p is not registered as an output parameter", new Callable() { + public Void call() throws Exception { + cstmt.execute(); + return null; + } + }); + cstmt.close(); + } + + /** + * Tests fix for Bug#23188498 - CLIENT HANG WHILE USING SERVERPREPSTMT WHEN PROFILESQL=TRUE AND USEIS=TRUE. + */ + public void testBug23188498() throws Exception { + createTable("testBug23188498", "(id INT)"); + + MySQLConnection testConn = (com.mysql.jdbc.MySQLConnection) getConnectionWithProps("useServerPrepStmts=true,useInformationSchema=true,profileSQL=true"); + ExecutorService executor = Executors.newSingleThreadExecutor(); + + // Insert data: + this.pstmt = testConn.prepareStatement("INSERT INTO testBug23188498 (id) VALUES (?)"); + this.pstmt.setInt(1, 10); + final PreparedStatement localPStmt1 = this.pstmt; + Future future1 = executor.submit(new Callable() { + public Void call() throws Exception { + localPStmt1.executeUpdate(); + return null; + } + }); + try { + future1.get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + // The connection hung, forcibly closing it releases resources. + this.stmt.execute("KILL CONNECTION " + testConn.getId()); + fail("Connection hung after executeUpdate()."); + } + this.pstmt.close(); + + // Fetch data: + this.pstmt = testConn.prepareStatement("SELECT * FROM testBug23188498 WHERE id > ?"); + this.pstmt.setInt(1, 1); + final PreparedStatement localPStmt2 = this.pstmt; + Future future2 = executor.submit(new Callable() { + public ResultSet call() throws Exception { + return localPStmt2.executeQuery(); + } + }); + try { + this.rs = future2.get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + // The connection hung, forcibly closing it releases resources. + this.stmt.execute("KILL CONNECTION " + testConn.getId()); + fail("Connection hung after executeQuery()."); + } + assertTrue(this.rs.next()); + assertEquals(10, this.rs.getInt(1)); + assertFalse(this.rs.next()); + this.pstmt.close(); + + executor.shutdownNow(); + testConn.close(); + } + + /** + * Tests fix for Bug#23201930 - CLIENT HANG WHEN RSLT CUNCURRENCY=CONCUR_UPDATABLE AND RSLTSET TYPE=FORWARD_ONLY. + */ + public void testBug23201930() throws Exception { + boolean useSSL = false; + boolean useSPS = false; + boolean useCursor = false; + boolean useCompr = false; + + final char[] chars = new char[32 * 1024]; + Arrays.fill(chars, 'x'); + final String longData = String.valueOf(chars); // Using large data makes SSL connections hang sometimes. + + do { + final String testCase = String.format("Case [SSL: %s, SPS: %s, Cursor: %s, Compr: %s]", useSSL ? "Y" : "N", useSPS ? "Y" : "N", + useCursor ? "Y" : "N", useCompr ? "Y" : "N"); + + createTable("testBug23201930", "(id TINYINT AUTO_INCREMENT PRIMARY KEY, f1 INT DEFAULT 1, f2 INT DEFAULT 1, f3 INT DEFAULT 1, " + + "f4 INT DEFAULT 1, f5 INT DEFAULT 1, fl LONGBLOB)"); + + final Properties props = new Properties(); + props.setProperty("useSSL", Boolean.toString(useSSL)); + if (useSSL) { + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "false"); + } + props.setProperty("useServerPrepStmts", Boolean.toString(useSPS)); + props.setProperty("useCursorFetch", Boolean.toString(useCursor)); + if (useCursor) { + props.setProperty("defaultFetchSize", "1"); + } + props.setProperty("useCompression", Boolean.toString(useCompr)); + + final MySQLConnection testConn = (MySQLConnection) getConnectionWithProps(props); + + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Future future = executor.submit(new Callable() { + public Void call() throws Exception { + final Statement testStmt = testConn.createStatement(); + testStmt.execute("INSERT INTO testBug23201930 (id) VALUES (100)"); + + PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testBug23201930 (id, fl) VALUES (?, ?)", ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_UPDATABLE); + testPstmt.setObject(1, 101, java.sql.Types.INTEGER); + testPstmt.setObject(2, longData, java.sql.Types.VARCHAR); + testPstmt.execute(); + testPstmt.setObject(1, 102, java.sql.Types.INTEGER); + testPstmt.execute(); + testPstmt.close(); + + testPstmt = testConn.prepareStatement("SELECT * FROM testBug23201930 WHERE id >= ? ORDER BY id ASC", ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_UPDATABLE); + testPstmt.setObject(1, 100, java.sql.Types.INTEGER); + final ResultSet testRs = testPstmt.executeQuery(); + assertTrue(testRs.next()); + assertEquals(100, testRs.getInt(1)); + assertTrue(testRs.next()); + assertEquals(101, testRs.getInt(1)); + assertTrue(testRs.next()); + assertEquals(102, testRs.getInt(1)); + assertFalse(testRs.next()); + testPstmt.close(); + return null; + } + }); + + try { + future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + // The connection hung, forcibly closing it releases resources. + this.stmt.executeQuery("KILL CONNECTION " + testConn.getId()); + fail(testCase + ": Connection hung!"); + } + executor.shutdownNow(); + + testConn.close(); + } while ((useSSL = !useSSL) || (useSPS = !useSPS) || (useCursor = !useCursor) || (useCompr = !useCompr)); // Cycle through all possible combinations. + } + + /** + * Tests fix for Bug#80615 - prepared statement leak when rewriteBatchedStatements=true and useServerPrepStmt. + * + * There are two bugs here: + * 1. A server prepared statement leakage by not actually closing the statement on server when .close() is called in the client side. This occurs when + * setting 'cachePrepStmts=true&useServerPrepStmts=true' and a prepared statement is set as non-poolable ('setPoolable(false)'). By itself this doesn't + * cause any visible issue because the connector has a fail-safe mechanism that uses client-side prepared statements when server-side prepared statements + * fail to be prepared. So, the connector ends up using client-side prepared statements after the number of open prepared statements on server hits the + * value of 'max_prepared_stmt_count'. + * 2. A prepared statement fails to be prepared when there are too many open prepared statements on server. By setting the options + * 'rewriteBatchedStatements=true&useServerPrepStmts=true' when a query happens to be rewritten a new (server-side) prepared statement is required but the + * fail-safe mechanism isn't implemented in this spot, so, since the leakage described above already consumed all available prepared statements on server, + * this ends up throwing the exception. + * + * This test combines three elements: + * 1. Call .close() on a server prepared statement. This promotes a prepared statement for caching if prepared statements cache is enabled. + * 2. cachePrepStmts=true|false. Turns on/off the prepared statements cache. + * 3. Call .setPoolable(true|false) on the prepared statement. This allows canceling the prepared statement caching, on a per statement basis. It has no + * effect if the prepared statements cache if turned off for the current connection. + * + * Expected behavior: + * - If .close() is not called on server prepared statements then they also can't be promoted for caching. This causes a server prepared statements leak in + * all remaining combinations. + * - If .close() is called on server prepared statements and the prepared statements cache is disabled by any form (either per connection or per statement), + * then the statements is immediately closed on server side too. + * - If .close() is called on server prepared statements and the prepared statements cache is enabled (both in the connection and in the statement) then the + * statement is cached and only effectively closed in the server side if and when removed from the cache. + */ + public void testBug80615() throws Exception { + final int prepStmtCacheSize = 5; + final int maxPrepStmtCount = 25; + final int testRepetitions = maxPrepStmtCount + 5; + int maxPrepStmtCountOri = -1; + + try { + // Check if it is possible to create a server prepared statement with the current max_prepared_stmt_count. + Connection checkConn = getConnectionWithProps("useServerPrepStmts=true"); + PreparedStatement checkPstmt = checkConn.prepareStatement("SELECT 1"); + assertTrue("Failed to create a server prepared statement possibly because there are too many active prepared statements on server already.", + checkPstmt instanceof ServerPreparedStatement); + checkPstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT @@GLOBAL.max_prepared_stmt_count"); + this.rs.next(); + maxPrepStmtCountOri = this.rs.getInt(1); + + this.stmt.execute("SET GLOBAL max_prepared_stmt_count = " + maxPrepStmtCount); + this.stmt.execute("FLUSH STATUS"); + + // Check if it is still possible to prepare new statements after setting the new max. This test requires at least prepStmtCacheSize + 2. + // The extra two statements are: + // 1 - The first statement that only gets cached in the end (when calling .close() on it). + // 2 - The statement that triggers the expelling of the oldest element of the cache to get room for itself. + for (int i = 1; i <= prepStmtCacheSize + 2; i++) { + checkPstmt = checkConn.prepareStatement("SELECT " + i); + assertTrue("Test ABORTED because the server doesn't allow preparing at least " + (prepStmtCacheSize + 2) + " more statements.", + checkPstmt instanceof ServerPreparedStatement); + } + checkConn.close(); // Also closes all prepared statements. + + // Good to go, start the test. + boolean closeStmt = false; + boolean useCache = false; + boolean poolable = false; + do { + final String testCase = String.format("Case: [Close STMTs: %s, Use cache: %s, Poolable: %s ]", closeStmt ? "Y" : "N", useCache ? "Y" : "N", + poolable ? "Y" : "N"); + + System.out.println(); + System.out.println(testCase); + System.out.println("********************************************************************************"); + + createTable("testBug80615", "(id INT)"); + + final Properties props = new Properties(); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("cachePrepStmts", Boolean.toString(useCache)); + if (useCache) { + props.setProperty("prepStmtCacheSize", String.valueOf(prepStmtCacheSize)); + } + + final Connection testConn = getConnectionWithProps(props); + final Statement checkStmt = testConn.createStatement(); + + // Prepare a statement to be executed later. This is prepare #1. + PreparedStatement testPstmt1 = testConn.prepareStatement("INSERT INTO testBug80615 VALUES (?)"); + assertTrue(testCase, testPstmt1 instanceof ServerPreparedStatement); + ((StatementImpl) testPstmt1).setPoolable(poolable); // Need to cast, this is a JDBC 4.0 feature. + testPstmt1.setInt(1, 100); + testPstmt1.addBatch(); + testPstmt1.setInt(1, 200); + testPstmt1.addBatch(); + + int prepCount = 1; // One server-side prepared statement already prepared. + int expectedPrepCount = prepCount; + int expectedExecCount = 0; + int expectedCloseCount = 0; + + testBug80615CheckComStmtStatus(prepCount, true, testCase, checkStmt, expectedPrepCount, expectedExecCount, expectedCloseCount); + + // Prepare a number of statements higher than the limit set on server. There are at most (*) maxPrepStmtCount - 1 prepares available. + // This should exhaust the number of allowed prepared statements, forcing the connector to use client-side prepared statements from that point + // forward unless statements are closed correctly. + // Under the tested circumstances there where some unexpected server prepared statements leaks (1st bug). + // (*) There's no canonical way of knowing exactly how many preparing statement slots are available because other sessions may be using them. + boolean isSPS = true; + do { + PreparedStatement testPstmt2 = testConn.prepareStatement("INSERT INTO testBug80615 VALUES (" + prepCount + " + ?)"); + prepCount++; + + isSPS = testPstmt2 instanceof ServerPreparedStatement; + if (closeStmt) { + // Statements are being correctly closed so there is room to create new ones every time. + assertTrue(testCase, isSPS); + } else if (prepCount > maxPrepStmtCount) { + // Not closing statements causes a server prepared statements leak on server. + // In this iteration (if not before) it should have started failing-over to a client-side prepared statement. + assertFalse(testCase, isSPS); + } else if (prepCount <= prepStmtCacheSize + 2) { + // There should be enough room to prepare server-side prepared statements. (This was checked in the beginning.) + assertTrue(testCase, isSPS); + } // prepStmtCacheSize + 1 < prepCount <= maxPrepStmtCount --> can't assert anything as there can statements prepared externally. + + ((StatementImpl) testPstmt2).setPoolable(poolable); // Need to cast, this is a JDBC 4.0 feature. + testPstmt2.setInt(1, 0); + testPstmt2.execute(); + if (isSPS) { + expectedPrepCount++; + expectedExecCount++; + } + if (closeStmt) { + testPstmt2.close(); + if (isSPS) { + if (useCache && poolable && (prepCount - 1) > prepStmtCacheSize) { // The first statement isn't cached yet. + // A statement (oldest in cache) is effectively closed on server side only after local statements cache is full. + expectedCloseCount++; + } else if (!useCache || !poolable) { + // The statement is closed immediately on server side. + expectedCloseCount++; + } + } + } + + testBug80615CheckComStmtStatus(prepCount, isSPS, testCase, checkStmt, expectedPrepCount, expectedExecCount, expectedCloseCount); + } while (prepCount < testRepetitions && isSPS); + + if (closeStmt) { + assertEquals(testCase, testRepetitions, prepCount); + } else { + assertTrue(testCase, prepCount > prepStmtCacheSize + 2); + assertTrue(testCase, prepCount <= maxPrepStmtCount + 1); + } + + // Batched statements are being rewritten so this will prepare another statement underneath. + // It was failing before if the the number of stmt prepares on server was exhausted at this point (2nd Bug). + testPstmt1.executeBatch(); + testPstmt1.close(); + + testConn.close(); + } while ((closeStmt = !closeStmt) || (useCache = !useCache) || (poolable = !poolable)); + } finally { + if (maxPrepStmtCountOri >= 0) { + this.stmt.execute("SET GLOBAL max_prepared_stmt_count = " + maxPrepStmtCountOri); + this.stmt.execute("FLUSH STATUS"); + } + } + } + + private void testBug80615CheckComStmtStatus(int prepCount, boolean isSPS, String testCase, Statement testStmt, int expectedPrepCount, int expectedExecCount, + int expectedCloseCount) throws Exception { + System.out.print(prepCount + ". "); + System.out.print(isSPS ? "[SPS]" : "[CPS]"); + + testCase += "\nIteration: " + prepCount; + + int actualPrepCount = 0; + int actualExecCount = 0; + int actualCloseCount = 0; + this.rs = testStmt.executeQuery("SHOW SESSION STATUS WHERE Variable_name IN ('Com_stmt_prepare', 'Com_stmt_execute', 'Com_stmt_close')"); + while (this.rs.next()) { + System.out.print(" (" + this.rs.getString(1).replace("Com_stmt_", "") + " " + this.rs.getInt(2) + ")"); + if (this.rs.getString(1).equalsIgnoreCase("Com_stmt_prepare")) { + actualPrepCount = this.rs.getInt(2); + } else if (this.rs.getString(1).equalsIgnoreCase("Com_stmt_execute")) { + actualExecCount = this.rs.getInt(2); + } else if (this.rs.getString(1).equalsIgnoreCase("Com_stmt_close")) { + actualCloseCount = this.rs.getInt(2); + } + } + System.out.println(); + + assertEquals(testCase, expectedPrepCount, actualPrepCount); + assertEquals(testCase, expectedExecCount, actualExecCount); + assertEquals(testCase, expectedCloseCount, actualCloseCount); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/StressRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/StressRegressionTest.java new file mode 100644 index 0000000..5f647d7 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/StressRegressionTest.java @@ -0,0 +1,478 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +import testsuite.BaseTestCase; + +/** + * Tests for multi-thread stress regressions. + */ +public class StressRegressionTest extends BaseTestCase { + private int numThreadsStarted; + + /** + * Creates a new StressRegressionTest + * + * @param name + * the name of the test. + */ + public StressRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StressRegressionTest.class); + } + + /** + * @throws Exception + */ + public synchronized void testContention() throws Exception { + if (!this.DISABLED_testContention) { + System.out.println("Calculating baseline elapsed time..."); + + long start = System.currentTimeMillis(); + + contentiousWork(this.conn, this.stmt, 0); + + long singleThreadElapsedTimeMillis = System.currentTimeMillis() - start; + + System.out.println("Single threaded execution took " + singleThreadElapsedTimeMillis + " ms."); + + int numThreadsToStart = 95; + + System.out.println("\nStarting " + numThreadsToStart + " threads."); + + this.numThreadsStarted = numThreadsToStart; + + ContentionThread[] threads = new ContentionThread[this.numThreadsStarted]; + + for (int i = 0; i < numThreadsToStart; i++) { + threads[i] = new ContentionThread(i); + threads[i].start(); + } + + for (;;) { + try { + wait(); + + if (this.numThreadsStarted == 0) { + break; + } + } catch (InterruptedException ie) { + // ignore + } + } + + // Collect statistics... + System.out.println("Done!"); + + double avgElapsedTimeMillis = 0; + + List elapsedTimes = new ArrayList(); + + for (int i = 0; i < numThreadsToStart; i++) { + elapsedTimes.add(new Long(threads[i].elapsedTimeMillis)); + + avgElapsedTimeMillis += ((double) threads[i].elapsedTimeMillis / numThreadsToStart); + } + + Collections.sort(elapsedTimes); + + System.out.println("Average elapsed time per-thread was " + avgElapsedTimeMillis + " ms."); + System.out.println("Median elapsed time per-thread was " + elapsedTimes.get(elapsedTimes.size() / 2) + " ms."); + System.out.println("Minimum elapsed time per-thread was " + elapsedTimes.get(0) + " ms."); + System.out.println("Maximum elapsed time per-thread was " + elapsedTimes.get(elapsedTimes.size() - 1) + " ms."); + } + } + + /** + * @throws Exception + */ + public void testCreateConnections() throws Exception { + Thread t = new CreateThread(); + t.start(); + t.join(); + } + + /** + * @throws Exception + */ + public void testCreateConnectionsUnderLoad() throws Exception { + Thread t = new CreateThread(new BusyThread()); + t.start(); + t.join(); + } + + /** + * @param threadConn + * @param threadStmt + * @param threadNumber + */ + void contentiousWork(Connection threadConn, Statement threadStmt, int threadNumber) { + Date now = new Date(); + + try { + for (int i = 0; i < 1000; i++) { + ResultSet threadRs = threadStmt.executeQuery("SELECT 1, 2"); + + while (threadRs.next()) { + threadRs.getString(1); + threadRs.getString(2); + } + + threadRs.close(); + + PreparedStatement pStmt = threadConn.prepareStatement("SELECT ?"); + pStmt.setTimestamp(1, new Timestamp(now.getTime())); + + threadRs = pStmt.executeQuery(); + + while (threadRs.next()) { + threadRs.getTimestamp(1); + } + + threadRs.close(); + pStmt.close(); + } + } catch (Exception ex) { + throw new RuntimeException(ex.toString()); + } + } + + synchronized void reportDone() { + // TODO: This test should just be refactored to use an executor and futures. + // this.numThreadsStarted--; + notify(); + } + + public class BusyThread extends Thread { + boolean stop = false; + + @Override + public void run() { + boolean doStop = this.stop; + while (!doStop) { + doStop = this.stop; + } + } + } + + class ContentionThread extends Thread { + Connection threadConn; + + Statement threadStmt; + + int threadNumber; + + long elapsedTimeMillis; + + public ContentionThread(int num) throws SQLException { + this.threadNumber = num; + this.threadConn = getConnectionWithProps(new Properties()); + this.threadStmt = this.threadConn.createStatement(); + + System.out.println(this.threadConn); + } + + @Override + public void run() { + long start = System.currentTimeMillis(); + + try { + contentiousWork(this.threadConn, this.threadStmt, this.threadNumber); + this.elapsedTimeMillis = System.currentTimeMillis() - start; + + System.out.println("Thread " + this.threadNumber + " finished."); + } finally { + if (this.elapsedTimeMillis == 0) { + this.elapsedTimeMillis = System.currentTimeMillis() - start; + } + + reportDone(); + + try { + this.threadStmt.close(); + this.threadConn.close(); + } catch (SQLException ex) { + // ignore + } + } + } + } + + class CreateThread extends Thread { + BusyThread busyThread; + + int numConnections = 15; + + public CreateThread() { + } + + public CreateThread(BusyThread toStop) { + this.busyThread = toStop; + } + + public CreateThread(int numConns) { + this.numConnections = numConns; + } + + @Override + public void run() { + try { + Connection[] connList = new Connection[this.numConnections]; + + long maxConnTime = Long.MIN_VALUE; + long minConnTime = Long.MAX_VALUE; + double averageTime = 0; + + Properties nullProps = new Properties(); + + if (this.busyThread != null) { + this.busyThread.start(); + } + + for (int i = 0; i < this.numConnections; i++) { + long startTime = System.currentTimeMillis(); + connList[i] = getConnectionWithProps(nullProps); + + long endTime = System.currentTimeMillis(); + long ellapsedTime = endTime - startTime; + + if (ellapsedTime < minConnTime) { + minConnTime = ellapsedTime; + } + + if (ellapsedTime > maxConnTime) { + maxConnTime = ellapsedTime; + } + + averageTime += ((double) ellapsedTime / this.numConnections); + } + + if (this.busyThread != null) { + this.busyThread.stop = true; + } + + for (int i = 0; i < this.numConnections; i++) { + connList[i].close(); + } + + System.out.println(minConnTime + "/" + maxConnTime + "/" + averageTime); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * Tests fix for BUG#67760 - Deadlock when concurrently executing prepared statements with Timestamp objects + * + * Concurrent execution of Timestamp, Date and Time related setters and getters from a PreparedStatement and ResultSet object obtained from a same shared + * Connection may result in a deadlock. + * + * This test exploits a non-deterministic situation that can end in a deadlock. It executes two concurrent jobs for 10 seconds while stressing the referred + * methods. The deadlock was observed before 3 seconds have elapsed, all times, in development environment. + * + * WARNING! If this test fails there is no guarantee that the JVM will remain stable and won't affect any other tests. It is imperative that this test + * passes to ensure other tests results. + * + * @throws Exception + * if the test fails. + */ + public void testBug67760() throws Exception { + /* + * Use a brand new Connection not shared by anyone else, otherwise it may block later on test teardown. + */ + final Connection testConn = getConnectionWithProps(""); + + /* + * Thread to execute set[Timestamp|Date|Time]() methods in an instance of a PreparedStatement constructed from a shared Connection. + */ + Thread job1 = new Thread(new Runnable() { + public void run() { + try { + System.out.println("Starting job 1 (" + Thread.currentThread().getName() + ") - PreparedStatement.set[Timestamp|Date|Time]()..."); + PreparedStatement testPstmt = testConn.prepareStatement("SELECT ?, ?, ?"); + + Timestamp ts = new Timestamp(System.currentTimeMillis()); + java.sql.Date dt = new java.sql.Date(System.currentTimeMillis()); + Time tm = new Time(System.currentTimeMillis()); + + while (SharedInfoForTestBug67760.running) { + SharedInfoForTestBug67760.job1Iterations++; + + testPstmt.setTimestamp(1, ts); + testPstmt.setDate(2, dt); + testPstmt.setTime(3, tm); + testPstmt.execute(); + } + System.out.println( + "Finishing job 1 (" + Thread.currentThread().getName() + ") after " + SharedInfoForTestBug67760.job1Iterations + " iterations..."); + testPstmt.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + /* + * Thread to execute get[Timestamp|Date|Time]() methods in an instance of a ResultSet obtained from a PreparedStatement constructed from a shared + * Connection. + */ + Thread job2 = new Thread(new Runnable() { + public void run() { + try { + System.out.println("Starting job 2 (" + Thread.currentThread().getName() + ") - ResultSet.get[Timestamp|Date|Time]()..."); + PreparedStatement testPstmt = testConn.prepareStatement("SELECT NOW(), CAST(NOW() AS DATE), CAST(NOW() AS TIME)"); + + while (SharedInfoForTestBug67760.running) { + SharedInfoForTestBug67760.job2Iterations++; + + ResultSet testRs = testPstmt.executeQuery(); + testRs.next(); + testRs.getTimestamp(1); + testRs.getDate(2); + testRs.getTime(3); + testRs.close(); + } + System.out.println( + "Finishing job 2 (" + Thread.currentThread().getName() + ") after " + SharedInfoForTestBug67760.job2Iterations + " iterations..."); + testPstmt.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + /* + * Start concurrent jobs and let them run for 10 seconds (100 * 100 milliseconds). + * Monitor jobs activity while they are running, allowing, at the most, a period of 2 seconds (20 * 100 milliseconds) inactivity before the test fails. + */ + final int recheckWaitTimeUnit = 100; + int recheckWaitTimeCountdown = 100; + + final int delta0IterationsCountdownSize = 20; + int delta0IterationsCountdown = delta0IterationsCountdownSize; + + System.out.println("Start concurrent jobs and let them run for aproximatly " + (recheckWaitTimeUnit * recheckWaitTimeCountdown / 1000) + " seconds..."); + final long startTime = System.currentTimeMillis(); + long elapsedTime = 0; + job1.start(); + job2.start(); + + do { + if (recheckWaitTimeCountdown == 1) { + // time to stop monitoring and finish the jobs + SharedInfoForTestBug67760.running = false; + } + Thread.sleep(recheckWaitTimeUnit); + + delta0IterationsCountdown = SharedInfoForTestBug67760.iterationsChanged() ? delta0IterationsCountdownSize : delta0IterationsCountdown - 1; + if (SharedInfoForTestBug67760.running && (!job1.isAlive() || !job2.isAlive())) { + fail("Something as failed. At least one of the threads has died."); + } + + if (delta0IterationsCountdown == 0 || !SharedInfoForTestBug67760.running) { + if (!SharedInfoForTestBug67760.running && (job1.isAlive() || job2.isAlive())) { + // jobs haven't died yet, allow them some more time to die + Thread.sleep(1000); + } + + if (job1.isAlive() && job2.isAlive()) { + // there must be a deadlock + elapsedTime = System.currentTimeMillis() - startTime; + System.out.println("Possible deadlock detected after " + elapsedTime + " milliseconds."); + + ThreadMXBean threadMXbean = ManagementFactory.getThreadMXBean(); + + ThreadInfo thread1Info = threadMXbean.getThreadInfo(job1.getId(), Integer.MAX_VALUE); + System.out.printf("%n%s stopped at iteration %d, blocked by the lock %s, owned by %s%n", job1.getName(), + SharedInfoForTestBug67760.job1Iterations, thread1Info.getLockName(), thread1Info.getLockOwnerName()); + System.out.println("Stacktrace:"); + for (StackTraceElement element : thread1Info.getStackTrace()) { + System.out.println(" " + element); + } + + ThreadInfo thread2Info = threadMXbean.getThreadInfo(job2.getId(), Integer.MAX_VALUE); + System.out.printf("%n%s stopped at iteration %d, blocked by the lock %s, owned by %s%n", job2.getName(), + SharedInfoForTestBug67760.job2Iterations, thread2Info.getLockName(), thread2Info.getLockOwnerName()); + System.out.println("Stacktrace:"); + for (StackTraceElement element : thread2Info.getStackTrace()) { + System.out.println(" " + element); + } + + fail("Possible deadlock detected after " + elapsedTime + + " milliseconds. See the console output for more details. WARNING: this failure may lead to JVM instability."); + } + } + } while (--recheckWaitTimeCountdown > 0); + + elapsedTime = System.currentTimeMillis() - startTime; + System.out.println("The test ended gracefully after " + elapsedTime + " milliseconds."); + + assertTrue("Test expected to run at least for " + (recheckWaitTimeUnit * recheckWaitTimeCountdown) + " milliseconds.", + elapsedTime >= recheckWaitTimeUnit * recheckWaitTimeCountdown); + + testConn.close(); + } + + private static final class SharedInfoForTestBug67760 { + static volatile boolean running = true; + + static volatile int job1Iterations = 0; + static volatile int job2Iterations = 0; + + static int prevJob1Iterations = 0; + static int prevJob2Iterations = 0; + + static boolean iterationsChanged() { + boolean iterationsChanged = prevJob1Iterations != job1Iterations && prevJob2Iterations != job2Iterations; + prevJob1Iterations = job1Iterations; + prevJob2Iterations = job2Iterations; + return iterationsChanged; + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/StringRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/StringRegressionTest.java new file mode 100644 index 0000000..4427fc3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/StringRegressionTest.java @@ -0,0 +1,855 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.Properties; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.util.Base64Decoder; + +import testsuite.BaseTestCase; + +/** + * Tests for regressions of bugs in String handling in the driver. + */ +public class StringRegressionTest extends BaseTestCase { + /** + * Creates a new StringTest object. + * + * @param name + */ + public StringRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StringRegressionTest.class); + } + + /** + * Tests character conversion bug. + * + * @throws Exception + * if there is an internal error (which is a bug). + */ + public void testAsciiCharConversion() throws Exception { + byte[] buf = new byte[10]; + buf[0] = (byte) '?'; + buf[1] = (byte) 'S'; + buf[2] = (byte) 't'; + buf[3] = (byte) 'a'; + buf[4] = (byte) 't'; + buf[5] = (byte) 'e'; + buf[6] = (byte) '-'; + buf[7] = (byte) 'b'; + buf[8] = (byte) 'o'; + buf[9] = (byte) 't'; + + String testString = "?State-bot"; + String convertedString = StringUtils.toAsciiString(buf); + + for (int i = 0; i < convertedString.length(); i++) { + System.out.println((byte) convertedString.charAt(i)); + } + + assertTrue("Converted string != test string", testString.equals(convertedString)); + } + + /** + * Tests fix for BUG#4010 -- GBK encoding getting escaped doubly when + * database default character set is GBK. Requires version older than 4.1.0 + * and server set to default character set of 'gbk' to run. + * + * @throws Exception + * if the test fails. + */ + public void testBug4010() throws Exception { + if (!versionMeetsMinimum(4, 1)) { + if ("GBK".equalsIgnoreCase(getMysqlVariable("character_set"))) { + String origString = "\u603d"; + Properties props = new Properties(); + props.put("useUnicode", "true"); + props.put("characterEncoding", "GBK"); + + Connection unicodeConn = getConnectionWithProps(props); + Statement unicodeStmt = unicodeConn.createStatement(); + PreparedStatement unicodePstmt = unicodeConn.prepareStatement("INSERT INTO testBug4010 VALUES (?)"); + + try { + unicodeStmt.executeUpdate("DROP TABLE IF EXISTS testBug4010"); + unicodeStmt.executeUpdate("CREATE TABLE testBug4010 (field1 varchar(10))"); + + unicodePstmt.setString(1, origString); + unicodePstmt.executeUpdate(); + + this.rs = unicodeStmt.executeQuery("SELECT * FROM testBug4010"); + assertTrue(this.rs.next()); + + String stringFromDb = this.rs.getString(1); + assertTrue("Retrieved string != sent string", origString.equals(stringFromDb)); + } finally { + unicodeStmt.executeUpdate("DROP TABLE IF EXISTS testBug4010"); + unicodeStmt.close(); + unicodePstmt.close(); + unicodeConn.close(); + } + } else { + System.err.println("WARN: Test not valid for servers not running GBK encoding"); + } + } else { + System.err.println("WARN: Test not valid for MySQL version > 4.1.0, skipping"); + } + } + + /** + * Tests for regression of encoding forced by user, reported by Jive + * Software + * + * @throws Exception + * when encoding is not supported (which is a bug) + */ + public void testEncodingRegression() throws Exception { + Properties props = new Properties(); + props.put("characterEncoding", "UTF-8"); + props.put("useUnicode", "true"); + DriverManager.getConnection(dbUrl, props).close(); + } + + /** + * Tests fix for BUG#879 + * + * @throws Exception + * if the bug resurfaces. + */ + public void testEscapeSJISDoubleEscapeBug() throws Exception { + String testString = "'It\\'s a boy!'"; + + byte[] testStringAsBytes = testString.getBytes("SJIS"); + + byte[] escapedStringBytes = StringUtils.escapeEasternUnicodeByteStream(testStringAsBytes, testString); + + String escapedString = new String(escapedStringBytes, "SJIS"); + + assertTrue(testString.equals(escapedString)); + + byte[] origByteStream = new byte[] { (byte) 0x95, (byte) 0x5c, (byte) 0x8e, (byte) 0x96, (byte) 0x5c, (byte) 0x62, (byte) 0x5c }; + + String origString = "\u955c\u8e96\u5c62\\"; + + byte[] newByteStream = StringUtils.escapeEasternUnicodeByteStream(origByteStream, origString); + + assertTrue((newByteStream.length == (origByteStream.length + 2)) && (newByteStream[1] == 0x5c) && (newByteStream[2] == 0x5c) + && (newByteStream[5] == 0x5c) && (newByteStream[6] == 0x5c)); + + origByteStream = new byte[] { (byte) 0x8d, (byte) 0xb2, (byte) 0x93, (byte) 0x91, (byte) 0x81, (byte) 0x40, (byte) 0x8c, (byte) 0x5c }; + + testString = new String(origByteStream, "SJIS"); + + Properties connProps = new Properties(); + connProps.put("useUnicode", "true"); + connProps.put("characterEncoding", "sjis"); + + Connection sjisConn = getConnectionWithProps(connProps); + Statement sjisStmt = sjisConn.createStatement(); + + try { + sjisStmt.executeUpdate("DROP TABLE IF EXISTS doubleEscapeSJISTest"); + sjisStmt.executeUpdate("CREATE TABLE doubleEscapeSJISTest (field1 BLOB)"); + + PreparedStatement sjisPStmt = sjisConn.prepareStatement("INSERT INTO doubleEscapeSJISTest VALUES (?)"); + sjisPStmt.setString(1, testString); + sjisPStmt.executeUpdate(); + + this.rs = sjisStmt.executeQuery("SELECT * FROM doubleEscapeSJISTest"); + + this.rs.next(); + + String retrString = this.rs.getString(1); + + System.out.println(retrString.equals(testString)); + } finally { + sjisStmt.executeUpdate("DROP TABLE IF EXISTS doubleEscapeSJISTest"); + } + } + + public void testGreekUtf8411() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Properties newProps = new Properties(); + newProps.put("useUnicode", "true"); + newProps.put("characterEncoding", "UTF-8"); + + Connection utf8Conn = this.getConnectionWithProps(newProps); + + Statement utfStmt = utf8Conn.createStatement(); + + createTable("greekunicode", "(ID INTEGER NOT NULL AUTO_INCREMENT,UpperCase VARCHAR (30),LowerCase VARCHAR (30),Accented " + + " VARCHAR (30),Special VARCHAR (30),PRIMARY KEY(ID)) DEFAULT CHARACTER SET utf8", "InnoDB"); + + String upper = "\u0394\u930F\u039A\u0399\u039C\u0397"; + String lower = "\u03B4\u03BF\u03BA\u03B9\u03BC\u03B7"; + String accented = "\u03B4\u03CC\u03BA\u03AF\u03BC\u03AE"; + String special = "\u037E\u03C2\u03B0"; + + utfStmt.executeUpdate("INSERT INTO greekunicode VALUES ('1','" + upper + "','" + lower + "','" + accented + "','" + special + "')"); + + this.rs = utfStmt.executeQuery("SELECT UpperCase, LowerCase, Accented, Special from greekunicode"); + + this.rs.next(); + + assertTrue(upper.equals(this.rs.getString(1))); + assertTrue(lower.equals(this.rs.getString(2))); + assertTrue(accented.equals(this.rs.getString(3))); + assertTrue(special.equals(this.rs.getString(4))); + } + } + + /** + * Tests that 'latin1' character conversion works correctly. + * + * @throws Exception + * if any errors occur + */ + public void testLatin1Encoding() throws Exception { + char[] latin1Charset = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, + 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, + 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, + 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, + 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, + 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, + 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, + 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, + 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, + 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, + 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; + + String latin1String = new String(latin1Charset); + Connection latin1Conn = null; + PreparedStatement pStmt = null; + + try { + Properties props = new Properties(); + props.put("characterEncoding", "cp1252"); + latin1Conn = getConnectionWithProps(props); + + createTable("latin1RegressTest", "(stringField TEXT)"); + + pStmt = latin1Conn.prepareStatement("INSERT INTO latin1RegressTest VALUES (?)"); + pStmt.setString(1, latin1String); + pStmt.executeUpdate(); + + ((com.mysql.jdbc.Connection) latin1Conn).setTraceProtocol(true); + + this.rs = this.stmt.executeQuery("SELECT * FROM latin1RegressTest"); + ((com.mysql.jdbc.Connection) latin1Conn).setTraceProtocol(false); + + this.rs.next(); + + String retrievedString = this.rs.getString(1); + + System.out.println(latin1String); + System.out.println(retrievedString); + + if (!retrievedString.equals(latin1String)) { + int stringLength = Math.min(retrievedString.length(), latin1String.length()); + + for (int i = 0; i < stringLength; i++) { + char rChar = retrievedString.charAt(i); + char origChar = latin1String.charAt(i); + + if ((rChar != '?') && (rChar != origChar)) { + fail("characters differ at position " + i + "'" + rChar + "' retrieved from database, original char was '" + origChar + "'"); + } + } + } + } finally { + if (latin1Conn != null) { + latin1Conn.close(); + } + } + } + + /** + * Tests newline being treated correctly. + * + * @throws Exception + * if an error occurs + */ + public void testNewlines() throws Exception { + String newlineStr = "Foo\nBar\n\rBaz"; + + createTable("newlineRegressTest", "(field1 MEDIUMTEXT)"); + + this.stmt.executeUpdate("INSERT INTO newlineRegressTest VALUES ('" + newlineStr + "')"); + this.pstmt = this.conn.prepareStatement("INSERT INTO newlineRegressTest VALUES (?)"); + this.pstmt.setString(1, newlineStr); + this.pstmt.executeUpdate(); + + this.rs = this.stmt.executeQuery("SELECT * FROM newlineRegressTest"); + + while (this.rs.next()) { + assertTrue(this.rs.getString(1).equals(newlineStr)); + } + + } + + /** + * Tests that single-byte character conversion works correctly. + * + * @throws Exception + * if any errors occur + */ + // TODO: Use Unicode Literal escapes for this, for now, this test is + // broken :( + /* + * public void testSingleByteConversion() throws Exception { + * testConversionForString("latin1", "��� ����"); + * testConversionForString("latin1", "Kaarle ��nis Ilmari"); + * testConversionForString("latin1", "������������������"); } + */ + + /** + * Tests that the 0x5c escaping works (we didn't use to have this). + * + * @throws Exception + * if an error occurs. + */ + public void testSjis5c() throws Exception { + byte[] origByteStream = new byte[] { (byte) 0x95, (byte) 0x5c, (byte) 0x8e, (byte) 0x96 }; + + // + // Print the hex values of the string + // + StringBuilder bytesOut = new StringBuilder(); + + for (int i = 0; i < origByteStream.length; i++) { + bytesOut.append(Integer.toHexString(origByteStream[i] & 255)); + bytesOut.append(" "); + } + + System.out.println(bytesOut.toString()); + + String origString = new String(origByteStream, "SJIS"); + byte[] newByteStream = StringUtils.getBytes(origString, "SJIS", "ISO8859_1 ", false, null, null); + + // + // Print the hex values of the string (should have an extra 0x5c) + // + bytesOut = new StringBuilder(); + + for (int i = 0; i < newByteStream.length; i++) { + bytesOut.append(Integer.toHexString(newByteStream[i] & 255)); + bytesOut.append(" "); + } + + System.out.println(bytesOut.toString()); + + // + // Now, insert and retrieve the value from the database + // + Connection sjisConn = null; + Statement sjisStmt = null; + + try { + Properties props = new Properties(); + props.put("useUnicode", "true"); + props.put("characterEncoding", "SJIS"); + sjisConn = getConnectionWithProps(props); + + sjisStmt = sjisConn.createStatement(); + + this.rs = sjisStmt.executeQuery("SHOW VARIABLES LIKE 'character_set%'"); + + while (this.rs.next()) { + System.out.println(this.rs.getString(1) + " = " + this.rs.getString(2)); + } + + sjisStmt.executeUpdate("DROP TABLE IF EXISTS sjisTest"); + + if (versionMeetsMinimum(4, 1)) { + sjisStmt.executeUpdate("CREATE TABLE sjisTest (field1 char(50)) DEFAULT CHARACTER SET SJIS"); + } else { + sjisStmt.executeUpdate("CREATE TABLE sjisTest (field1 char(50))"); + } + + this.pstmt = sjisConn.prepareStatement("INSERT INTO sjisTest VALUES (?)"); + this.pstmt.setString(1, origString); + this.pstmt.executeUpdate(); + + this.rs = sjisStmt.executeQuery("SELECT * FROM sjisTest"); + + while (this.rs.next()) { + byte[] testValueAsBytes = this.rs.getBytes(1); + + bytesOut = new StringBuilder(); + + for (int i = 0; i < testValueAsBytes.length; i++) { + bytesOut.append(Integer.toHexString(testValueAsBytes[i] & 255)); + bytesOut.append(" "); + } + + System.out.println("Value retrieved from database: " + bytesOut.toString()); + + String testValue = this.rs.getString(1); + + assertTrue(testValue.equals(origString)); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS sjisTest"); + } + } + + /** + * Tests that UTF-8 character conversion works correctly. + * + * @throws Exception + * if any errors occur + */ + public void testUtf8Encoding() throws Exception { + Properties props = new Properties(); + props.put("characterEncoding", "UTF8"); + props.put("useUnicode", "true"); + props.put("jdbcCompliantTruncation", "false"); + + Connection utfConn = DriverManager.getConnection(dbUrl, props); + testConversionForString("UTF8", utfConn, "\u043c\u0438\u0445\u0438"); + } + + public void testUtf8Encoding2() throws Exception { + String field1 = "K��sel"; + String field2 = "B�b"; + byte[] field1AsBytes = field1.getBytes("utf-8"); + byte[] field2AsBytes = field2.getBytes("utf-8"); + + Properties props = new Properties(); + props.put("characterEncoding", "UTF8"); + props.put("useUnicode", "true"); + + Connection utfConn = DriverManager.getConnection(dbUrl, props); + Statement utfStmt = utfConn.createStatement(); + + try { + utfStmt.executeUpdate("DROP TABLE IF EXISTS testUtf8"); + utfStmt.executeUpdate("CREATE TABLE testUtf8 (field1 varchar(32), field2 varchar(32)) CHARACTER SET UTF8"); + utfStmt.executeUpdate("INSERT INTO testUtf8 VALUES ('" + field1 + "','" + field2 + "')"); + + PreparedStatement pStmt = utfConn.prepareStatement("INSERT INTO testUtf8 VALUES (?, ?)"); + pStmt.setString(1, field1); + pStmt.setString(2, field2); + pStmt.executeUpdate(); + + this.rs = utfStmt.executeQuery("SELECT * FROM testUtf8"); + assertTrue(this.rs.next()); + + // Compare results stored using direct statement + // Compare to original string + assertTrue(field1.equals(this.rs.getString(1))); + assertTrue(field2.equals(this.rs.getString(2))); + + // Compare byte-for-byte, ignoring encoding + assertTrue(bytesAreSame(field1AsBytes, this.rs.getBytes(1))); + assertTrue(bytesAreSame(field2AsBytes, this.rs.getBytes(2))); + + assertTrue(this.rs.next()); + + // Compare to original string + assertTrue(field1.equals(this.rs.getString(1))); + assertTrue(field2.equals(this.rs.getString(2))); + + // Compare byte-for-byte, ignoring encoding + assertTrue(bytesAreSame(field1AsBytes, this.rs.getBytes(1))); + assertTrue(bytesAreSame(field2AsBytes, this.rs.getBytes(2))); + } finally { + utfStmt.executeUpdate("DROP TABLE IF EXISTS testUtf8"); + } + } + + private boolean bytesAreSame(byte[] byte1, byte[] byte2) { + if (byte1.length != byte2.length) { + return false; + } + + for (int i = 0; i < byte1.length; i++) { + if (byte1[i] != byte2[i]) { + return false; + } + } + + return true; + } + + private void testConversionForString(String charsetName, Connection convConn, String charsToTest) throws Exception { + PreparedStatement pStmt = null; + + this.stmt = convConn.createStatement(); + + if (!versionMeetsMinimum(4, 1)) { + createTable("CREATE TABLE charConvTest_" + charsetName, "(field1 CHAR(50))"); + } else { + createTable("charConvTest_" + charsetName, "(field1 CHAR(50) CHARACTER SET " + charsetName + ")"); + } + + this.stmt.executeUpdate("INSERT INTO charConvTest_" + charsetName + " VALUES ('" + charsToTest + "')"); + pStmt = convConn.prepareStatement("INSERT INTO charConvTest_" + charsetName + " VALUES (?)"); + pStmt.setString(1, charsToTest); + pStmt.executeUpdate(); + this.rs = this.stmt.executeQuery("SELECT * FROM charConvTest_" + charsetName); + + assertTrue(this.rs.next()); + + String testValue = this.rs.getString(1); + System.out.println(testValue); + assertTrue(testValue.equals(charsToTest)); + + } + + /** + * Tests fix for BUG#7601, '+' duplicated in fixDecimalExponent(). + * + * @throws Exception + * if the test fails + */ + public void testBug7601() throws Exception { + assertTrue("1.5E+7".equals(StringUtils.fixDecimalExponent("1.5E+7"))); + assertTrue("1.5E-7".equals(StringUtils.fixDecimalExponent("1.5E-7"))); + assertTrue("1.5E+7".equals(StringUtils.fixDecimalExponent("1.5E7"))); + } + + public void testBug11629() throws Exception { + class TeeByteArrayOutputStream extends ByteArrayOutputStream { + PrintStream branch; + StackTraceElement[] callStackTrace = null; + + public TeeByteArrayOutputStream(PrintStream branch) { + this.branch = branch; + } + + @Override + public void write(int b) { + this.branch.write(b); + super.write(b); + setCallStackTrace(); + } + + @Override + public void write(byte[] b) throws IOException { + this.branch.write(b); + super.write(b); + setCallStackTrace(); + } + + @Override + public void write(byte[] b, int off, int len) { + this.branch.write(b, off, len); + super.write(b, off, len); + setCallStackTrace(); + } + + private void setCallStackTrace() { + if (this.callStackTrace == null) { + this.callStackTrace = Thread.currentThread().getStackTrace(); + } + } + + public void printCallStackTrace() { + if (this.callStackTrace != null) { + for (StackTraceElement ste : this.callStackTrace) { + this.branch.println(">>> " + ste.toString()); + } + } + } + } + + PrintStream oldOut = System.out; + PrintStream oldError = System.err; + + try { + TeeByteArrayOutputStream bOut = new TeeByteArrayOutputStream(System.out); + PrintStream newOut = new PrintStream(bOut); + System.out.flush(); + System.setOut(newOut); + + TeeByteArrayOutputStream bErr = new TeeByteArrayOutputStream(System.err); + PrintStream newErr = new PrintStream(bErr); + System.err.flush(); + System.setErr(newErr); + + Properties props = new Properties(); + props.setProperty("useSSL", "false"); + props.setProperty("characterEncoding", "utf8"); + getConnectionWithProps(props).close(); + System.setOut(oldOut); + System.setErr(oldError); + + bOut.printCallStackTrace(); + bErr.printCallStackTrace(); + + String withExclaims = new String(bOut.toByteArray()); + assertTrue("Unexpected: '" + withExclaims + "'", withExclaims.indexOf("!") == -1); + assertTrue("Unexpected: '" + withExclaims + "'", withExclaims.length() == 0); // to catch any other + bOut.close(); + + withExclaims = new String(bErr.toByteArray()); + assertTrue("Unexpected: '" + withExclaims + "'", withExclaims.indexOf("!") == -1); + assertTrue("Unexpected: '" + withExclaims + "'", withExclaims.length() == 0); // to catch any other + bErr.close(); + } finally { + System.setOut(oldOut); + System.setErr(oldError); + } + } + + /** + * Tests fix for BUG#11614 - StringUtils.getBytes() doesn't work when using + * multibyte character encodings and a length in _characters_ is specified. + * + * @throws Exception + * if the test fails. + */ + public void testBug11614() throws Exception { + if (versionMeetsMinimum(4, 1)) { + createTable("testBug11614", + "(`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `text` TEXT NOT NULL," + "PRIMARY KEY(`id`)) CHARACTER SET utf8 COLLATE utf8_general_ci"); + + Properties props = new Properties(); + props.setProperty("characterEncoding", "utf8"); + + Connection utf8Conn = null; + + try { + utf8Conn = getConnectionWithProps(props); + + utf8Conn.createStatement().executeUpdate("INSERT INTO testBug11614 (`id`,`text`) values (1,'')"); + this.rs = utf8Conn.createStatement().executeQuery("SELECT `text` FROM testBug11614 WHERE id=1"); + assertTrue(this.rs.next()); + + Clob c = this.rs.getClob(1); + c.truncate(0); + int blockSize = 8192; + int sizeToTest = blockSize + 100; + + StringBuilder blockBuf = new StringBuilder(sizeToTest); + + for (int i = 0; i < sizeToTest; i++) { + blockBuf.append('\u00f6'); + } + + String valueToTest = blockBuf.toString(); + + c.setString(1, valueToTest); + this.pstmt = utf8Conn.prepareStatement("UPDATE testBug11614 SET `text` = ? WHERE id=1"); + this.pstmt.setClob(1, c); + this.pstmt.executeUpdate(); + this.pstmt.close(); + + String fromDatabase = getSingleIndexedValueWithQuery(utf8Conn, 1, "SELECT `text` FROM testBug11614").toString(); + assertEquals(valueToTest, fromDatabase); + } finally { + if (utf8Conn != null) { + utf8Conn.close(); + } + + } + } + } + + public void testCodePage1252() throws Exception { + if (versionMeetsMinimum(4, 1, 0)) { + /* + * from + * ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/ + * CP1252.TXT + * + * 0x80 0x20AC #EURO SIGN 0x81 #UNDEFINED 0x82 0x201A #SINGLE LOW-9 + * QUOTATION MARK 0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK 0x84 + * 0x201E #DOUBLE LOW-9 QUOTATION MARK 0x85 0x2026 #HORIZONTAL + * ELLIPSIS 0x86 0x2020 #DAGGER 0x87 0x2021 #DOUBLE DAGGER 0x88 + * 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT 0x89 0x2030 #PER MILLE + * SIGN 0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON 0x8B 0x2039 + * #SINGLE LEFT-POINTING ANGLE QUOTATION MARK 0x8C 0x0152 #LATIN + * CAPITAL LIGATURE OE 0x8D #UNDEFINED 0x8E 0x017D #LATIN CAPITAL + * LETTER Z WITH CARON 0x8F #UNDEFINED 0x90 #UNDEFINED + */ + String codePage1252 = new String(new byte[] { (byte) 0x80, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, + (byte) 0x88, (byte) 0x89, (byte) 0x8a, (byte) 0x8b, (byte) 0x8c, (byte) 0x8e }, "Cp1252"); + + System.out.println(codePage1252); + + Properties props = new Properties(); + props.setProperty("characterEncoding", "Cp1252"); + Connection cp1252Conn = getConnectionWithProps(props); + createTable("testCp1252", "(field1 varchar(32) CHARACTER SET latin1)"); + cp1252Conn.createStatement().executeUpdate("INSERT INTO testCp1252 VALUES ('" + codePage1252 + "')"); + this.rs = cp1252Conn.createStatement().executeQuery("SELECT field1 FROM testCp1252"); + this.rs.next(); + assertEquals(this.rs.getString(1), codePage1252); + } + } + + /** + * Tests fix for BUG#23645 - Some collations/character sets reported as + * "unknown" (specifically cias variants of existing character sets), and + * inability to override the detected server character set. + * + * @throws Exception + * if the test fails. + */ + public void testBug23645() throws Exception { + if (versionMeetsMinimum(4, 1)) { + // Part of this isn't easily testable, hence the assertion in + // CharsetMapping + // that checks for mappings existing in both directions... + + // What we test here is the ability to override the character + // mapping + // when the server returns an "unknown" character encoding. + + String currentlyConfiguredCharacterSet = getSingleIndexedValueWithQuery(2, "SHOW VARIABLES LIKE 'character_set_connection'").toString(); + System.out.println(currentlyConfiguredCharacterSet); + + String javaNameForMysqlName = CharsetMapping.getJavaEncodingForMysqlCharset(currentlyConfiguredCharacterSet); + System.out.println(javaNameForMysqlName); + + for (int i = 1; i < CharsetMapping.MAP_SIZE; i++) { + String possibleCharset = CharsetMapping.getJavaEncodingForCollationIndex(i); + + if (!javaNameForMysqlName.equals(possibleCharset)) { + System.out.println(possibleCharset); + + Properties props = new Properties(); + props.setProperty("characterEncoding", possibleCharset); + props.setProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex", "65535"); + + Connection forcedCharConn = null; + + forcedCharConn = getConnectionWithProps(props); + + String forcedCharset = getSingleIndexedValueWithQuery(forcedCharConn, 2, "SHOW VARIABLES LIKE 'character_set_connection'").toString(); + + System.out.println(forcedCharset); + + break; + } + } + + } + } + + /** + * Tests fix for BUG#24840 - character encoding of "US-ASCII" doesn't map + * correctly for 4.1 or newer + * + * @throws Exception + * if the test fails. + */ + public void testBug24840() throws Exception { + Properties props = new Properties(); + props.setProperty("characterEncoding", "US-ASCII"); + + getConnectionWithProps(props).close(); + } + + /** + * Tests fix for BUG#25047 - StringUtils.indexOfIgnoreCaseRespectQuotes() isn't case-insensitive on the first character of the target. + * + * UPD: Method StringUtils.indexOfIgnoreCaseRespectQuotes() was replaced by StringUtils.indexOfIgnoreCase() + * + * @throws Exception + * if the test fails. + */ + public void testBug25047() throws Exception { + assertEquals(26, StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) values (?)", "VALUES", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS)); + assertEquals(26, StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) VALUES (?)", "values", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS)); + + assertEquals(StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) values (?)", "VALUES", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS), + StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) VALUES (?)", "VALUES", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS)); + assertEquals(StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) values (?)", "values", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS), + StringUtils.indexOfIgnoreCase(0, "insert into Test (TestID) VALUES (?)", "values", "`", "`", StringUtils.SEARCH_MODE__MRK_COM_WS)); + } + + /** + * Tests fix for BUG#64731 - StringUtils.getBytesWrapped throws StringIndexOutOfBoundsException. + * + * @throws Exception + * if the test fails. + */ + public void testBug64731() throws Exception { + byte[] data = StringUtils.getBytesWrapped("0f0f0702", '\'', '\'', null, "gbk", "latin1", false, null); + assertTrue(StringUtils.toString(data), true); + } + + public void testBase64Decoder() throws Exception { + testBase64DecoderItem("TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0\n" + + "aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1\n" + + "c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0\n" + + "aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl\n" + + "LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", + + "Man is distinguished, not only by his reason, but by this singular passion" + + " from other animals, which is a lust of the mind, that by a perseverance of" + + " delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."); + + testBase64DecoderItem("TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0\n" + + "aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1\n" + + "c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0\n" + + "aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl\n" + + "LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZQ==", + + "Man is distinguished, not only by his reason, but by this singular passion" + + " from other animals, which is a lust of the mind, that by a perseverance of" + + " delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure"); + + testBase64DecoderItem("TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0\n" + + "aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1\n" + + "c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0\n" + + "aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl\n" + + "LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3Vy", + + "Man is distinguished, not only by his reason, but by this singular passion" + + " from other animals, which is a lust of the mind, that by a perseverance of" + + " delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasur"); + } + + private void testBase64DecoderItem(String source, String expected) throws Exception { + assertEquals(expected, new String(Base64Decoder.decode(source.getBytes(), 0, source.length()))); + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/SubqueriesRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/SubqueriesRegressionTest.java new file mode 100644 index 0000000..f608ec6 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/SubqueriesRegressionTest.java @@ -0,0 +1,175 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import testsuite.BaseTestCase; + +/** + * Tests SubQueries on MySQL > 4.1 + */ +public class SubqueriesRegressionTest extends BaseTestCase { + private final static int REPETITIONS = 100; + + /** + */ + public SubqueriesRegressionTest(String name) { + super(name); + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#setUp() + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + createTables(); + } + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#tearDown() + */ + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(SubqueriesRegressionTest.class); + } + + public void testSubQuery1() throws Exception { + if (versionMeetsMinimum(4, 1)) { + for (int i = 0; i < REPETITIONS; i++) { + + this.rs = this.stmt.executeQuery( + "select t3.colA from t3, t1 where t3.colA = 'bbbb' and t3.colB = t1.colA and exists (select 'X' from t2 where t2.colB = t1.colB)"); + assertTrue(this.rs.next()); + assertTrue("bbbb".equals(this.rs.getString(1))); + assertTrue(!this.rs.next()); + } + } + } + + public void testSubQuery2() throws Exception { + if (versionMeetsMinimum(4, 1)) { + for (int i = 0; i < REPETITIONS; i++) { + + this.rs = this.stmt.executeQuery( + "select t3.colA from t3, t1 where t3.colA = 'bbbb' and t3.colB = t1.colA and exists (select 'X' from t2 where t2.colB = 2)"); + assertTrue(this.rs.next()); + assertTrue("bbbb".equals(this.rs.getString(1))); + assertTrue(!this.rs.next()); + + } + } + } + + public void testSubQuery3() throws Exception { + if (versionMeetsMinimum(4, 1)) { + for (int i = 0; i < REPETITIONS; i++) { + + this.rs = this.stmt.executeQuery("select * from t1 where t1.colA = 'efgh' and exists (select 'X' from t2 where t2.colB = t1.colB)"); + assertTrue(this.rs.next()); + assertTrue("efgh".equals(this.rs.getString(1))); + assertTrue("2".equals(this.rs.getString(2))); + assertTrue(!this.rs.next()); + + } + } + } + + public void testSubQuery4() throws Exception { + // not really a subquery, but we want to have this in our testsuite + if (versionMeetsMinimum(4, 1)) { + for (int i = 0; i < REPETITIONS; i++) { + this.rs = this.stmt.executeQuery("select colA, '' from t2 union select colA, colB from t3"); + + assertTrue(this.rs.next()); + assertTrue("type1".equals(this.rs.getString(1))); + assertTrue("".equals(this.rs.getString(2))); + + assertTrue(this.rs.next()); + assertTrue("type2".equals(this.rs.getString(1))); + assertTrue("".equals(this.rs.getString(2))); + + assertTrue(this.rs.next()); + assertTrue("type3".equals(this.rs.getString(1))); + assertTrue("".equals(this.rs.getString(2))); + + assertTrue(this.rs.next()); + assertTrue("aaaa".equals(this.rs.getString(1))); + assertTrue("'" + this.rs.getString(2) + "' != expected of 'abcd'", "abcd".equals(this.rs.getString(2))); + + assertTrue(this.rs.next()); + assertTrue("bbbb".equals(this.rs.getString(1))); + assertTrue("efgh".equals(this.rs.getString(2))); + + assertTrue(this.rs.next()); + assertTrue("cccc".equals(this.rs.getString(1))); + assertTrue("'" + this.rs.getString(2) + "' != expected of 'ijkl'", "ijkl".equals(this.rs.getString(2))); + + assertTrue(!this.rs.next()); + } + } + } + + public void testSubQuery5() throws Exception { + if (versionMeetsMinimum(4, 1)) { + for (int i = 0; i < REPETITIONS; i++) { + + this.rs = this.stmt.executeQuery("select t1.colA from t1, t4 where t4.colA = t1.colA and exists (select 'X' from t2 where t2.colA = t4.colB)"); + assertTrue(this.rs.next()); + assertTrue("abcd".equals(this.rs.getString(1))); + assertTrue(this.rs.next()); + assertTrue("efgh".equals(this.rs.getString(1))); + assertTrue(this.rs.next()); + assertTrue("ijkl".equals(this.rs.getString(1))); + assertTrue(!this.rs.next()); + + } + } + } + + private void createTables() throws Exception { + createTable("t1", "(colA varchar(10), colB decimal(3,0))"); + createTable("t2", "(colA varchar(10), colB varchar(10))"); + createTable("t3", "(colA varchar(10), colB varchar(10))"); + createTable("t4", "(colA varchar(10), colB varchar(10))"); + this.stmt.executeUpdate("insert into t1 values ('abcd', 1), ('efgh', 2), ('ijkl', 3)"); + this.stmt.executeUpdate("insert into t2 values ('type1', '1'), ('type2', '2'), ('type3', '3')"); + this.stmt.executeUpdate("insert into t3 values ('aaaa', 'abcd'), ('bbbb', 'efgh'), ('cccc', 'ijkl')"); + this.stmt.executeUpdate("insert into t4 values ('abcd', 'type1'), ('efgh', 'type2'), ('ijkl', 'type3')"); + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/SyntaxRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/SyntaxRegressionTest.java new file mode 100644 index 0000000..0416afb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/SyntaxRegressionTest.java @@ -0,0 +1,1221 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.Properties; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; + +import testsuite.BaseTestCase; + +/** + * Regression tests for syntax + */ +public class SyntaxRegressionTest extends BaseTestCase { + + public SyntaxRegressionTest(String name) { + super(name); + } + + /** + * ALTER TABLE syntax changed in 5.6GA + * + * ALTER TABLE ... , algorithm, concurrency + * + * algorithm: + * | ALGORITHM [=] DEFAULT + * | ALGORITHM [=] INPLACE + * | ALGORITHM [=] COPY + * + * concurrency: + * | LOCK [=] DEFAULT + * | LOCK [=] NONE + * | LOCK [=] SHARED + * | LOCK [=] EXCLUSIVE + * + * @throws SQLException + */ + public void testAlterTableAlgorithmLock() throws SQLException { + if (versionMeetsMinimum(5, 6, 6)) { + + Connection c = null; + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + + try { + c = getConnectionWithProps(props); + + String[] algs = { "", ", ALGORITHM DEFAULT", ", ALGORITHM = DEFAULT", ", ALGORITHM INPLACE", ", ALGORITHM = INPLACE", ", ALGORITHM COPY", + ", ALGORITHM = COPY" }; + + String[] lcks = { "", ", LOCK DEFAULT", ", LOCK = DEFAULT", ", LOCK NONE", ", LOCK = NONE", ", LOCK SHARED", ", LOCK = SHARED", + ", LOCK EXCLUSIVE", ", LOCK = EXCLUSIVE" }; + + createTable("testAlterTableAlgorithmLock", "(x VARCHAR(10) NOT NULL DEFAULT '') CHARSET=latin2"); + + int i = 1; + for (String alg : algs) { + for (String lck : lcks) { + i = i ^ 1; + + // TODO: 5.6.10 reports: "LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED." + // We should check if situation change in future + if (!(lck.contains("NONE") && alg.contains("COPY"))) { + + String sql = "ALTER TABLE testAlterTableAlgorithmLock CHARSET=latin" + (i + 1) + alg + lck; + this.stmt.executeUpdate(sql); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testAlterTableAlgorithmLock CHARSET=?" + alg + lck); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = c.prepareStatement(sql); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + } + } + } + + } finally { + if (c != null) { + c.close(); + } + } + } + } + + /** + * CREATE TABLE syntax changed in 5.6GA + * + * InnoDB: Allow the location of file-per-table tablespaces to be chosen + * CREATE TABLE ... DATA DIRECTORY = 'absolute/path/to/directory/' + * + * Notes: + * - DATA DIRECTORY option can't be used with temporary tables. + * - DATA DIRECTORY and INDEX DIRECTORY can't be used together for InnoDB. + * - Using these options result in an 'option ignored' warning for servers below MySQL 5.7.7. This syntax isn't allowed for MySQL 5.7.7 and higher. + * + * @throws SQLException + */ + public void testCreateTableDataDirectory() throws SQLException { + + if (versionMeetsMinimum(5, 6, 6)) { + try { + String tmpdir = null; + String separator = File.separatorChar == '\\' ? File.separator + File.separator : File.separator; + this.rs = this.stmt.executeQuery("SHOW VARIABLES WHERE Variable_name='tmpdir' or Variable_name='innodb_file_per_table'"); + while (this.rs.next()) { + if ("tmpdir".equals(this.rs.getString(1))) { + tmpdir = this.rs.getString(2); + if (tmpdir.endsWith(File.separator)) { + tmpdir = tmpdir.substring(0, tmpdir.length() - 1); + } + if (File.separatorChar == '\\') { + tmpdir = StringUtils.escapeQuote(tmpdir, File.separator); + } + } else if ("innodb_file_per_table".equals(this.rs.getString(1))) { + if (!this.rs.getString(2).equals("ON")) { + fail("You need to set innodb_file_per_table to ON before running this test!"); + } + } + } + + dropTable("testCreateTableDataDirectorya"); + dropTable("testCreateTableDataDirectoryb"); + dropTable("testCreateTableDataDirectoryc"); + dropTable("testCreateTableDataDirectoryd"); + + createTable("testCreateTableDataDirectorya", "(x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + "'"); + createTable("testCreateTableDataDirectoryb", "(x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + separator + "'"); + this.stmt.executeUpdate("CREATE TEMPORARY TABLE testCreateTableDataDirectoryc (x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + + (versionMeetsMinimum(5, 7, 7) ? "' ENGINE = MyISAM" : "'")); + createTable("testCreateTableDataDirectoryd", "(x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + separator + + "' INDEX DIRECTORY = '" + tmpdir + (versionMeetsMinimum(5, 7, 7) ? "' ENGINE = MyISAM" : "'")); + + this.stmt.executeUpdate("ALTER TABLE testCreateTableDataDirectorya DISCARD TABLESPACE"); + + this.pstmt = this.conn + .prepareStatement("CREATE TABLE testCreateTableDataDirectorya (x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + "'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement( + "CREATE TABLE testCreateTableDataDirectorya (x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + separator + "'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement( + "CREATE TEMPORARY TABLE testCreateTableDataDirectorya (x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + "'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("CREATE TABLE testCreateTableDataDirectorya (x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + + tmpdir + "' INDEX DIRECTORY = '" + tmpdir + "'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testCreateTableDataDirectorya DISCARD TABLESPACE"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + } finally { + // we need to drop them even if retainArtifacts=true, otherwise temp files could be deleted by OS and DB became corrupted + dropTable("testCreateTableDataDirectorya"); + dropTable("testCreateTableDataDirectoryb"); + dropTable("testCreateTableDataDirectoryc"); + dropTable("testCreateTableDataDirectoryd"); + } + + } + } + + /** + * Test case for transportable tablespaces syntax support: + * + * FLUSH TABLES ... FOR EXPORT + * ALTER TABLE ... DISCARD TABLESPACE + * ALTER TABLE ... IMPORT TABLESPACE + * + * @throws SQLException + */ + public void testTransportableTablespaces() throws Exception { + + if (versionMeetsMinimum(5, 6, 8)) { + String tmpdir = null; + String uuid = null; + this.rs = this.stmt + .executeQuery("SHOW VARIABLES WHERE Variable_name='tmpdir' or Variable_name='innodb_file_per_table' or Variable_name='server_uuid'"); + while (this.rs.next()) { + if ("tmpdir".equals(this.rs.getString(1))) { + tmpdir = this.rs.getString(2); + if (tmpdir.endsWith(File.separator)) { + tmpdir = tmpdir.substring(0, tmpdir.length() - File.separator.length()); + } + } else if ("innodb_file_per_table".equals(this.rs.getString(1))) { + if (!this.rs.getString(2).equals("ON")) { + fail("You need to set innodb_file_per_table to ON before running this test!"); + } + } else if ("server_uuid".equals(this.rs.getString(1))) { + uuid = this.rs.getString(2); + } + } + + if (uuid != null) { + tmpdir = tmpdir + File.separator + uuid; + } + + if (File.separatorChar == '\\') { + tmpdir = StringUtils.escapeQuote(tmpdir, File.separator); + } + + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + if (dbname == null) { + assertTrue("No database selected", false); + } + + dropTable("testTransportableTablespaces1"); + dropTable("testTransportableTablespaces2"); + + File checkTableSpaceFile1 = new File(tmpdir + File.separator + dbname + File.separator + "testTransportableTablespaces1.ibd"); + if (checkTableSpaceFile1.exists()) { + checkTableSpaceFile1.delete(); + } + + File checkTableSpaceFile2 = new File(tmpdir + File.separator + dbname + File.separator + "testTransportableTablespaces2.ibd"); + if (checkTableSpaceFile2.exists()) { + checkTableSpaceFile2.delete(); + } + + try { + createTable("testTransportableTablespaces1", "(x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + "'"); + createTable("testTransportableTablespaces2", "(x VARCHAR(10) NOT NULL DEFAULT '') DATA DIRECTORY = '" + tmpdir + "'"); + this.stmt.executeUpdate("FLUSH TABLES testTransportableTablespaces1, testTransportableTablespaces2 FOR EXPORT"); + this.stmt.executeUpdate("UNLOCK TABLES"); + + File tempFile = File.createTempFile("testTransportableTablespaces1", "tmp"); + tempFile.deleteOnExit(); + + String tableSpacePath = tmpdir + File.separator + dbname + File.separator + "testTransportableTablespaces1.ibd"; + File tableSpaceFile = new File(tableSpacePath); + + copyFile(tableSpaceFile, tempFile); + this.stmt.executeUpdate("ALTER TABLE testTransportableTablespaces1 DISCARD TABLESPACE"); + + tableSpaceFile = new File(tableSpacePath); + copyFile(tempFile, tableSpaceFile); + + this.stmt.executeUpdate("ALTER TABLE testTransportableTablespaces1 IMPORT TABLESPACE"); + + this.pstmt = this.conn.prepareStatement("FLUSH TABLES testTransportableTablespaces1, testTransportableTablespaces2 FOR EXPORT"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testTransportableTablespaces1 DISCARD TABLESPACE"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testTransportableTablespaces1 IMPORT TABLESPACE"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + } finally { + // we need to drop them even if retainArtifacts=true, otherwise temp files could be deleted by OS and DB became corrupted + dropTable("testTransportableTablespaces1"); + dropTable("testTransportableTablespaces2"); + } + } + } + + private void copyFile(File source, File dest) throws IOException { + FileInputStream is = null; + FileOutputStream os = null; + try { + is = new FileInputStream(source); + os = new FileOutputStream(dest); + int nLength; + byte[] buf = new byte[8000]; + while (true) { + nLength = is.read(buf); + if (nLength < 0) { + break; + } + os.write(buf, 0, nLength); + } + + } finally { + if (is != null) { + try { + is.close(); + } catch (Exception ex) { + } + } + if (os != null) { + try { + os.close(); + } catch (Exception ex) { + } + } + } + } + + /** + * Test case for ALTER [IGNORE] TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t2 syntax + * + * @throws SQLException + */ + public void testExchangePartition() throws Exception { + if (versionMeetsMinimum(5, 6, 6)) { + createTable("testExchangePartition1", "(id int(11) NOT NULL AUTO_INCREMENT, year year(4) DEFAULT NULL," + + " modified timestamp NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB ROW_FORMAT=COMPACT PARTITION BY HASH (id) PARTITIONS 2"); + createTable("testExchangePartition2", "LIKE testExchangePartition1"); + + this.stmt.executeUpdate("ALTER TABLE testExchangePartition2 REMOVE PARTITIONING"); + if (versionMeetsMinimum(5, 7, 4)) { + this.stmt.executeUpdate("ALTER TABLE testExchangePartition1 EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + } else { + this.stmt.executeUpdate("ALTER IGNORE TABLE testExchangePartition1 EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + } + + if (versionMeetsMinimum(5, 7, 4)) { + this.pstmt = this.conn.prepareStatement("ALTER TABLE testExchangePartition1 EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + } else { + this.pstmt = this.conn + .prepareStatement("ALTER IGNORE TABLE testExchangePartition1 " + "EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + } + assertEquals(Util.isJdbc4() ? Class.forName(Util.isJdbc42() ? "com.mysql.jdbc.JDBC42PreparedStatement" : "com.mysql.jdbc.JDBC4PreparedStatement") + : com.mysql.jdbc.PreparedStatement.class, this.pstmt.getClass()); + this.pstmt.executeUpdate(); + + Connection testConn = null; + try { + testConn = getConnectionWithProps("useServerPrepStmts=true,emulateUnsupportedPstmts=false"); + if (versionMeetsMinimum(5, 7, 4)) { + this.pstmt = testConn.prepareStatement("ALTER TABLE testExchangePartition1 EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + } else { + this.pstmt = testConn + .prepareStatement("ALTER IGNORE TABLE testExchangePartition1 " + "EXCHANGE PARTITION p1 WITH TABLE testExchangePartition2"); + + } + assertEquals(Util.isJdbc4() + ? Class.forName(Util.isJdbc42() ? "com.mysql.jdbc.JDBC42ServerPreparedStatement" : "com.mysql.jdbc.JDBC4ServerPreparedStatement") + : com.mysql.jdbc.ServerPreparedStatement.class, this.pstmt.getClass()); + this.pstmt.executeUpdate(); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Test for explicit partition selection syntax + * + * @throws SQLException + */ + public void testExplicitPartitions() throws Exception { + + if (versionMeetsMinimum(5, 6, 5)) { + Connection c = null; + String datadir = null; + Properties props = new NonRegisteringDriver().parseURL(dbUrl, null); + String dbname = props.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + try { + + this.stmt.executeUpdate("SET @old_default_storage_engine = @@default_storage_engine"); + this.stmt.executeUpdate("SET @@default_storage_engine = 'InnoDB'"); + + c = getConnectionWithProps(props); + + createTable("testExplicitPartitions", + "(a INT NOT NULL, b varchar (64), INDEX (b,a), PRIMARY KEY (a)) ENGINE = InnoDB" + + " PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) SUBPARTITIONS 2" + + " (PARTITION pNeg VALUES LESS THAN (0) (SUBPARTITION subp0, SUBPARTITION subp1)," + + " PARTITION `p0-9` VALUES LESS THAN (10) (SUBPARTITION subp2, SUBPARTITION subp3)," + + " PARTITION `p10-99` VALUES LESS THAN (100) (SUBPARTITION subp4, SUBPARTITION subp5)," + + " PARTITION `p100-99999` VALUES LESS THAN (100000) (SUBPARTITION subp6, SUBPARTITION subp7))"); + + this.stmt.executeUpdate("INSERT INTO testExplicitPartitions PARTITION (pNeg, pNeg) VALUES (-1, \"pNeg(-subp1)\")"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testExplicitPartitions PARTITION (pNeg, subp0) VALUES (-3, \"pNeg(-subp1)\")"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + + this.pstmt = c.prepareStatement("INSERT INTO testExplicitPartitions PARTITION (pNeg, subp0) VALUES (-2, \"(pNeg-)subp0\")"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.pstmt = c.prepareStatement( + "INSERT INTO testExplicitPartitions PARTITION (`p100-99999`) VALUES (100, \"`p100-99999`(-subp6)\"), (101, \"`p100-99999`(-subp7)\"), (1000, \"`p100-99999`(-subp6)\")"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.stmt.executeUpdate("INSERT INTO testExplicitPartitions PARTITION(`p10-99`,subp3) VALUES (1, \"subp3\"), (10, \"p10-99\")"); + this.stmt.executeUpdate("INSERT INTO testExplicitPartitions PARTITION(subp3) VALUES (3, \"subp3\")"); + this.stmt.executeUpdate("INSERT INTO testExplicitPartitions PARTITION(`p0-9`) VALUES (5, \"p0-9:subp3\")"); + + this.stmt.executeUpdate("FLUSH STATUS"); + this.stmt.execute("SELECT * FROM testExplicitPartitions PARTITION (subp2)"); + + this.pstmt = this.conn.prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (subp2,pNeg) AS TableAlias"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (subp2,pNeg) AS TableAlias"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + + this.pstmt = this.conn.prepareStatement("LOCK TABLE testExplicitPartitions READ, testExplicitPartitions as TableAlias READ"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement("LOCK TABLE testExplicitPartitions READ, testExplicitPartitions as TableAlias READ"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + + this.pstmt = this.conn.prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (subp3) AS TableAlias"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + this.pstmt = c.prepareStatement("SELECT COUNT(*) FROM testExplicitPartitions PARTITION (`p10-99`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.pstmt = this.conn.prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (pNeg) WHERE a = 100"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + this.pstmt = c.prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (pNeg) WHERE a = 100"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.stmt.executeUpdate("UNLOCK TABLES"); + + // Test LOAD + this.rs = this.stmt.executeQuery("SHOW VARIABLES WHERE Variable_name='datadir'"); + this.rs.next(); + datadir = this.rs.getString(2); + + if (dbname == null) { + fail("No database selected"); + } else { + File f = new File(datadir + dbname + File.separator + "loadtestExplicitPartitions.txt"); + if (f.exists()) { + f.delete(); + } + } + + this.pstmt = this.conn + .prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (pNeg, `p10-99`) INTO OUTFILE 'loadtestExplicitPartitions.txt'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c + .prepareStatement("SELECT * FROM testExplicitPartitions PARTITION (pNeg, `p10-99`) INTO OUTFILE 'loadtestExplicitPartitions.txt'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.execute("SELECT * FROM testExplicitPartitions PARTITION (pNeg, `p10-99`) INTO OUTFILE 'loadtestExplicitPartitions.txt'"); + + this.pstmt = this.conn.prepareStatement("ALTER TABLE testExplicitPartitions TRUNCATE PARTITION pNeg, `p10-99`"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement("ALTER TABLE testExplicitPartitions TRUNCATE PARTITION pNeg, `p10-99`"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.executeUpdate("ALTER TABLE testExplicitPartitions TRUNCATE PARTITION pNeg, `p10-99`"); + this.stmt.executeUpdate("FLUSH STATUS"); + + this.pstmt = this.conn + .prepareStatement("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, subp4, subp5)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c + .prepareStatement("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, subp4, subp5)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.stmt.executeUpdate("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, subp4, subp5)"); + + this.stmt.executeUpdate("ALTER TABLE testExplicitPartitions TRUNCATE PARTITION pNeg, `p10-99`"); + this.stmt.executeUpdate("FLUSH STATUS"); + this.pstmt = this.conn + .prepareStatement("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, `p10-99`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c + .prepareStatement("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, `p10-99`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.stmt.executeUpdate("LOCK TABLE testExplicitPartitions WRITE"); + this.stmt.executeUpdate("LOAD DATA INFILE 'loadtestExplicitPartitions.txt' INTO TABLE testExplicitPartitions PARTITION (pNeg, `p10-99`)"); + this.stmt.executeUpdate("UNLOCK TABLES"); + + // Test UPDATE + this.stmt.executeUpdate("UPDATE testExplicitPartitions PARTITION(subp0) SET b = concat(b, ', Updated')"); + + this.pstmt = this.conn.prepareStatement("UPDATE testExplicitPartitions PARTITION(subp0) SET b = concat(b, ', Updated2') WHERE a = -2"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + + this.pstmt = c + .prepareStatement("UPDATE testExplicitPartitions PARTITION(subp0) SET a = -4, b = concat(b, ', Updated from a = -2') WHERE a = -2"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.stmt.executeUpdate("UPDATE testExplicitPartitions PARTITION(subp0) SET b = concat(b, ', Updated2') WHERE a = 100"); + this.stmt.executeUpdate("UPDATE testExplicitPartitions PARTITION(subp0) SET a = -2, b = concat(b, ', Updated from a = 100') WHERE a = 100"); + + this.pstmt = this.conn.prepareStatement( + "UPDATE testExplicitPartitions PARTITION(`p100-99999`, pNeg) SET a = -222, b = concat(b, ', Updated from a = 100') WHERE a = 100"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + + this.pstmt = c.prepareStatement("UPDATE testExplicitPartitions SET b = concat(b, ', Updated2') WHERE a = 1000000"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + // Test DELETE + this.stmt.executeUpdate("DELETE FROM testExplicitPartitions PARTITION (pNeg) WHERE a = -1"); + this.pstmt = this.conn.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (pNeg) WHERE a = -1"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + this.pstmt = c.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (pNeg) WHERE a = -1"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.stmt.executeUpdate("DELETE FROM testExplicitPartitions PARTITION (subp1) WHERE b like '%subp1%'"); + this.pstmt = this.conn.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (subp1) WHERE b like '%subp1%'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt.execute(); + this.pstmt = c.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (subp1) WHERE b like '%subp1%'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.pstmt.execute(); + + this.stmt.executeUpdate("FLUSH STATUS"); + this.stmt.executeUpdate("LOCK TABLE testExplicitPartitions WRITE"); + this.stmt.executeUpdate("DELETE FROM testExplicitPartitions PARTITION (subp1) WHERE b = 'p0-9:subp3'"); + this.pstmt = this.conn.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (subp1) WHERE b = 'p0-9:subp3'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.stmt.executeUpdate("DELETE FROM testExplicitPartitions PARTITION (`p0-9`) WHERE b = 'p0-9:subp3'"); + this.pstmt = this.conn.prepareStatement("DELETE FROM testExplicitPartitions PARTITION (`p0-9`) WHERE b = 'p0-9:subp3'"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.stmt.executeUpdate("UNLOCK TABLES"); + + // Test multi-table DELETE + this.stmt.executeUpdate("CREATE TABLE testExplicitPartitions2 LIKE testExplicitPartitions"); + + this.pstmt = this.conn.prepareStatement( + "INSERT INTO testExplicitPartitions2 PARTITION (`p10-99`, subp3, `p100-99999`) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement( + "INSERT INTO testExplicitPartitions2 PARTITION (`p10-99`, subp3, `p100-99999`) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.executeUpdate( + "INSERT INTO testExplicitPartitions2 PARTITION (`p10-99`, subp3, `p100-99999`) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + + this.stmt.executeUpdate("ALTER TABLE testExplicitPartitions2 TRUNCATE PARTITION `p10-99`, `p0-9`, `p100-99999`"); + + this.pstmt = this.conn.prepareStatement( + "INSERT IGNORE INTO testExplicitPartitions2 PARTITION (subp3) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement( + "INSERT IGNORE INTO testExplicitPartitions2 PARTITION (subp3) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.executeUpdate( + "INSERT IGNORE INTO testExplicitPartitions2 PARTITION (subp3) SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + + this.stmt.executeUpdate("TRUNCATE TABLE testExplicitPartitions2"); + this.stmt.executeUpdate("INSERT INTO testExplicitPartitions2 SELECT * FROM testExplicitPartitions PARTITION (subp3, `p10-99`, `p100-99999`)"); + + this.pstmt = this.conn + .prepareStatement("CREATE TABLE testExplicitPartitions3 SELECT * FROM testExplicitPartitions PARTITION (pNeg,subp3,`p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c + .prepareStatement("CREATE TABLE testExplicitPartitions3 SELECT * FROM testExplicitPartitions PARTITION (pNeg,subp3,`p100-99999`)"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.stmt.executeUpdate("CREATE TABLE testExplicitPartitions3 SELECT * FROM testExplicitPartitions PARTITION (pNeg,subp3,`p100-99999`)"); + + this.pstmt = this.conn.prepareStatement( + "DELETE testExplicitPartitions, testExplicitPartitions2 FROM testExplicitPartitions PARTITION (pNeg), testExplicitPartitions3, testExplicitPartitions2 PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions3.a = testExplicitPartitions2.a"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement( + "DELETE testExplicitPartitions, testExplicitPartitions2 FROM testExplicitPartitions PARTITION (pNeg), testExplicitPartitions3, testExplicitPartitions2 PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions3.a = testExplicitPartitions2.a"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.executeUpdate( + "DELETE testExplicitPartitions, testExplicitPartitions2 FROM testExplicitPartitions PARTITION (pNeg), testExplicitPartitions3, testExplicitPartitions2 PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions3.a = testExplicitPartitions2.a"); + + this.pstmt = this.conn.prepareStatement( + "DELETE FROM testExplicitPartitions2, testExplicitPartitions3 USING testExplicitPartitions2 PARTITION (`p0-9`), testExplicitPartitions3, testExplicitPartitions PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions2.a = testExplicitPartitions.a"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement); + this.pstmt = c.prepareStatement( + "DELETE FROM testExplicitPartitions2, testExplicitPartitions3 USING testExplicitPartitions2 PARTITION (`p0-9`), testExplicitPartitions3, testExplicitPartitions PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions2.a = testExplicitPartitions.a"); + assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement); + this.stmt.executeUpdate( + "DELETE FROM testExplicitPartitions2, testExplicitPartitions3 USING testExplicitPartitions2 PARTITION (`p0-9`), testExplicitPartitions3, testExplicitPartitions PARTITION (subp3) WHERE testExplicitPartitions.a = testExplicitPartitions3.a AND testExplicitPartitions3.b = 'subp3' AND testExplicitPartitions2.a = testExplicitPartitions.a"); + + this.stmt.executeUpdate("SET @@default_storage_engine = @old_default_storage_engine"); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testExplicitPartitions, testExplicitPartitions2, testExplicitPartitions3"); + + if (c != null) { + c.close(); + } + if (datadir != null) { + File f = new File(datadir + dbname + File.separator + "loadtestExplicitPartitions.txt"); + if (f.exists()) { + f.deleteOnExit(); + } else { + fail("File " + datadir + dbname + File.separator + "loadtestExplicitPartitions.txt cannot be deleted." + + "You should run server and tests on the same filesystem."); + } + } + } + } + } + + /** + * WL#1326 - GIS: Precise spatial operations + * + * GIS functions added in 5.6GA: ST_Intersection(g1 geometry, g2 geometry); ST_Difference(g1 geometry, g2 geometry); + * ST_Union(g1 geometry, g2 geometry); ST_SymDifference(g1 geometry, g2 geometry); ST_Buffer(g1 geometry, d + * numeric). + * + * @throws SQLException + */ + public void testGISPreciseSpatialFunctions() throws Exception { + if (!versionMeetsMinimum(5, 6)) { + return; + } + + String[] querySamples = new String[] { + "SELECT ST_AsText(ST_Intersection(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), ST_GeomFromText('POLYGON((0 3, 8 3, 4 9, 0 3))')))", + "SELECT ST_AsText(ST_Difference(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), ST_GeomFromText('POLYGON((0 3, 8 3, 4 9, 0 3))')))", + "SELECT ST_AsText(ST_Union(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), ST_GeomFromText('POLYGON((0 3, 8 3, 4 9, 0 3))')))", + "SELECT ST_AsText(ST_SymDifference(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), ST_GeomFromText('POLYGON((0 3, 8 3, 4 9, 0 3))')))", + "SELECT ST_AsText(ST_Buffer(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), 0.5))", + "SELECT ST_Distance(ST_GeomFromText('POLYGON((0 0, 8 0, 4 6, 0 0))'), ST_GeomFromText('POLYGON((0 10, 8 10, 4 16, 0 10))'))" }; + + for (String query : querySamples) { + this.rs = this.stmt.executeQuery(query); + assertTrue("Query should return at least one row.", this.rs.next()); + assertFalse("Query should return only one row.", this.rs.next()); + this.rs.close(); + } + } + + /** + * WL#5787 - IPv6-capable INET_ATON and INET_NTOA functions + * + * IPv6 functions added in 5.6GA: INET6_ATON(ip) and INET6_NTOA(ip). + * + * @throws SQLException + */ + public void testIPv6Functions() throws Exception { + if (!versionMeetsMinimum(5, 6, 11)) { + // MySQL 5.6.11 includes a bug fix (Bug#68454) that is required to run this test successfully. + return; + } + + String[][] dataSamples = new String[][] { { "127.0.0.1", "172.0.0.1" }, { "192.168.1.1", "::ffff:192.168.1.1" }, { "10.1", "::ffff:10.1" }, + { "172.16.260.4", "172.16.260.4" }, { "::1", "::1" }, { "10AA:10bb:10CC:10dd:10EE:10FF:10aa:10BB", "10aa:10bb:10cc:10dd:10ee:10ff:10aa:10bb" }, + { "00af:0000:0000:0000:10af:000a:000b:0001", "00af:0000:0000:0000:10af:000a:000b:0001" }, + { "48:4df1::0010:ad3:1100", "48:4df1::0010:ad3:1100" }, + { "2000:abcd:1234:0000:efgh:1000:2000:3000", "2000:abcd:1234:0000:efgh:1000:2000:3000" }, + { "2000:abcd:1234:0000:1000:2000:3000", "2000:abcd:1234:0000:1000:2000:3000" } }; + String[][] dataExpected = new String[][] { { "127.0.0.1", "172.0.0.1" }, { "192.168.1.1", "::ffff:192.168.1.1" }, { "10.0.0.1", null }, { null, null }, + { null, "::1" }, { null, "10aa:10bb:10cc:10dd:10ee:10ff:10aa:10bb" }, { null, "af::10af:a:b:1" }, { null, "48:4df1::10:ad3:1100" }, + { null, null }, { null, null } }; + + createTable("testWL5787", "(id INT AUTO_INCREMENT PRIMARY KEY, ipv4 INT UNSIGNED, ipv6 VARBINARY(16))"); + + Connection testConn = this.conn; + if (versionMeetsMinimum(5, 7, 10)) { + // MySQL 5.7.10+ requires non STRICT_TRANS_TABLES to use these functions with invalid data. + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "false"); + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + testConn = getConnectionWithProps(props); + } + this.pstmt = testConn.prepareStatement("INSERT INTO testWL5787 VALUES (NULL, INET_ATON(?), INET6_ATON(?))"); + + for (String[] data : dataSamples) { + this.pstmt.setString(1, data[0]); + this.pstmt.setString(2, data[1]); + this.pstmt.addBatch(); + } + int c = 0; + for (int r : this.pstmt.executeBatch()) { + c += r; + } + assertEquals("Failed inserting data samples: wrong number of inserts.", dataSamples.length, c); + + this.rs = this.stmt.executeQuery("SELECT id, INET_NTOA(ipv4), INET6_NTOA(ipv6) FROM testWL5787"); + int i = 0; + while (this.rs.next()) { + i = this.rs.getInt(1); + assertEquals("Wrong IPv4 data in row [" + i + "].", dataExpected[i - 1][0], this.rs.getString(2)); + assertEquals("Wrong IPv6 data in row [" + i + "].", dataExpected[i - 1][1], this.rs.getString(3)); + } + + this.pstmt.close(); + testConn.close(); + } + + /** + * WL#5538 - InnoDB Full-Text Search Support + * + * CREATE TABLE syntax changed in 5.6GA + * + * InnoDB engine accepts FULLTEXT indexes. + * CREATE TABLE ... FULLTEXT(...) ... ENGINE=InnoDB + * + * @throws SQLException + */ + public void testFULLTEXTSearchInnoDB() throws Exception { + + if (!versionMeetsMinimum(5, 6)) { + return; + } + + createTable("testFULLTEXTSearchInnoDB", + "(id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, " + "title VARCHAR(200), body TEXT, FULLTEXT (title , body)) ENGINE=InnoDB"); + + this.stmt.executeUpdate("INSERT INTO testFULLTEXTSearchInnoDB (title, body) VALUES ('MySQL Tutorial','DBMS stands for DataBase ...'), " + + "('How To Use MySQL Well','After you went through a ...'), ('Optimizing MySQL','In this tutorial we will show ...'), " + + "('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'), ('MySQL vs. YourSQL','In the following database comparison ...'), " + + "('MySQL Security','When configured properly, MySQL ...')"); + + String[] querySamples = new String[] { "SELECT * FROM testFULLTEXTSearchInnoDB WHERE MATCH (title, body) AGAINST ('database' IN NATURAL LANGUAGE MODE)", + "SELECT * FROM testFULLTEXTSearchInnoDB WHERE MATCH (title, body) AGAINST ('database' IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION)", + "SELECT * FROM testFULLTEXTSearchInnoDB WHERE MATCH (title, body) AGAINST ('YourSQL' IN BOOLEAN MODE)", + "SELECT * FROM testFULLTEXTSearchInnoDB WHERE MATCH (title, body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE)", + "SELECT MATCH (title, body) AGAINST ('database' IN NATURAL LANGUAGE MODE) FROM testFULLTEXTSearchInnoDB", + "SELECT MATCH (title, body) AGAINST ('database' IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION) FROM testFULLTEXTSearchInnoDB", + "SELECT MATCH (title, body) AGAINST ('YourSQL' IN BOOLEAN MODE) FROM testFULLTEXTSearchInnoDB", + "SELECT MATCH (title, body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE) FROM testFULLTEXTSearchInnoDB" }; + + for (String query : querySamples) { + this.rs = this.stmt.executeQuery(query); + assertTrue("Query [" + query + "] should return some rows.", this.rs.next()); + this.rs.close(); + } + } + + /** + * WL#6555 - Online rename index + * + * ALTER TABLE syntax changed in 5.7.1 + * + * Alter table allows to rename indexes. ALTER TABLE ... RENAME INDEX x TO y + * + * @throws SQLException + */ + public void testRenameIndex() throws Exception { + + if (!versionMeetsMinimum(5, 7, 1)) { + return; + } + + createTable("testRenameIndex", "(col1 INT, col2 INT, INDEX (col1)) ENGINE=InnoDB"); + this.stmt.execute("CREATE INDEX testIdx ON testRenameIndex (col2)"); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + this.rs = dbmd.getIndexInfo(null, null, "testRenameIndex", false, true); + assertTrue("Expected 1 (of 2) indexes.", this.rs.next()); + assertEquals("Wrong index name for table 'testRenameIndex'.", "col1", this.rs.getString(6)); + assertTrue("Expected 2 (of 2) indexes.", this.rs.next()); + assertEquals("Wrong index name for table 'testRenameIndex'.", "testIdx", this.rs.getString(6)); + assertFalse("No more indexes expected for table 'testRenameIndex'.", this.rs.next()); + + this.stmt.execute("ALTER TABLE testRenameIndex RENAME INDEX col1 TO col1Index"); + this.stmt.execute("ALTER TABLE testRenameIndex RENAME INDEX testIdx TO testIndex"); + + this.rs = dbmd.getIndexInfo(null, null, "testRenameIndex", false, true); + assertTrue("Expected 1 (of 2) indexes.", this.rs.next()); + assertEquals("Wrong index name for table 'testRenameIndex'.", "col1Index", this.rs.getString(6)); + assertTrue("Expected 2 (of 2) indexes.", this.rs.next()); + assertEquals("Wrong index name for table 'testRenameIndex'.", "testIndex", this.rs.getString(6)); + assertFalse("No more indexes expected for table 'testRenameIndex'.", this.rs.next()); + } + + /** + * WL#6406 - Stacked diagnostic areas + * + * "STACKED" in "GET [CURRENT | STACKED] DIAGNOSTICS" syntax was added in 5.7.0. Final behavior was implemented in + * version 5.7.2, by WL#5928 - Most statements should clear the diagnostic area. + * + * @throws SQLException + */ + public void testGetStackedDiagnostics() throws Exception { + + if (!versionMeetsMinimum(5, 7, 2)) { + return; + } + + // test calling GET STACKED DIAGNOSTICS outside an handler + final Statement locallyScopedStmt = this.stmt; + assertThrows(SQLException.class, "GET STACKED DIAGNOSTICS when handler not active", new Callable() { + public Void call() throws Exception { + locallyScopedStmt.execute("GET STACKED DIAGNOSTICS @num = NUMBER"); + return null; + } + }); + + // test calling GET STACKED DIAGNOSTICS inside an handler + // (stored procedure is based on documentation example) + createTable("testGetStackedDiagnosticsTbl", "(c VARCHAR(8) NOT NULL)"); + createProcedure("testGetStackedDiagnosticsSP", + "() BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN GET CURRENT DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO, @msg = MESSAGE_TEXT; " + + "SELECT 'current DA before insert in handler' AS op, @errno AS errno, @msg AS msg; " // 1st result + + "GET STACKED DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO, @msg = MESSAGE_TEXT; " + + "SELECT 'stacked DA before insert in handler' AS op, @errno AS errno, @msg AS msg; " // 2nd result + + "INSERT INTO testGetStackedDiagnosticsTbl (c) VALUES('gnitset'); " + "GET CURRENT DIAGNOSTICS @num = NUMBER; " + + "IF @num = 0 THEN SELECT 'INSERT succeeded, current DA is empty' AS op; " // 3rd result + + "ELSE GET CURRENT DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO, @msg = MESSAGE_TEXT; " + + "SELECT 'current DA after insert in handler' AS op, @errno AS errno, @msg AS msg; END IF; " + + "GET STACKED DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO, @msg = MESSAGE_TEXT; " + + "SELECT 'stacked DA after insert in handler' AS op, @errno AS errno, @msg AS msg; END; " // 4th result + + "INSERT INTO testGetStackedDiagnosticsTbl (c) VALUES ('testing');INSERT INTO testGetStackedDiagnosticsTbl (c) VALUES (NULL); END"); + + CallableStatement cStmt = this.conn.prepareCall("CALL testGetStackedDiagnosticsSP()"); + assertTrue(cStmt.execute()); + + // test 1st ResultSet + this.rs = cStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals("current DA before insert in handler", this.rs.getString(1)); + assertEquals(1048, this.rs.getInt(2)); + assertEquals("Column 'c' cannot be null", this.rs.getString(3)); + assertFalse(this.rs.next()); + this.rs.close(); + + // test 2nd ResultSet + assertTrue(cStmt.getMoreResults()); + this.rs = cStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals("stacked DA before insert in handler", this.rs.getString(1)); + assertEquals(1048, this.rs.getInt(2)); + assertEquals("Column 'c' cannot be null", this.rs.getString(3)); + assertFalse(this.rs.next()); + this.rs.close(); + + // test 3rd ResultSet + assertTrue(cStmt.getMoreResults()); + this.rs = cStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals("INSERT succeeded, current DA is empty", this.rs.getString(1)); + assertFalse(this.rs.next()); + this.rs.close(); + + // test 4th ResultSet + assertTrue(cStmt.getMoreResults()); + this.rs = cStmt.getResultSet(); + assertTrue(this.rs.next()); + assertEquals("stacked DA after insert in handler", this.rs.getString(1)); + assertEquals(1048, this.rs.getInt(2)); + assertEquals("Column 'c' cannot be null", this.rs.getString(3)); + assertFalse(this.rs.next()); + this.rs.close(); + + // no more ResultSets + assertFalse(cStmt.getMoreResults()); + cStmt.close(); + + // test table contents + this.rs = this.stmt.executeQuery("SELECT * FROM testGetStackedDiagnosticsTbl"); + assertTrue(this.rs.next()); + assertEquals("testing", this.rs.getString(1)); + assertTrue(this.rs.next()); + assertEquals("gnitset", this.rs.getString(1)); + assertFalse(this.rs.next()); + this.rs.close(); + } + + /** + * WL#6868 - Support transportable tablespaces for single innodb partition. + * + * New syntax introduced in MySQL 5.7.4. + * ALTER TABLE t DISCARD PARTITION {p[[,p1]..]|ALL} TABLESPACE; + * ALTER TABLE t IMPORT PARTITION {p[[,p1]..]|ALL} TABLESPACE; + */ + public void testDiscardImportPartitions() throws Exception { + + if (!versionMeetsMinimum(5, 7, 4)) { + return; + } + + createTable("testDiscardImportPartitions", + "(id INT) ENGINE = InnoDB PARTITION BY RANGE (id) (PARTITION p1 VALUES LESS THAN (0), PARTITION p2 VALUES LESS THAN MAXVALUE)"); + + this.stmt.executeUpdate("INSERT INTO testDiscardImportPartitions VALUES (-3), (-2), (-1), (0), (1), (2), (3)"); + + this.rs = this.stmt.executeQuery("CHECK TABLE testDiscardImportPartitions"); + assertTrue(this.rs.next()); + assertEquals("status", this.rs.getString(3)); + assertEquals("OK", this.rs.getString(4)); + this.rs.close(); + + this.stmt.executeUpdate("ALTER TABLE testDiscardImportPartitions DISCARD PARTITION p1 TABLESPACE"); + + this.rs = this.stmt.executeQuery("CHECK TABLE testDiscardImportPartitions"); + assertTrue(this.rs.next()); + assertEquals("error", this.rs.getString(3)); + assertEquals("Partition p1 returned error", this.rs.getString(4)); + this.rs.close(); + + assertThrows(SQLException.class, "Tablespace is missing for table .*", new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + SyntaxRegressionTest.this.stmt.executeUpdate("ALTER TABLE testDiscardImportPartitions IMPORT PARTITION p1 TABLESPACE"); + return null; + } + }); + } + + /** + * WL#7909 - Server side JSON functions + * + * Test support for data type JSON. + * + * New JSON functions added in MySQL 5.7.8: + * - JSON_APPEND(), Append data to JSON document (only in 5.7.8) + * - JSON_ARRAY_APPEND(), Append data to JSON document (added in 5.7.9+) + * - JSON_ARRAY_INSERT(), Insert into JSON array + * - JSON_ARRAY(), Create JSON array + * - JSON_CONTAINS_PATH(), Whether JSON document contains any data at path + * - JSON_CONTAINS(), Whether JSON document contains specific object at path + * - JSON_DEPTH(), Maximum depth of JSON document + * - JSON_EXTRACT(), Return data from JSON document + * - JSON_INSERT(), Insert data into JSON document + * - JSON_KEYS(), Array of keys from JSON document + * - JSON_LENGTH(), Number of elements in JSON document + * - JSON_MERGE(), Merge JSON documents + * - JSON_OBJECT(), Create JSON object + * - JSON_QUOTE(), Quote JSON document + * - JSON_REMOVE(), Remove data from JSON document + * - JSON_REPLACE(), Replace values in JSON document + * - JSON_SEARCH(), Path to value within JSON document + * - JSON_SET(), Insert data into JSON document + * - JSON_TYPE(), Type of JSON value + * - JSON_UNQUOTE(), Unquote JSON value + * - JSON_VALID(), Whether JSON value is valid + */ + public void testJsonType() throws Exception { + if (!versionMeetsMinimum(5, 7, 8)) { + return; + } + + createTable("testJsonType", "(id INT PRIMARY KEY, jsonDoc JSON)"); + assertEquals(1, this.stmt.executeUpdate("INSERT INTO testJsonType VALUES (1, '{\"key1\": \"value1\"}')")); + + // Plain statement. + this.rs = this.stmt.executeQuery("SELECT * FROM testJsonType"); + assertEquals("JSON", this.rs.getMetaData().getColumnTypeName(2)); + assertTrue(this.rs.next()); + assertEquals("{\"key1\": \"value1\"}", this.rs.getString(2)); + assertEquals("{\"key1\": \"value1\"}", this.rs.getObject(2)); + assertFalse(this.rs.next()); + + // Updatable ResultSet. + Statement testStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + this.rs = testStmt.executeQuery("SELECT * FROM testJsonType"); + assertTrue(this.rs.next()); + this.rs.updateString(2, "{\"key1\": \"value1\", \"key2\": \"value2\"}"); + this.rs.updateRow(); + + this.rs = testStmt.executeQuery("SELECT * FROM testJsonType"); + assertEquals("JSON", this.rs.getMetaData().getColumnTypeName(2)); + assertTrue(this.rs.next()); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getString(2)); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getObject(2)); + assertFalse(this.rs.next()); + + // PreparedStatement. + this.pstmt = this.conn.prepareStatement("SELECT * FROM testJsonType"); + this.rs = this.pstmt.executeQuery(); + assertEquals("JSON", this.rs.getMetaData().getColumnTypeName(2)); + assertTrue(this.rs.next()); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getString(2)); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getObject(2)); + assertFalse(this.rs.next()); + + // ServerPreparedStatement. + Connection testConn = getConnectionWithProps("useServerPrepStmts=true"); + this.pstmt = testConn.prepareStatement("SELECT * FROM testJsonType"); + this.rs = this.pstmt.executeQuery(); + assertEquals("JSON", this.rs.getMetaData().getColumnTypeName(2)); + assertTrue(this.rs.next()); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getString(2)); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", this.rs.getObject(2)); + assertFalse(this.rs.next()); + testConn.close(); + + // CallableStatement. + createProcedure("testJsonTypeProc", "(OUT jsonDoc JSON) SELECT t.jsonDoc INTO jsonDoc FROM testJsonType t"); + CallableStatement testCstmt = this.conn.prepareCall("{CALL testJsonTypeProc(?)}"); + testCstmt.registerOutParameter(1, Types.CHAR); + testCstmt.execute(); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", testCstmt.getString(1)); + assertEquals("{\"key1\": \"value1\", \"key2\": \"value2\"}", testCstmt.getObject(1)); + + // JSON functions. + testJsonTypeCheckFunction(versionMeetsMinimum(5, 7, 9) ? "SELECT JSON_ARRAY_APPEND('[1]', '$', 2)" : "SELECT JSON_APPEND('[1]', '$', 2)", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_ARRAY_INSERT('[2]', '$[0]', 1)", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_ARRAY(1, 2)", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_CONTAINS_PATH('{\"a\": 1}', 'one', '$.a')", "1"); + testJsonTypeCheckFunction("SELECT JSON_CONTAINS('{\"a\": 1}', '1', '$.a')", "1"); + testJsonTypeCheckFunction("SELECT JSON_DEPTH('{\"a\": 1}')", "2"); + testJsonTypeCheckFunction("SELECT JSON_EXTRACT('[1, 2]', '$[0]')", "1"); + testJsonTypeCheckFunction("SELECT JSON_INSERT('[1]', '$[1]', 2)", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_KEYS('{\"a\": 1}')", "[\"a\"]"); + testJsonTypeCheckFunction("SELECT JSON_LENGTH('{\"a\": 1}')", "1"); + testJsonTypeCheckFunction("SELECT JSON_MERGE('[1]', '[2]')", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_OBJECT('a', 1)", "{\"a\": 1}"); + testJsonTypeCheckFunction("SELECT JSON_QUOTE('[1]')", "\"[1]\""); + testJsonTypeCheckFunction("SELECT JSON_REMOVE('[1, 2]', '$[1]')", "[1]"); + testJsonTypeCheckFunction("SELECT JSON_REPLACE('[0]', '$[0]', 1)", "[1]"); + testJsonTypeCheckFunction("SELECT JSON_SEARCH('{\"a\": \"1\"}', 'one', '1')", "\"$.a\""); + testJsonTypeCheckFunction("SELECT JSON_SET('[1, 1]', '$[1]', 2)", "[1, 2]"); + testJsonTypeCheckFunction("SELECT JSON_TYPE('[]')", "ARRAY"); + testJsonTypeCheckFunction("SELECT JSON_UNQUOTE('\"[1]\"')", "[1]"); + testJsonTypeCheckFunction("SELECT JSON_VALID('{\"a\": 1}')", "1"); + } + + private void testJsonTypeCheckFunction(String sql, String expectedResult) throws Exception { + this.rs = this.stmt.executeQuery(sql); + assertTrue(this.rs.next()); + assertEquals(expectedResult, this.rs.getString(1)); + } + + /** + * WL#8016 - Parser for optimizer hints. + * + * Test syntax for optimizer hints. + * + * New optimizer hints feature added in MySQL 5.7.7. Hints are permitted in these contexts: + * At the beginning of DML statements + * - SELECT /*+ ... */ ... + * - INSERT /*+ ... */ ... + * - REPLACE /*+ ... */ ... + * - UPDATE /*+ ... */ ... + * - DELETE /*+ ... */ ... + * At the beginning of query blocks: + * - (SELECT /*+ ... */ ... ) + * - (SELECT ... ) UNION (SELECT /*+ ... */ ... ) + * - (SELECT /*+ ... */ ... ) UNION (SELECT /*+ ... */ ... ) + * - UPDATE ... WHERE x IN (SELECT /*+ ... */ ...) + * - INSERT ... SELECT /*+ ... */ ... + * In hintable statements prefaced by EXPLAIN. For example: + * - EXPLAIN SELECT /*+ ... */ ... + * - EXPLAIN UPDATE ... WHERE x IN (SELECT /*+ ... */ ...) + */ + public void testHints() throws Exception { + if (!versionMeetsMinimum(5, 7, 7)) { + return; + } + + /* + * Test hints syntax variations. + */ + // Valid hints. + testHintsSyntax("SELECT /*+ max_execution_time(100) */ SLEEP(5)", true, false); + testHintsSyntax("SELECT/*+ max_execution_time(100) */SLEEP(5)", true, false); + testHintsSyntax("SELECT /*+ max_execution_time(100) */ SLEEP(5) /*+ wrong location, just comments */", true, false); + testHintsSyntax("SELECT /*+ max_execution_time(100) *//* comment */ SLEEP(5)", true, false); + + // Invalid hints. + testHintsSyntax("SELECT /*+ max_execution_time *//*+ (100) */ SLEEP(0.5)", false, true); + testHintsSyntax("SELECT /*+! max_execution_time (100) */ SLEEP(0.5)", false, true); + + // Valid and invalid hints. + testHintsSyntax("SELECT /*+ max_execution_time (100) bad_hint */ SLEEP(5)", true, true); + + // No hints. + testHintsSyntax("/*+ max_execution_time(100) */SELECT SLEEP(0.5)", false, false); + testHintsSyntax("SELECT SLEEP(0.5) /*+ max_execution_time(100) */", false, false); + testHintsSyntax("SELECT /* + max_execution_time(100) */ SLEEP(0.5)", false, false); + testHintsSyntax("SELECT /* comment *//*+ max_execution_time(100) */ SLEEP(0.5)", false, false); + testHintsSyntax("SELECT /*!+1-1, */ 1", false, false); + + /* + * Test hints in different query types using Statements. + */ + createTable("testHints", "(id INT PRIMARY KEY, txt CHAR(2))"); + + // Hints in single query. + assertEquals(1, this.stmt.executeUpdate("INSERT /*+ mrr(testHints) */ INTO testHints VALUES (1, 'a')")); + assertNull(this.stmt.getWarnings()); + assertEquals(2, this.stmt.executeUpdate("REPLACE /*+ mrr(testHints) */ INTO testHints VALUES (1, 'A')")); + assertNull(this.stmt.getWarnings()); + assertEquals(1, this.stmt.executeUpdate("UPDATE /*+ mrr(testHints) */ testHints SET txt = 'Aa'")); + assertNull(this.stmt.getWarnings()); + this.rs = this.stmt.executeQuery("SELECT /*+ max_execution_time(100) */ * FROM testHints"); + assertNull(this.stmt.getWarnings()); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals("Aa", this.rs.getString(2)); + assertFalse(this.rs.next()); + assertEquals(1, this.stmt.executeUpdate("DELETE /*+ mrr(testHints) */ FROM testHints")); + assertNull(this.stmt.getWarnings()); + + // Hints in sub-query block. + assertEquals(1, this.stmt.executeUpdate("INSERT INTO testHints (SELECT /*+ qb_name(dummy) */ 2, 'b')")); + assertNull(this.stmt.getWarnings()); + assertEquals(2, this.stmt.executeUpdate("REPLACE INTO testHints (SELECT /*+ qb_name(dummy) */ 2, 'B')")); + assertNull(this.stmt.getWarnings()); + assertEquals(1, this.stmt.executeUpdate("UPDATE testHints SET txt = 'Bb' WHERE id IN (SELECT /*+ qb_name(dummy) */ 2)")); + assertNull(this.stmt.getWarnings()); + this.rs = this.stmt.executeQuery("SELECT /*+ max_execution_time(100) */ 1, 'Aa' UNION SELECT /*+ qb_name(dummy) */ * FROM testHints"); + assertNull(this.stmt.getWarnings()); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals("Aa", this.rs.getString(2)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertEquals("Bb", this.rs.getString(2)); + assertFalse(this.rs.next()); + assertEquals(1, this.stmt.executeUpdate("DELETE FROM testHints WHERE id IN (SELECT /*+ qb_name(dummy) */ 2)")); + assertNull(this.stmt.getWarnings()); + + /* + * Test hints in different query types using PreparedStatements. + */ + for (String connProps : new String[] { "useServerPrepStmts=false", "useServerPrepStmts=true" }) { + Connection testConn = null; + testConn = getConnectionWithProps(connProps); + + // Hints in single query. + this.pstmt = testConn.prepareStatement("INSERT /*+ mrr(testHints) */ INTO testHints VALUES (?, ?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setString(2, "a"); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("REPLACE /*+ mrr(testHints) */ INTO testHints VALUES (?, ?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setString(2, "A"); + assertEquals(2, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("UPDATE /*+ mrr(testHints) */ testHints SET txt = ?"); + this.pstmt.setString(1, "Aa"); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("SELECT /*+ max_execution_time(100) */ * FROM testHints WHERE id = ?"); + this.pstmt.setInt(1, 1); + this.rs = this.pstmt.executeQuery(); + assertNull(this.pstmt.getWarnings()); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals("Aa", this.rs.getString(2)); + assertFalse(this.rs.next()); + this.pstmt = testConn.prepareStatement("DELETE /*+ mrr(testHints) */ FROM testHints WHERE id = ?"); + this.pstmt.setInt(1, 1); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + + // Hints in sub-query block. + this.pstmt = testConn.prepareStatement("INSERT INTO testHints (SELECT /*+ qb_name(dummy) */ ?, ?)"); + this.pstmt.setInt(1, 2); + this.pstmt.setString(2, "b"); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("REPLACE INTO testHints (SELECT /*+ qb_name(dummy) */ ?, ?)"); + this.pstmt.setInt(1, 2); + this.pstmt.setString(2, "B"); + assertEquals(2, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("UPDATE testHints SET txt = 'Bb' WHERE id IN (SELECT /*+ qb_name(dummy) */ ?)"); + this.pstmt.setInt(1, 2); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + this.pstmt = testConn.prepareStatement("SELECT /*+ max_execution_time(100) */ ?, ? UNION SELECT /*+ qb_name(dummy) */ * FROM testHints"); + this.pstmt.setInt(1, 1); + this.pstmt.setString(2, "Aa"); + this.rs = this.pstmt.executeQuery(); + assertNull(this.pstmt.getWarnings()); + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertEquals("Aa", this.rs.getString(2)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertEquals("Bb", this.rs.getString(2)); + assertFalse(this.rs.next()); + this.pstmt = testConn.prepareStatement("DELETE FROM testHints WHERE id IN (SELECT /*+ qb_name(dummy) */ ?)"); + this.pstmt.setInt(1, 2); + assertEquals(1, this.pstmt.executeUpdate()); + assertNull(this.pstmt.getWarnings()); + + testConn.close(); + } + } + + private void testHintsSyntax(String query, boolean processesHint, boolean warningExpected) throws Exception { + this.stmt.clearWarnings(); + this.rs = this.stmt.executeQuery(query); + if (warningExpected) { + assertNotNull(this.stmt.getWarnings()); + assertTrue(this.stmt.getWarnings().getMessage().startsWith("Optimizer hint syntax error")); + } else { + assertNull(this.stmt.getWarnings()); + } + assertTrue(this.rs.next()); + assertEquals(processesHint ? 1 : 0, this.rs.getInt(1)); + assertFalse(this.rs.next()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/UtilsRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/UtilsRegressionTest.java new file mode 100644 index 0000000..5880bc9 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/UtilsRegressionTest.java @@ -0,0 +1,698 @@ +/* + Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; + +import com.mysql.jdbc.Connection; +import com.mysql.jdbc.ExceptionInterceptor; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.TimeUtil; + +import testsuite.BaseTestCase; + +/** + * Regression tests for utility classes. + */ +public class UtilsRegressionTest extends BaseTestCase { + + /** + * Creates a new UtilsRegressionTest. + * + * @param name + * the name of the test + */ + public UtilsRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(UtilsRegressionTest.class); + } + + /** + * Tests all TimeZone mappings supported. + * + * @throws Exception + * if the test fails. + */ + public void testTimeZones() throws Exception { + /* + * Time Zones can be identified by many different ways according to Unicode CLDR database. The following map contain the correspondence between + * alternative Time Zone designations to Standard Time Zones ID (IANA/Olson database). This data was generated from IANA Time Zone database v. 2015f + * (http://www.iana.org/time-zones) and Unicode CLDR v.28 (http://cldr.unicode.org/) + * + * Both the file com/mysql/jdbc/TimeZoneMapping.properties and the following data are generated from a MySQL Connector/J internal utility. + */ + + Map tzMap = new HashMap(); + + // GENERATED CODE STARTS HERE + + // Windows Zones: + tzMap.put("AUS Central Daylight Time", "Australia/Darwin"); + tzMap.put("AUS Central Standard Time", "Australia/Darwin"); + tzMap.put("AUS Eastern Daylight Time", "Australia/Sydney"); + tzMap.put("AUS Eastern Standard Time", "Australia/Sydney"); + tzMap.put("Afghanistan Daylight Time", "Asia/Kabul"); + tzMap.put("Afghanistan Standard Time", "Asia/Kabul"); + tzMap.put("Alaskan Daylight Time", "America/Anchorage"); + tzMap.put("Alaskan Standard Time", "America/Anchorage"); + tzMap.put("Arab Daylight Time", "Asia/Riyadh"); + tzMap.put("Arab Standard Time", "Asia/Riyadh"); + tzMap.put("Arabian Daylight Time", "Asia/Dubai"); + tzMap.put("Arabian Standard Time", "Asia/Dubai"); + tzMap.put("Arabic Daylight Time", "Asia/Baghdad"); + tzMap.put("Arabic Standard Time", "Asia/Baghdad"); + tzMap.put("Argentina Daylight Time", "America/Buenos_Aires"); + tzMap.put("Argentina Standard Time", "America/Buenos_Aires"); + tzMap.put("Atlantic Daylight Time", "America/Halifax"); + tzMap.put("Atlantic Standard Time", "America/Halifax"); + tzMap.put("Azerbaijan Daylight Time", "Asia/Baku"); + tzMap.put("Azerbaijan Standard Time", "Asia/Baku"); + tzMap.put("Azores Daylight Time", "Atlantic/Azores"); + tzMap.put("Azores Standard Time", "Atlantic/Azores"); + tzMap.put("Bahia Daylight Time", "America/Bahia"); + tzMap.put("Bahia Standard Time", "America/Bahia"); + tzMap.put("Bangladesh Daylight Time", "Asia/Dhaka"); + tzMap.put("Bangladesh Standard Time", "Asia/Dhaka"); + tzMap.put("Belarus Daylight Time", "Europe/Minsk"); + tzMap.put("Belarus Standard Time", "Europe/Minsk"); + tzMap.put("Canada Central Daylight Time", "America/Regina"); + tzMap.put("Canada Central Standard Time", "America/Regina"); + tzMap.put("Cape Verde Daylight Time", "Atlantic/Cape_Verde"); + tzMap.put("Cape Verde Standard Time", "Atlantic/Cape_Verde"); + tzMap.put("Caucasus Daylight Time", "Asia/Yerevan"); + tzMap.put("Caucasus Standard Time", "Asia/Yerevan"); + tzMap.put("Cen. Australia Daylight Time", "Australia/Adelaide"); + tzMap.put("Cen. Australia Standard Time", "Australia/Adelaide"); + tzMap.put("Central America Daylight Time", "America/Guatemala"); + tzMap.put("Central America Standard Time", "America/Guatemala"); + tzMap.put("Central Asia Daylight Time", "Asia/Almaty"); + tzMap.put("Central Asia Standard Time", "Asia/Almaty"); + tzMap.put("Central Brazilian Daylight Time", "America/Cuiaba"); + tzMap.put("Central Brazilian Standard Time", "America/Cuiaba"); + tzMap.put("Central Daylight Time", "America/Chicago"); + tzMap.put("Central Daylight Time (Mexico)", "America/Mexico_City"); + tzMap.put("Central Europe Daylight Time", "Europe/Budapest"); + tzMap.put("Central Europe Standard Time", "Europe/Budapest"); + tzMap.put("Central European Daylight Time", "Europe/Warsaw"); + tzMap.put("Central European Standard Time", "Europe/Warsaw"); + tzMap.put("Central Pacific Daylight Time", "Pacific/Guadalcanal"); + tzMap.put("Central Pacific Standard Time", "Pacific/Guadalcanal"); + tzMap.put("Central Standard Time", "America/Chicago"); + tzMap.put("Central Standard Time (Mexico)", "America/Mexico_City"); + tzMap.put("China Daylight Time", "Asia/Shanghai"); + tzMap.put("China Standard Time", "Asia/Shanghai"); + tzMap.put("Dateline Daylight Time", "Etc/GMT+12"); + tzMap.put("Dateline Standard Time", "Etc/GMT+12"); + tzMap.put("E. Africa Daylight Time", "Africa/Nairobi"); + tzMap.put("E. Africa Standard Time", "Africa/Nairobi"); + tzMap.put("E. Australia Daylight Time", "Australia/Brisbane"); + tzMap.put("E. Australia Standard Time", "Australia/Brisbane"); + tzMap.put("E. South America Daylight Time", "America/Sao_Paulo"); + tzMap.put("E. South America Standard Time", "America/Sao_Paulo"); + tzMap.put("Eastern Daylight Time", "America/New_York"); + tzMap.put("Eastern Daylight Time (Mexico)", "America/Cancun"); + tzMap.put("Eastern Standard Time", "America/New_York"); + tzMap.put("Eastern Standard Time (Mexico)", "America/Cancun"); + tzMap.put("Egypt Daylight Time", "Africa/Cairo"); + tzMap.put("Egypt Standard Time", "Africa/Cairo"); + tzMap.put("Ekaterinburg Daylight Time", "Asia/Yekaterinburg"); + tzMap.put("Ekaterinburg Standard Time", "Asia/Yekaterinburg"); + tzMap.put("FLE Daylight Time", "Europe/Kiev"); + tzMap.put("FLE Standard Time", "Europe/Kiev"); + tzMap.put("Fiji Daylight Time", "Pacific/Fiji"); + tzMap.put("Fiji Standard Time", "Pacific/Fiji"); + tzMap.put("GMT Daylight Time", "Europe/London"); + tzMap.put("GMT Standard Time", "Europe/London"); + tzMap.put("GTB Daylight Time", "Europe/Bucharest"); + tzMap.put("GTB Standard Time", "Europe/Bucharest"); + tzMap.put("Georgian Daylight Time", "Asia/Tbilisi"); + tzMap.put("Georgian Standard Time", "Asia/Tbilisi"); + tzMap.put("Greenland Daylight Time", "America/Godthab"); + tzMap.put("Greenland Standard Time", "America/Godthab"); + tzMap.put("Greenwich Daylight Time", "Atlantic/Reykjavik"); + tzMap.put("Greenwich Standard Time", "Atlantic/Reykjavik"); + tzMap.put("Hawaiian Daylight Time", "Pacific/Honolulu"); + tzMap.put("Hawaiian Standard Time", "Pacific/Honolulu"); + tzMap.put("India Daylight Time", "Asia/Calcutta"); + tzMap.put("India Standard Time", "Asia/Calcutta"); + tzMap.put("Iran Daylight Time", "Asia/Tehran"); + tzMap.put("Iran Standard Time", "Asia/Tehran"); + tzMap.put("Israel Daylight Time", "Asia/Jerusalem"); + tzMap.put("Israel Standard Time", "Asia/Jerusalem"); + tzMap.put("Jordan Daylight Time", "Asia/Amman"); + tzMap.put("Jordan Standard Time", "Asia/Amman"); + tzMap.put("Kaliningrad Daylight Time", "Europe/Kaliningrad"); + tzMap.put("Kaliningrad Standard Time", "Europe/Kaliningrad"); + tzMap.put("Korea Daylight Time", "Asia/Seoul"); + tzMap.put("Korea Standard Time", "Asia/Seoul"); + tzMap.put("Libya Daylight Time", "Africa/Tripoli"); + tzMap.put("Libya Standard Time", "Africa/Tripoli"); + tzMap.put("Line Islands Daylight Time", "Pacific/Kiritimati"); + tzMap.put("Line Islands Standard Time", "Pacific/Kiritimati"); + tzMap.put("Magadan Daylight Time", "Asia/Magadan"); + tzMap.put("Magadan Standard Time", "Asia/Magadan"); + tzMap.put("Mauritius Daylight Time", "Indian/Mauritius"); + tzMap.put("Mauritius Standard Time", "Indian/Mauritius"); + tzMap.put("Middle East Daylight Time", "Asia/Beirut"); + tzMap.put("Middle East Standard Time", "Asia/Beirut"); + tzMap.put("Montevideo Daylight Time", "America/Montevideo"); + tzMap.put("Montevideo Standard Time", "America/Montevideo"); + tzMap.put("Morocco Daylight Time", "Africa/Casablanca"); + tzMap.put("Morocco Standard Time", "Africa/Casablanca"); + tzMap.put("Mountain Daylight Time", "America/Denver"); + tzMap.put("Mountain Daylight Time (Mexico)", "America/Chihuahua"); + tzMap.put("Mountain Standard Time", "America/Denver"); + tzMap.put("Mountain Standard Time (Mexico)", "America/Chihuahua"); + tzMap.put("Myanmar Daylight Time", "Asia/Rangoon"); + tzMap.put("Myanmar Standard Time", "Asia/Rangoon"); + tzMap.put("N. Central Asia Daylight Time", "Asia/Novosibirsk"); + tzMap.put("N. Central Asia Standard Time", "Asia/Novosibirsk"); + tzMap.put("Namibia Daylight Time", "Africa/Windhoek"); + tzMap.put("Namibia Standard Time", "Africa/Windhoek"); + tzMap.put("Nepal Daylight Time", "Asia/Katmandu"); + tzMap.put("Nepal Standard Time", "Asia/Katmandu"); + tzMap.put("New Zealand Daylight Time", "Pacific/Auckland"); + tzMap.put("New Zealand Standard Time", "Pacific/Auckland"); + tzMap.put("Newfoundland Daylight Time", "America/St_Johns"); + tzMap.put("Newfoundland Standard Time", "America/St_Johns"); + tzMap.put("North Asia Daylight Time", "Asia/Krasnoyarsk"); + tzMap.put("North Asia East Daylight Time", "Asia/Irkutsk"); + tzMap.put("North Asia East Standard Time", "Asia/Irkutsk"); + tzMap.put("North Asia Standard Time", "Asia/Krasnoyarsk"); + tzMap.put("Pacific Daylight Time", "America/Los_Angeles"); + tzMap.put("Pacific Daylight Time (Mexico)", "America/Santa_Isabel"); + tzMap.put("Pacific SA Daylight Time", "America/Santiago"); + tzMap.put("Pacific SA Standard Time", "America/Santiago"); + tzMap.put("Pacific Standard Time", "America/Los_Angeles"); + tzMap.put("Pacific Standard Time (Mexico)", "America/Santa_Isabel"); + tzMap.put("Pakistan Daylight Time", "Asia/Karachi"); + tzMap.put("Pakistan Standard Time", "Asia/Karachi"); + tzMap.put("Paraguay Daylight Time", "America/Asuncion"); + tzMap.put("Paraguay Standard Time", "America/Asuncion"); + tzMap.put("Romance Daylight Time", "Europe/Paris"); + tzMap.put("Romance Standard Time", "Europe/Paris"); + tzMap.put("Russia Time Zone 10", "Asia/Srednekolymsk"); + tzMap.put("Russia Time Zone 11", "Asia/Kamchatka"); + tzMap.put("Russia Time Zone 3", "Europe/Samara"); + tzMap.put("Russian Daylight Time", "Europe/Moscow"); + tzMap.put("Russian Standard Time", "Europe/Moscow"); + tzMap.put("SA Eastern Daylight Time", "America/Cayenne"); + tzMap.put("SA Eastern Standard Time", "America/Cayenne"); + tzMap.put("SA Pacific Daylight Time", "America/Bogota"); + tzMap.put("SA Pacific Standard Time", "America/Bogota"); + tzMap.put("SA Western Daylight Time", "America/La_Paz"); + tzMap.put("SA Western Standard Time", "America/La_Paz"); + tzMap.put("SE Asia Daylight Time", "Asia/Bangkok"); + tzMap.put("SE Asia Standard Time", "Asia/Bangkok"); + tzMap.put("Samoa Daylight Time", "Pacific/Apia"); + tzMap.put("Samoa Standard Time", "Pacific/Apia"); + tzMap.put("Singapore Daylight Time", "Asia/Singapore"); + tzMap.put("Singapore Standard Time", "Asia/Singapore"); + tzMap.put("South Africa Daylight Time", "Africa/Johannesburg"); + tzMap.put("South Africa Standard Time", "Africa/Johannesburg"); + tzMap.put("Sri Lanka Daylight Time", "Asia/Colombo"); + tzMap.put("Sri Lanka Standard Time", "Asia/Colombo"); + tzMap.put("Syria Daylight Time", "Asia/Damascus"); + tzMap.put("Syria Standard Time", "Asia/Damascus"); + tzMap.put("Taipei Daylight Time", "Asia/Taipei"); + tzMap.put("Taipei Standard Time", "Asia/Taipei"); + tzMap.put("Tasmania Daylight Time", "Australia/Hobart"); + tzMap.put("Tasmania Standard Time", "Australia/Hobart"); + tzMap.put("Tokyo Daylight Time", "Asia/Tokyo"); + tzMap.put("Tokyo Standard Time", "Asia/Tokyo"); + tzMap.put("Tonga Daylight Time", "Pacific/Tongatapu"); + tzMap.put("Tonga Standard Time", "Pacific/Tongatapu"); + tzMap.put("Turkey Daylight Time", "Europe/Istanbul"); + tzMap.put("Turkey Standard Time", "Europe/Istanbul"); + tzMap.put("US Eastern Daylight Time", "America/Indianapolis"); + tzMap.put("US Eastern Standard Time", "America/Indianapolis"); + tzMap.put("US Mountain Daylight Time", "America/Phoenix"); + tzMap.put("US Mountain Standard Time", "America/Phoenix"); + tzMap.put("UTC", "Etc/GMT"); + tzMap.put("UTC+12", "Etc/GMT-12"); + tzMap.put("UTC-02", "Etc/GMT+2"); + tzMap.put("UTC-11", "Etc/GMT+11"); + tzMap.put("Ulaanbaatar Daylight Time", "Asia/Ulaanbaatar"); + tzMap.put("Ulaanbaatar Standard Time", "Asia/Ulaanbaatar"); + tzMap.put("Venezuela Daylight Time", "America/Caracas"); + tzMap.put("Venezuela Standard Time", "America/Caracas"); + tzMap.put("Vladivostok Daylight Time", "Asia/Vladivostok"); + tzMap.put("Vladivostok Standard Time", "Asia/Vladivostok"); + tzMap.put("W. Australia Daylight Time", "Australia/Perth"); + tzMap.put("W. Australia Standard Time", "Australia/Perth"); + tzMap.put("W. Central Africa Daylight Time", "Africa/Lagos"); + tzMap.put("W. Central Africa Standard Time", "Africa/Lagos"); + tzMap.put("W. Europe Daylight Time", "Europe/Berlin"); + tzMap.put("W. Europe Standard Time", "Europe/Berlin"); + tzMap.put("West Asia Daylight Time", "Asia/Tashkent"); + tzMap.put("West Asia Standard Time", "Asia/Tashkent"); + tzMap.put("West Pacific Daylight Time", "Pacific/Port_Moresby"); + tzMap.put("West Pacific Standard Time", "Pacific/Port_Moresby"); + tzMap.put("Yakutsk Daylight Time", "Asia/Yakutsk"); + tzMap.put("Yakutsk Standard Time", "Asia/Yakutsk"); + + // Linked Time Zones alias: + tzMap.put("Africa/Addis_Ababa", "Africa/Nairobi"); + tzMap.put("Africa/Asmara", "Africa/Nairobi"); + tzMap.put("Africa/Asmera", "Africa/Nairobi"); + tzMap.put("Africa/Bamako", "Africa/Abidjan"); + tzMap.put("Africa/Bangui", "Africa/Lagos"); + tzMap.put("Africa/Banjul", "Africa/Abidjan"); + tzMap.put("Africa/Blantyre", "Africa/Maputo"); + tzMap.put("Africa/Brazzaville", "Africa/Lagos"); + tzMap.put("Africa/Bujumbura", "Africa/Maputo"); + tzMap.put("Africa/Conakry", "Africa/Abidjan"); + tzMap.put("Africa/Dakar", "Africa/Abidjan"); + tzMap.put("Africa/Dar_es_Salaam", "Africa/Nairobi"); + tzMap.put("Africa/Djibouti", "Africa/Nairobi"); + tzMap.put("Africa/Douala", "Africa/Lagos"); + tzMap.put("Africa/Freetown", "Africa/Abidjan"); + tzMap.put("Africa/Gaborone", "Africa/Maputo"); + tzMap.put("Africa/Harare", "Africa/Maputo"); + tzMap.put("Africa/Juba", "Africa/Khartoum"); + tzMap.put("Africa/Kampala", "Africa/Nairobi"); + tzMap.put("Africa/Kigali", "Africa/Maputo"); + tzMap.put("Africa/Kinshasa", "Africa/Lagos"); + tzMap.put("Africa/Libreville", "Africa/Lagos"); + tzMap.put("Africa/Lome", "Africa/Abidjan"); + tzMap.put("Africa/Luanda", "Africa/Lagos"); + tzMap.put("Africa/Lubumbashi", "Africa/Maputo"); + tzMap.put("Africa/Lusaka", "Africa/Maputo"); + tzMap.put("Africa/Malabo", "Africa/Lagos"); + tzMap.put("Africa/Maseru", "Africa/Johannesburg"); + tzMap.put("Africa/Mbabane", "Africa/Johannesburg"); + tzMap.put("Africa/Mogadishu", "Africa/Nairobi"); + tzMap.put("Africa/Niamey", "Africa/Lagos"); + tzMap.put("Africa/Nouakchott", "Africa/Abidjan"); + tzMap.put("Africa/Ouagadougou", "Africa/Abidjan"); + tzMap.put("Africa/Porto-Novo", "Africa/Lagos"); + tzMap.put("Africa/Sao_Tome", "Africa/Abidjan"); + tzMap.put("Africa/Timbuktu", "Africa/Abidjan"); + tzMap.put("America/Anguilla", "America/Port_of_Spain"); + tzMap.put("America/Antigua", "America/Port_of_Spain"); + tzMap.put("America/Argentina/ComodRivadavia", "America/Argentina/Catamarca"); + tzMap.put("America/Aruba", "America/Curacao"); + tzMap.put("America/Atka", "America/Adak"); + tzMap.put("America/Buenos_Aires", "America/Argentina/Buenos_Aires"); + tzMap.put("America/Catamarca", "America/Argentina/Catamarca"); + tzMap.put("America/Coral_Harbour", "America/Atikokan"); + tzMap.put("America/Cordoba", "America/Argentina/Cordoba"); + tzMap.put("America/Dominica", "America/Port_of_Spain"); + tzMap.put("America/Ensenada", "America/Tijuana"); + tzMap.put("America/Fort_Wayne", "America/Indiana/Indianapolis"); + tzMap.put("America/Grenada", "America/Port_of_Spain"); + tzMap.put("America/Guadeloupe", "America/Port_of_Spain"); + tzMap.put("America/Indianapolis", "America/Indiana/Indianapolis"); + tzMap.put("America/Jujuy", "America/Argentina/Jujuy"); + tzMap.put("America/Knox_IN", "America/Indiana/Knox"); + tzMap.put("America/Kralendijk", "America/Curacao"); + tzMap.put("America/Louisville", "America/Kentucky/Louisville"); + tzMap.put("America/Lower_Princes", "America/Curacao"); + tzMap.put("America/Marigot", "America/Port_of_Spain"); + tzMap.put("America/Mendoza", "America/Argentina/Mendoza"); + tzMap.put("America/Montreal", "America/Toronto"); + tzMap.put("America/Montserrat", "America/Port_of_Spain"); + tzMap.put("America/Porto_Acre", "America/Rio_Branco"); + tzMap.put("America/Rosario", "America/Argentina/Cordoba"); + tzMap.put("America/Shiprock", "America/Denver"); + tzMap.put("America/St_Barthelemy", "America/Port_of_Spain"); + tzMap.put("America/St_Kitts", "America/Port_of_Spain"); + tzMap.put("America/St_Lucia", "America/Port_of_Spain"); + tzMap.put("America/St_Thomas", "America/Port_of_Spain"); + tzMap.put("America/St_Vincent", "America/Port_of_Spain"); + tzMap.put("America/Tortola", "America/Port_of_Spain"); + tzMap.put("America/Virgin", "America/Port_of_Spain"); + tzMap.put("Antarctica/McMurdo", "Pacific/Auckland"); + tzMap.put("Antarctica/South_Pole", "Pacific/Auckland"); + tzMap.put("Arctic/Longyearbyen", "Europe/Oslo"); + tzMap.put("Asia/Aden", "Asia/Riyadh"); + tzMap.put("Asia/Ashkhabad", "Asia/Ashgabat"); + tzMap.put("Asia/Bahrain", "Asia/Qatar"); + tzMap.put("Asia/Calcutta", "Asia/Kolkata"); + tzMap.put("Asia/Chongqing", "Asia/Shanghai"); + tzMap.put("Asia/Chungking", "Asia/Shanghai"); + tzMap.put("Asia/Dacca", "Asia/Dhaka"); + tzMap.put("Asia/Harbin", "Asia/Shanghai"); + tzMap.put("Asia/Istanbul", "Europe/Istanbul"); + tzMap.put("Asia/Kashgar", "Asia/Urumqi"); + tzMap.put("Asia/Katmandu", "Asia/Kathmandu"); + tzMap.put("Asia/Kuwait", "Asia/Riyadh"); + tzMap.put("Asia/Macao", "Asia/Macau"); + tzMap.put("Asia/Muscat", "Asia/Dubai"); + tzMap.put("Asia/Phnom_Penh", "Asia/Bangkok"); + tzMap.put("Asia/Saigon", "Asia/Ho_Chi_Minh"); + tzMap.put("Asia/Tel_Aviv", "Asia/Jerusalem"); + tzMap.put("Asia/Thimbu", "Asia/Thimphu"); + tzMap.put("Asia/Ujung_Pandang", "Asia/Makassar"); + tzMap.put("Asia/Ulan_Bator", "Asia/Ulaanbaatar"); + tzMap.put("Asia/Vientiane", "Asia/Bangkok"); + tzMap.put("Atlantic/Faeroe", "Atlantic/Faroe"); + tzMap.put("Atlantic/Jan_Mayen", "Europe/Oslo"); + tzMap.put("Atlantic/St_Helena", "Africa/Abidjan"); + tzMap.put("Australia/ACT", "Australia/Sydney"); + tzMap.put("Australia/Canberra", "Australia/Sydney"); + tzMap.put("Australia/LHI", "Australia/Lord_Howe"); + tzMap.put("Australia/NSW", "Australia/Sydney"); + tzMap.put("Australia/North", "Australia/Darwin"); + tzMap.put("Australia/Queensland", "Australia/Brisbane"); + tzMap.put("Australia/South", "Australia/Adelaide"); + tzMap.put("Australia/Tasmania", "Australia/Hobart"); + tzMap.put("Australia/Victoria", "Australia/Melbourne"); + tzMap.put("Australia/West", "Australia/Perth"); + tzMap.put("Australia/Yancowinna", "Australia/Broken_Hill"); + tzMap.put("Brazil/Acre", "America/Rio_Branco"); + tzMap.put("Brazil/DeNoronha", "America/Noronha"); + tzMap.put("Brazil/East", "America/Sao_Paulo"); + tzMap.put("Brazil/West", "America/Manaus"); + tzMap.put("Canada/Atlantic", "America/Halifax"); + tzMap.put("Canada/Central", "America/Winnipeg"); + tzMap.put("Canada/East-Saskatchewan", "America/Regina"); + tzMap.put("Canada/Eastern", "America/Toronto"); + tzMap.put("Canada/Mountain", "America/Edmonton"); + tzMap.put("Canada/Newfoundland", "America/St_Johns"); + tzMap.put("Canada/Pacific", "America/Vancouver"); + tzMap.put("Canada/Saskatchewan", "America/Regina"); + tzMap.put("Canada/Yukon", "America/Whitehorse"); + tzMap.put("Chile/Continental", "America/Santiago"); + tzMap.put("Chile/EasterIsland", "Pacific/Easter"); + tzMap.put("Cuba", "America/Havana"); + tzMap.put("Egypt", "Africa/Cairo"); + tzMap.put("Eire", "Europe/Dublin"); + tzMap.put("Europe/Belfast", "Europe/London"); + tzMap.put("Europe/Bratislava", "Europe/Prague"); + tzMap.put("Europe/Busingen", "Europe/Zurich"); + tzMap.put("Europe/Guernsey", "Europe/London"); + tzMap.put("Europe/Isle_of_Man", "Europe/London"); + tzMap.put("Europe/Jersey", "Europe/London"); + tzMap.put("Europe/Ljubljana", "Europe/Belgrade"); + tzMap.put("Europe/Mariehamn", "Europe/Helsinki"); + tzMap.put("Europe/Nicosia", "Asia/Nicosia"); + tzMap.put("Europe/Podgorica", "Europe/Belgrade"); + tzMap.put("Europe/San_Marino", "Europe/Rome"); + tzMap.put("Europe/Sarajevo", "Europe/Belgrade"); + tzMap.put("Europe/Skopje", "Europe/Belgrade"); + tzMap.put("Europe/Tiraspol", "Europe/Chisinau"); + tzMap.put("Europe/Vaduz", "Europe/Zurich"); + tzMap.put("Europe/Vatican", "Europe/Rome"); + tzMap.put("Europe/Zagreb", "Europe/Belgrade"); + tzMap.put("GB", "Europe/London"); + tzMap.put("GB-Eire", "Europe/London"); + tzMap.put("GMT+0", "Etc/GMT"); + tzMap.put("GMT-0", "Etc/GMT"); + tzMap.put("GMT0", "Etc/GMT"); + tzMap.put("Greenwich", "Etc/GMT"); + tzMap.put("Hongkong", "Asia/Hong_Kong"); + tzMap.put("Iceland", "Atlantic/Reykjavik"); + tzMap.put("Indian/Antananarivo", "Africa/Nairobi"); + tzMap.put("Indian/Comoro", "Africa/Nairobi"); + tzMap.put("Indian/Mayotte", "Africa/Nairobi"); + tzMap.put("Iran", "Asia/Tehran"); + tzMap.put("Israel", "Asia/Jerusalem"); + tzMap.put("Jamaica", "America/Jamaica"); + tzMap.put("Japan", "Asia/Tokyo"); + tzMap.put("Kwajalein", "Pacific/Kwajalein"); + tzMap.put("Libya", "Africa/Tripoli"); + tzMap.put("Mexico/BajaNorte", "America/Tijuana"); + tzMap.put("Mexico/BajaSur", "America/Mazatlan"); + tzMap.put("Mexico/General", "America/Mexico_City"); + tzMap.put("NZ", "Pacific/Auckland"); + tzMap.put("NZ-CHAT", "Pacific/Chatham"); + tzMap.put("Navajo", "America/Denver"); + tzMap.put("PRC", "Asia/Shanghai"); + tzMap.put("Pacific/Johnston", "Pacific/Honolulu"); + tzMap.put("Pacific/Midway", "Pacific/Pago_Pago"); + tzMap.put("Pacific/Ponape", "Pacific/Pohnpei"); + tzMap.put("Pacific/Saipan", "Pacific/Guam"); + tzMap.put("Pacific/Samoa", "Pacific/Pago_Pago"); + tzMap.put("Pacific/Truk", "Pacific/Chuuk"); + tzMap.put("Pacific/Yap", "Pacific/Chuuk"); + tzMap.put("Poland", "Europe/Warsaw"); + tzMap.put("Portugal", "Europe/Lisbon"); + tzMap.put("ROC", "Asia/Taipei"); + tzMap.put("ROK", "Asia/Seoul"); + tzMap.put("Singapore", "Asia/Singapore"); + tzMap.put("Turkey", "Europe/Istanbul"); + tzMap.put("UCT", "Etc/UCT"); + tzMap.put("US/Alaska", "America/Anchorage"); + tzMap.put("US/Aleutian", "America/Adak"); + tzMap.put("US/Arizona", "America/Phoenix"); + tzMap.put("US/Central", "America/Chicago"); + tzMap.put("US/East-Indiana", "America/Indiana/Indianapolis"); + tzMap.put("US/Eastern", "America/New_York"); + tzMap.put("US/Hawaii", "Pacific/Honolulu"); + tzMap.put("US/Indiana-Starke", "America/Indiana/Knox"); + tzMap.put("US/Michigan", "America/Detroit"); + tzMap.put("US/Mountain", "America/Denver"); + tzMap.put("US/Pacific", "America/Los_Angeles"); + tzMap.put("US/Pacific-New", "America/Los_Angeles"); + tzMap.put("US/Samoa", "Pacific/Pago_Pago"); + tzMap.put("Universal", "Etc/UTC"); + tzMap.put("W-SU", "Europe/Moscow"); + tzMap.put("Zulu", "Etc/UTC"); + + // Standard (IANA) abbreviations: + tzMap.put("ACWST", "Australia/Eucla"); + tzMap.put("AFT", "Asia/Kabul"); + tzMap.put("ALMT", "Asia/Almaty"); + tzMap.put("ANAT", "Asia/Anadyr"); + tzMap.put("AZOST", "Atlantic/Azores"); + tzMap.put("AZOT", "Atlantic/Azores"); + tzMap.put("AZST", "Asia/Baku"); + tzMap.put("AZT", "Asia/Baku"); + tzMap.put("BDT", "Asia/Dhaka"); + tzMap.put("BNT", "Asia/Brunei"); + tzMap.put("BOT", "America/La_Paz"); + tzMap.put("BRST", "America/Sao_Paulo"); + tzMap.put("BTT", "Asia/Thimphu"); + tzMap.put("CAT", "Africa/Maputo"); + tzMap.put("CCT", "Indian/Cocos"); + tzMap.put("CHADT", "Pacific/Chatham"); + tzMap.put("CHAST", "Pacific/Chatham"); + tzMap.put("CHOST", "Asia/Choibalsan"); + tzMap.put("CHOT", "Asia/Choibalsan"); + tzMap.put("CHUT", "Pacific/Chuuk"); + tzMap.put("CKT", "Pacific/Rarotonga"); + tzMap.put("COT", "America/Bogota"); + tzMap.put("CVT", "Atlantic/Cape_Verde"); + tzMap.put("CXT", "Indian/Christmas"); + tzMap.put("ChST", "Pacific/Guam"); + tzMap.put("DAVT", "Antarctica/Davis"); + tzMap.put("DDUT", "Antarctica/DumontDUrville"); + tzMap.put("EAST", "Pacific/Easter"); + tzMap.put("ECT", "America/Guayaquil"); + tzMap.put("EGST", "America/Scoresbysund"); + tzMap.put("EGT", "America/Scoresbysund"); + tzMap.put("FJST", "Pacific/Fiji"); + tzMap.put("FJT", "Pacific/Fiji"); + tzMap.put("FKST", "Atlantic/Stanley"); + tzMap.put("FNT", "America/Noronha"); + tzMap.put("GALT", "Pacific/Galapagos"); + tzMap.put("GAMT", "Pacific/Gambier"); + tzMap.put("GET", "Asia/Tbilisi"); + tzMap.put("GFT", "America/Cayenne"); + tzMap.put("GILT", "Pacific/Tarawa"); + tzMap.put("GYT", "America/Guyana"); + tzMap.put("HDT", "America/Adak"); + tzMap.put("HKT", "Asia/Hong_Kong"); + tzMap.put("HOVST", "Asia/Hovd"); + tzMap.put("HOVT", "Asia/Hovd"); + tzMap.put("IDT", "Asia/Jerusalem"); + tzMap.put("IOT", "Indian/Chagos"); + tzMap.put("IRST", "Asia/Tehran"); + tzMap.put("JST", "Asia/Tokyo"); + tzMap.put("KGT", "Asia/Bishkek"); + tzMap.put("KOST", "Pacific/Kosrae"); + tzMap.put("LHDT", "Australia/Lord_Howe"); + tzMap.put("LHST", "Australia/Lord_Howe"); + tzMap.put("LINT", "Pacific/Kiritimati"); + tzMap.put("MAGT", "Asia/Magadan"); + tzMap.put("MART", "Pacific/Marquesas"); + tzMap.put("MAWT", "Antarctica/Mawson"); + tzMap.put("MEST", "MET"); + tzMap.put("MET", "MET"); + tzMap.put("MIST", "Antarctica/Macquarie"); + tzMap.put("MMT", "Asia/Rangoon"); + tzMap.put("MUT", "Indian/Mauritius"); + tzMap.put("MVT", "Indian/Maldives"); + tzMap.put("NCT", "Pacific/Noumea"); + tzMap.put("NDT", "America/St_Johns"); + tzMap.put("NFT", "Pacific/Norfolk"); + tzMap.put("NOVT", "Asia/Novosibirsk"); + tzMap.put("NPT", "Asia/Kathmandu"); + tzMap.put("NRT", "Pacific/Nauru"); + tzMap.put("NST", "America/St_Johns"); + tzMap.put("NUT", "Pacific/Niue"); + tzMap.put("NZDT", "Pacific/Auckland"); + tzMap.put("NZST", "Pacific/Auckland"); + tzMap.put("OMST", "Asia/Omsk"); + tzMap.put("ORAT", "Asia/Oral"); + tzMap.put("PET", "America/Lima"); + tzMap.put("PETT", "Asia/Kamchatka"); + tzMap.put("PGT", "Pacific/Port_Moresby"); + tzMap.put("PHOT", "Pacific/Enderbury"); + tzMap.put("PHT", "Asia/Manila"); + tzMap.put("PKT", "Asia/Karachi"); + tzMap.put("PMDT", "America/Miquelon"); + tzMap.put("PMST", "America/Miquelon"); + tzMap.put("PONT", "Pacific/Pohnpei"); + tzMap.put("PWT", "Pacific/Palau"); + tzMap.put("PYST", "America/Asuncion"); + tzMap.put("PYT", "America/Asuncion"); + tzMap.put("QYZT", "Asia/Qyzylorda"); + tzMap.put("RET", "Indian/Reunion"); + tzMap.put("ROTT", "Antarctica/Rothera"); + tzMap.put("SAKT", "Asia/Sakhalin"); + tzMap.put("SAMT", "Europe/Samara"); + tzMap.put("SAST", "Africa/Johannesburg"); + tzMap.put("SBT", "Pacific/Guadalcanal"); + tzMap.put("SCT", "Indian/Mahe"); + tzMap.put("SGT", "Asia/Singapore"); + tzMap.put("SRET", "Asia/Srednekolymsk"); + tzMap.put("SRT", "America/Paramaribo"); + tzMap.put("SST", "Pacific/Pago_Pago"); + tzMap.put("SYOT", "Antarctica/Syowa"); + tzMap.put("TAHT", "Pacific/Tahiti"); + tzMap.put("TFT", "Indian/Kerguelen"); + tzMap.put("TJT", "Asia/Dushanbe"); + tzMap.put("TKT", "Pacific/Fakaofo"); + tzMap.put("TLT", "Asia/Dili"); + tzMap.put("TMT", "Asia/Ashgabat"); + tzMap.put("TOT", "Pacific/Tongatapu"); + tzMap.put("TVT", "Pacific/Funafuti"); + tzMap.put("ULAST", "Asia/Ulaanbaatar"); + tzMap.put("ULAT", "Asia/Ulaanbaatar"); + tzMap.put("UYT", "America/Montevideo"); + tzMap.put("VET", "America/Caracas"); + tzMap.put("VOST", "Antarctica/Vostok"); + tzMap.put("VUT", "Pacific/Efate"); + tzMap.put("WAKT", "Pacific/Wake"); + tzMap.put("WAST", "Africa/Windhoek"); + tzMap.put("WFT", "Pacific/Wallis"); + tzMap.put("WGST", "America/Godthab"); + tzMap.put("WGT", "America/Godthab"); + tzMap.put("WIT", "Asia/Jayapura"); + tzMap.put("WITA", "Asia/Makassar"); + tzMap.put("WSDT", "Pacific/Apia"); + tzMap.put("WSST", "Pacific/Apia"); + tzMap.put("XJT", "Asia/Urumqi"); + tzMap.put("YEKT", "Asia/Yekaterinburg"); + + // GENERATED CODE ENDS HERE + + for (String key : tzMap.keySet()) { + assertEquals("Custom time Zone '" + key + "' mapping", tzMap.get(key), TimeUtil.getCanonicalTimezone(key, null)); + } + + for (String tz : TimeZone.getAvailableIDs()) { + String canonicalTZ; + try { + canonicalTZ = TimeUtil.getCanonicalTimezone(tz, null); + } catch (SQLException e) { + canonicalTZ = null; + } + assertNotNull("System Time Zone '" + tz + "' mapping missing", canonicalTZ); + } + } + + /** + * Tests fix for BUG#70436 - Incorrect mapping of windows timezone to Olson timezone. + * + * @throws Exception + * if the test fails. + */ + public void testBug70436() throws Exception { + assertEquals("Asia/Yerevan", TimeUtil.getCanonicalTimezone("Caucasus Standard Time", null)); + assertEquals("Asia/Tbilisi", TimeUtil.getCanonicalTimezone("Georgian Standard Time", null)); + } + + /** + * Tests fix for Bug#82115 - Some exceptions are intercepted twice or fail to set the init cause. + */ + public void testBug82115() throws Exception { + Exception ex = SQLError.createSQLException("ORIGINAL_EXCEPTION", "0", new Exception("ORIGINAL_CAUSE"), null, null); + assertEquals("ORIGINAL_EXCEPTION", ex.getMessage()); + assertEquals("ORIGINAL_CAUSE", ex.getCause().getMessage()); + + ex = SQLError.createSQLException("ORIGINAL_EXCEPTION", "0", new Exception("ORIGINAL_CAUSE"), new ExceptionInterceptor() { + boolean alreadyIntercepted = false; + + public void init(Connection conn, Properties props) throws SQLException { + this.alreadyIntercepted = false; + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, Connection conn) { + assertFalse(this.alreadyIntercepted); + this.alreadyIntercepted = true; + + assertEquals("ORIGINAL_EXCEPTION", sqlEx.getMessage()); + assertEquals("ORIGINAL_CAUSE", sqlEx.getCause().getMessage()); + + SQLException newSqlEx = new SQLException("INTERCEPT_EXCEPTION"); + return newSqlEx; + } + }, null); + assertEquals("INTERCEPT_EXCEPTION", ex.getMessage()); + assertNull(ex.getCause()); + + ex = SQLError.createSQLException("ORIGINAL_EXCEPTION", "0", new Exception("ORIGINAL_CAUSE"), new ExceptionInterceptor() { + boolean alreadyIntercepted = false; + + public void init(Connection conn, Properties props) throws SQLException { + this.alreadyIntercepted = false; + } + + public void destroy() { + } + + public SQLException interceptException(SQLException sqlEx, Connection conn) { + assertFalse(this.alreadyIntercepted); + this.alreadyIntercepted = true; + + assertEquals("ORIGINAL_EXCEPTION", sqlEx.getMessage()); + assertEquals("ORIGINAL_CAUSE", sqlEx.getCause().getMessage()); + + SQLException newSqlEx = new SQLException("INTERCEPT_EXCEPTION"); + newSqlEx.initCause(new Exception("INTERCEPT_CAUSE")); + return newSqlEx; + } + }, null); + assertEquals("INTERCEPT_EXCEPTION", ex.getMessage()); + assertEquals("INTERCEPT_CAUSE", ex.getCause().getMessage()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java new file mode 100644 index 0000000..c2321b0 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ConnectionRegressionTest.java @@ -0,0 +1,293 @@ +/* + Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression.jdbc4; + +import java.io.ObjectInputStream.GetField; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLNonTransientException; +import java.sql.SQLTransientException; +import java.sql.Statement; +import java.util.Properties; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.Messages; +import com.mysql.jdbc.MysqlErrorNumbers; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.Util; + +import javax.sql.PooledConnection; + +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; +import com.mysql.jdbc.jdbc2.optional.JDBC4MysqlPooledConnection; +import com.mysql.jdbc.jdbc2.optional.JDBC4MysqlXAConnection; +import com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection; + +import testsuite.BaseTestCase; +import testsuite.regression.ConnectionRegressionTest.Bug72712StatementInterceptor; +import testsuite.regression.ConnectionRegressionTest.Bug75168LoadBalanceExceptionChecker; + +public class ConnectionRegressionTest extends BaseTestCase { + /** + * Creates a new ConnectionRegressionTest. + * + * @param name + * the name of the test + */ + public ConnectionRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ConnectionRegressionTest.class); + } + + /** + * Tests fix for Bug#75168 - loadBalanceExceptionChecker interface cannot work using JDBC4/JDK7 + * + * Bug observed only with JDBC4 classes. This test is duplicated in testsuite.regression.ConnectionRegressionTest#testBug75168(). + * The two nested static classes, Bug75168LoadBalanceExceptionChecker and Bug75168StatementInterceptor are shared between the two tests. + * + * @throws Exception + */ + public void testBug75168() throws Exception { + final Properties props = new Properties(); + props.setProperty("loadBalanceExceptionChecker", Bug75168LoadBalanceExceptionChecker.class.getName()); + props.setProperty("statementInterceptors", testsuite.regression.ConnectionRegressionTest.Bug75168StatementInterceptor.class.getName()); + + Connection connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers + for (int i = 0; i < 3; i++) { + Statement stmtTest = null; + try { + stmtTest = connTest.createStatement(); + stmtTest.execute("SELECT * FROM nonexistent_table"); + fail("'Table doesn't exist' exception was expected."); + } catch (SQLException e) { + assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); + } finally { + if (stmtTest != null) { + stmtTest.close(); + } + } + } + connTest.close(); + + boolean stop = false; + do { + connTest = getLoadBalancedConnection(2, null, props); // get a load balancing connection with two default servers + for (int i = 0; i < 3; i++) { + PreparedStatement pstmtTest = null; + try { + pstmtTest = connTest.prepareStatement("SELECT * FROM nonexistent_table"); + pstmtTest.execute(); + fail("'Table doesn't exist' exception was expected."); + } catch (SQLException e) { + assertTrue("'Table doesn't exist' exception was expected.", e.getMessage().endsWith("nonexistent_table' doesn't exist")); + } finally { + if (pstmtTest != null) { + pstmtTest.close(); + } + } + } + connTest.close(); + + // do it again with server prepared statements + props.setProperty("useServerPrepStmts", "true"); + } while (stop = !stop); + } + + /** + * Tests fix for BUG#20685022 - SSL CONNECTION TO MYSQL 5.7.6 COMMUNITY SERVER FAILS + * + * This test is duplicated in testuite.regression.ConnectionRegressionTest.testBug20685022(). + * + * @throws Exception + * if the test fails. + */ + public void testBug20685022() throws Exception { + if (!isCommunityEdition()) { + return; + } + + final Properties props = new Properties(); + + /* + * case 1: non verifying server certificate + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "false"); + + getConnectionWithProps(props); + + /* + * case 2: verifying server certificate using key store provided by connection properties + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "true"); + props.setProperty("trustCertificateKeyStoreUrl", "file:src/testsuite/ssl-test-certs/test-cert-store"); + props.setProperty("trustCertificateKeyStoreType", "JKS"); + props.setProperty("trustCertificateKeyStorePassword", "password"); + + getConnectionWithProps(props); + + /* + * case 3: verifying server certificate using key store provided by system properties + */ + props.clear(); + props.setProperty("useSSL", "true"); + props.setProperty("requireSSL", "true"); + props.setProperty("verifyServerCertificate", "true"); + + String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store"; + System.setProperty("javax.net.ssl.keyStore", trustStorePath); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStore", trustStorePath); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + + getConnectionWithProps(props); + } + + /** + * Tests fix for BUG#62452 - NPE thrown in JDBC4MySQLPooledException when statement is closed. + * + * @throws Exception + */ + public void testBug62452() throws Exception { + PooledConnection con = null; + + MysqlConnectionPoolDataSource pds = new MysqlConnectionPoolDataSource(); + pds.setUrl(dbUrl); + con = pds.getPooledConnection(); + assertTrue(con instanceof JDBC4MysqlPooledConnection); + testBug62452WithConnection(con); + + MysqlXADataSource xads = new MysqlXADataSource(); + xads.setUrl(dbUrl); + + xads.setPinGlobalTxToPhysicalConnection(false); + con = xads.getXAConnection(); + assertTrue(con instanceof JDBC4MysqlXAConnection); + testBug62452WithConnection(con); + + xads.setPinGlobalTxToPhysicalConnection(true); + con = xads.getXAConnection(); + assertTrue(con instanceof JDBC4SuspendableXAConnection); + testBug62452WithConnection(con); + + } + + private void testBug62452WithConnection(PooledConnection con) throws Exception { + this.pstmt = con.getConnection().prepareStatement("SELECT 1"); + this.rs = this.pstmt.executeQuery(); + con.close(); + + // If PooledConnection is already closed by some reason a NullPointerException was thrown on the next line + // because the closed connection has nulled out the list that it synchronises on when the closed event is fired. + this.pstmt.close(); + } + + /** + * Tests fix for Bug#16634180 - LOCK WAIT TIMEOUT EXCEEDED CAUSES SQLEXCEPTION, SHOULD CAUSE SQLTRANSIENTEXCEPTION + * + * @throws Exception + * if the test fails. + */ + public void testBug16634180() throws Exception { + + createTable("testBug16634180", "(pk integer primary key, val integer)", "InnoDB"); + this.stmt.executeUpdate("insert into testBug16634180 values(0,0)"); + + Connection c1 = null; + Connection c2 = null; + + try { + c1 = getConnectionWithProps(new Properties()); + c1.setAutoCommit(false); + Statement s1 = c1.createStatement(); + s1.executeUpdate("update testBug16634180 set val=val+1 where pk=0"); + + c2 = getConnectionWithProps(new Properties()); + c2.setAutoCommit(false); + Statement s2 = c2.createStatement(); + try { + s2.executeUpdate("update testBug16634180 set val=val+1 where pk=0"); + fail("ER_LOCK_WAIT_TIMEOUT should be thrown."); + } catch (SQLTransientException ex) { + assertEquals(MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT, ex.getErrorCode()); + assertEquals(SQLError.SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE, ex.getSQLState()); + assertEquals("Lock wait timeout exceeded; try restarting transaction", ex.getMessage()); + } + } finally { + if (c1 != null) { + c1.close(); + } + if (c2 != null) { + c2.close(); + } + } + } + + /** + * + * @throws Exception + */ + public void testBug56122() throws Exception { + for (final Connection testConn : new Connection[] { this.conn, getFailoverConnection(), getLoadBalancedConnection(), + getMasterSlaveReplicationConnection() }) { + testConn.createClob(); + testConn.createBlob(); + testConn.createNClob(); + testConn.createSQLXML(); + testConn.isValid(12345); + testConn.setClientInfo(new Properties()); + testConn.setClientInfo("NAME", "VALUE"); + testConn.getClientInfo(); + testConn.getClientInfo("CLIENT"); + assertThrows(SQLFeatureNotSupportedException.class, new Callable() { + public Void call() throws Exception { + testConn.createArrayOf("A_TYPE", null); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, new Callable() { + public Void call() throws Exception { + testConn.createStruct("A_TYPE", null); + return null; + } + }); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ExceptionSubclassesTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ExceptionSubclassesTest.java new file mode 100644 index 0000000..247f210 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/ExceptionSubclassesTest.java @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression.jdbc4; + +import com.mysql.jdbc.SQLError; + +import testsuite.BaseTestCase; + +public class ExceptionSubclassesTest extends BaseTestCase { + /** + * Creates a new ExceptionSubclassesTest. + * + * @param name + * the name of the test + */ + public ExceptionSubclassesTest(String name) { + super(name); + } + + public void testBug17750877() throws Exception { + + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLTransientConnectionException", + SQLError.createSQLException("test", "08000", 0, true, null).getClass().getCanonicalName()); + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException", + SQLError.createSQLException("test", "08000", 0, false, null).getClass().getCanonicalName()); + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException", + SQLError.createSQLException("test", "42000", null).getClass().getCanonicalName()); + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException", + SQLError.createSQLException("test", "23000", null).getClass().getCanonicalName()); + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException", + SQLError.createSQLException("test", "40000", null).getClass().getCanonicalName()); + assertEquals("com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException", + SQLError.createSQLException("test", "70100", null).getClass().getCanonicalName()); + + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java new file mode 100644 index 0000000..54c7628 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/MetaDataRegressionTest.java @@ -0,0 +1,862 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression.jdbc4; + +import java.sql.DatabaseMetaData; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Arrays; +import java.util.List; + +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.Util; + +import testsuite.BaseTestCase; + +public class MetaDataRegressionTest extends BaseTestCase { + /** + * Creates a new MetaDataRegressionTest. + * + * @param name + * the name of the test + */ + public MetaDataRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MetaDataRegressionTest.class); + } + + /** + * Tests fix for BUG#68307 - getFunctionColumns() returns incorrect "COLUMN_TYPE" information. This is a JDBC4 + * feature. + * + * @throws Exception + * if the test fails. + */ + public void testBug68307() throws Exception { + createFunction("testBug68307_func", "(func_param_in INT) RETURNS INT DETERMINISTIC RETURN 1"); + + createProcedure("testBug68307_proc", "(IN proc_param_in INT, OUT proc_param_out INT, INOUT proc_param_inout INT) SELECT 1"); + + // test metadata from MySQL + DatabaseMetaData testDbMetaData = conn.getMetaData(); + checkFunctionColumnTypeForBug68307("MySQL", testDbMetaData); + checkProcedureColumnTypeForBug68307("MySQL", testDbMetaData); + + // test metadata from I__S + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + testDbMetaData = connUseIS.getMetaData(); + checkFunctionColumnTypeForBug68307("I__S", testDbMetaData); + checkProcedureColumnTypeForBug68307("I__S", testDbMetaData); + connUseIS.close(); + } + + private void checkFunctionColumnTypeForBug68307(String testAgainst, DatabaseMetaData testDbMetaData) throws Exception { + rs = testDbMetaData.getFunctionColumns(null, null, "testBug68307_%", "%"); + + while (rs.next()) { + String message = testAgainst + ", function <" + rs.getString("FUNCTION_NAME") + "." + rs.getString("COLUMN_NAME") + ">"; + if (rs.getString("COLUMN_NAME") == null || rs.getString("COLUMN_NAME").length() == 0) { + assertEquals(message, DatabaseMetaData.functionReturn, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_in")) { + assertEquals(message, DatabaseMetaData.functionColumnIn, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_inout")) { + assertEquals(message, DatabaseMetaData.functionColumnInOut, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_out")) { + assertEquals(message, DatabaseMetaData.functionColumnOut, rs.getShort("COLUMN_TYPE")); + } else { + fail("Column '" + rs.getString("FUNCTION_NAME") + "." + rs.getString("COLUMN_NAME") + "' not expected within test case."); + } + } + } + + private void checkProcedureColumnTypeForBug68307(String testAgainst, DatabaseMetaData testDbMetaData) throws Exception { + rs = testDbMetaData.getProcedureColumns(null, null, "testBug68307_%", "%"); + + while (rs.next()) { + String message = testAgainst + ", procedure <" + rs.getString("PROCEDURE_NAME") + "." + rs.getString("COLUMN_NAME") + ">"; + if (rs.getString("COLUMN_NAME") == null || rs.getString("COLUMN_NAME").length() == 0) { + assertEquals(message, DatabaseMetaData.procedureColumnReturn, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_in")) { + assertEquals(message, DatabaseMetaData.procedureColumnIn, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_inout")) { + assertEquals(message, DatabaseMetaData.procedureColumnInOut, rs.getShort("COLUMN_TYPE")); + } else if (rs.getString("COLUMN_NAME").endsWith("_out")) { + assertEquals(message, DatabaseMetaData.procedureColumnOut, rs.getShort("COLUMN_TYPE")); + } else { + fail("Column '" + rs.getString("FUNCTION_NAME") + "." + rs.getString("COLUMN_NAME") + "' not expected within test case."); + } + } + } + + /** + * Tests fix for BUG#44451 - getTables does not return resultset with expected columns. + * + * @throws Exception + * if the test fails. + */ + public void testBug44451() throws Exception { + String methodName; + List expectedFields; + String[] testStepDescription = new String[] { "MySQL MetaData", "I__S MetaData" }; + Connection connUseIS = getConnectionWithProps("useInformationSchema=true"); + Connection[] testConnections = new Connection[] { conn, connUseIS }; + + methodName = "getClientInfoProperties()"; + expectedFields = Arrays.asList("NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + rs = testDbMetaData.getClientInfoProperties(); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, rs); + rs.close(); + } + + methodName = "getFunctions()"; + expectedFields = Arrays.asList("FUNCTION_CAT", "FUNCTION_SCHEM", "FUNCTION_NAME", "REMARKS", "FUNCTION_TYPE", "SPECIFIC_NAME"); + for (int i = 0; i < testStepDescription.length; i++) { + DatabaseMetaData testDbMetaData = testConnections[i].getMetaData(); + rs = testDbMetaData.getFunctions(null, null, "%"); + checkReturnedColumnsForBug44451(testStepDescription[i], methodName, expectedFields, rs); + rs.close(); + } + + connUseIS.close(); + } + + private void checkReturnedColumnsForBug44451(String stepDescription, String methodName, List expectedFields, ResultSet resultSetToCheck) + throws Exception { + ResultSetMetaData rsMetaData = resultSetToCheck.getMetaData(); + int numberOfColumns = rsMetaData.getColumnCount(); + + assertEquals(stepDescription + ", wrong column count in method '" + methodName + "'.", expectedFields.size(), numberOfColumns); + for (int i = 0; i < numberOfColumns; i++) { + int position = i + 1; + assertEquals(stepDescription + ", wrong column at position '" + position + "' in method '" + methodName + "'.", expectedFields.get(i), + rsMetaData.getColumnName(position)); + } + rs.close(); + } + + /** + * Tests fix for BUG#69298 - Different outcome from DatabaseMetaData.getFunctions() when using I__S. + * + * @throws Exception + * if the test fails. + */ + public void testBug69298() throws Exception { + Connection testConn; + + createFunction("testBug69298_func", "(param_func INT) RETURNS INT COMMENT 'testBug69298_func comment' DETERMINISTIC RETURN 1"); + createProcedure("testBug69298_proc", "(IN param_proc INT) COMMENT 'testBug69298_proc comment' SELECT 1"); + + // test with standard connection + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) this.conn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) this.conn).getGetProceduresReturnsFunctions()); + checkGetFunctionsForBug69298("Std. Connection MetaData", this.conn); + checkGetFunctionColumnsForBug69298("Std. Connection MetaData", this.conn); + checkGetProceduresForBug69298("Std. Connection MetaData", this.conn); + checkGetProcedureColumnsForBug69298("Std. Connection MetaData", this.conn); + + // test with property useInformationSchema=true + testConn = getConnectionWithProps("useInformationSchema=true"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetFunctionsForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + checkGetFunctionColumnsForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + checkGetProceduresForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. useInfoSchema(1) MetaData", testConn); + testConn.close(); + + // test with property getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("getProceduresReturnsFunctions=false"); + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetFunctionsForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + checkGetFunctionColumnsForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + checkGetProceduresForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. getProcRetFunc(0) MetaData", testConn); + testConn.close(); + + // test with property useInformationSchema=true & getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("useInformationSchema=true,getProceduresReturnsFunctions=false"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkGetFunctionsForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + checkGetFunctionColumnsForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + checkGetProceduresForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + checkGetProcedureColumnsForBug69298("Prop. useInfoSchema(1) + getProcRetFunc(0) MetaData", testConn); + testConn.close(); + } + + private void checkGetFunctionsForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet functionsMD = testDbMetaData.getFunctions(null, null, "testBug69298_%"); + String sd = stepDescription + " getFunctions() "; + + assertTrue(sd + "one row expected.", functionsMD.next()); + + // function: testBug69298_func + assertEquals(sd + "-> FUNCTION_CAT", testConn.getCatalog(), functionsMD.getString("FUNCTION_CAT")); + assertEquals(sd + "-> FUNCTION_SCHEM", null, functionsMD.getString("FUNCTION_SCHEM")); + assertEquals(sd + "-> FUNCTION_NAME", "testBug69298_func", functionsMD.getString("FUNCTION_NAME")); + assertEquals(sd + "-> REMARKS", "testBug69298_func comment", functionsMD.getString("REMARKS")); + assertEquals(sd + "-> FUNCTION_TYPE", DatabaseMetaData.functionNoTable, functionsMD.getShort("FUNCTION_TYPE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", functionsMD.getString("SPECIFIC_NAME")); + + assertFalse(stepDescription + "no more rows expected.", functionsMD.next()); + } + + private void checkGetFunctionColumnsForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet funcColsMD = testDbMetaData.getFunctionColumns(null, null, "testBug69298_%", "%"); + String sd = stepDescription + " getFunctionColumns() "; + + assertTrue(sd + "1st of 2 rows expected.", funcColsMD.next()); + + // function column: testBug69298_func return + assertEquals(sd + "-> FUNCTION_CAT", testConn.getCatalog(), funcColsMD.getString("FUNCTION_CAT")); + assertEquals(sd + "-> FUNCTION_SCHEM", null, funcColsMD.getString("FUNCTION_SCHEM")); + assertEquals(sd + "-> FUNCTION_NAME", "testBug69298_func", funcColsMD.getString("FUNCTION_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "", funcColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.functionReturn, funcColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, funcColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", funcColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, funcColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, funcColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, funcColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, funcColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.functionNullable, funcColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, funcColsMD.getString("REMARKS")); + assertEquals(sd + "-> CHAR_OCTET_LENGTH", 0, funcColsMD.getInt("CHAR_OCTET_LENGTH")); + assertEquals(sd + "-> ORDINAL_POSITION", 0, funcColsMD.getInt("ORDINAL_POSITION")); + assertEquals(sd + "-> IS_NULLABLE", "YES", funcColsMD.getString("IS_NULLABLE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", funcColsMD.getString("SPECIFIC_NAME")); + + assertTrue(sd + "2nd of 2 rows expected.", funcColsMD.next()); + + // function column: testBug69298_func.param_func + assertEquals(sd + "-> FUNCTION_CAT", testConn.getCatalog(), funcColsMD.getString("FUNCTION_CAT")); + assertEquals(sd + "-> FUNCTION_SCHEM", null, funcColsMD.getString("FUNCTION_SCHEM")); + assertEquals(sd + "-> FUNCTION_NAME", "testBug69298_func", funcColsMD.getString("FUNCTION_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "param_func", funcColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.functionColumnIn, funcColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, funcColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", funcColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, funcColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, funcColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, funcColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, funcColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.functionNullable, funcColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, funcColsMD.getString("REMARKS")); + assertEquals(sd + "-> CHAR_OCTET_LENGTH", 0, funcColsMD.getInt("CHAR_OCTET_LENGTH")); + assertEquals(sd + "-> ORDINAL_POSITION", 1, funcColsMD.getInt("ORDINAL_POSITION")); + assertEquals(sd + "-> IS_NULLABLE", "YES", funcColsMD.getString("IS_NULLABLE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", funcColsMD.getString("SPECIFIC_NAME")); + + assertFalse(sd + "no more rows expected.", funcColsMD.next()); + } + + private void checkGetProceduresForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet proceduresMD = testDbMetaData.getProcedures(null, null, "testBug69298_%"); + String sd = stepDescription + " getProcedures() "; + boolean isGetProceduresReturnsFunctions = ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions(); + + if (isGetProceduresReturnsFunctions) { + assertTrue(sd + "1st of 2 rows expected.", proceduresMD.next()); + + // function: testBug69298_func + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), proceduresMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, proceduresMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", proceduresMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> REMARKS", "testBug69298_func comment", proceduresMD.getString("REMARKS")); + assertEquals(sd + "-> PROCEDURE_TYPE", DatabaseMetaData.procedureReturnsResult, proceduresMD.getShort("PROCEDURE_TYPE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", proceduresMD.getString("SPECIFIC_NAME")); + + assertTrue(sd + "2nd of 2 rows expected.", proceduresMD.next()); + } else { + assertTrue(sd + "one row expected.", proceduresMD.next()); + } + + // procedure: testBug69298_proc + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), proceduresMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, proceduresMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_proc", proceduresMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> REMARKS", "testBug69298_proc comment", proceduresMD.getString("REMARKS")); + assertEquals(sd + "-> PROCEDURE_TYPE", DatabaseMetaData.procedureNoResult, proceduresMD.getShort("PROCEDURE_TYPE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_proc", proceduresMD.getString("SPECIFIC_NAME")); + + assertFalse(stepDescription + "no more rows expected.", proceduresMD.next()); + } + + private void checkGetProcedureColumnsForBug69298(String stepDescription, Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet procColsMD = testDbMetaData.getProcedureColumns(null, null, "testBug69298_%", "%"); + String sd = stepDescription + " getProcedureColumns() "; + boolean isGetProceduresReturnsFunctions = ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions(); + + if (isGetProceduresReturnsFunctions) { + assertTrue(sd + "1st of 3 rows expected.", procColsMD.next()); + + // function column: testBug69298_func return + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnReturn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + assertEquals(sd + "-> COLUMN_DEF", null, procColsMD.getString("COLUMN_DEF")); + assertEquals(sd + "-> SQL_DATA_TYPE", 0, procColsMD.getInt("SQL_DATA_TYPE")); + assertEquals(sd + "-> SQL_DATETIME_SUB", 0, procColsMD.getInt("SQL_DATETIME_SUB")); + assertEquals(sd + "-> CHAR_OCTET_LENGTH", 0, procColsMD.getInt("CHAR_OCTET_LENGTH")); + assertEquals(sd + "-> ORDINAL_POSITION", 0, procColsMD.getInt("ORDINAL_POSITION")); + assertEquals(sd + "-> IS_NULLABLE", "YES", procColsMD.getString("IS_NULLABLE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", procColsMD.getString("SPECIFIC_NAME")); + + assertTrue(sd + "2nd of 3 rows expected.", procColsMD.next()); + + // function column: testBug69298_func.param_func + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_func", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "param_func", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnIn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + assertEquals(sd + "-> COLUMN_DEF", null, procColsMD.getString("COLUMN_DEF")); + assertEquals(sd + "-> SQL_DATA_TYPE", 0, procColsMD.getInt("SQL_DATA_TYPE")); + assertEquals(sd + "-> SQL_DATETIME_SUB", 0, procColsMD.getInt("SQL_DATETIME_SUB")); + assertEquals(sd + "-> CHAR_OCTET_LENGTH", 0, procColsMD.getInt("CHAR_OCTET_LENGTH")); + assertEquals(sd + "-> ORDINAL_POSITION", 1, procColsMD.getInt("ORDINAL_POSITION")); + assertEquals(sd + "-> IS_NULLABLE", "YES", procColsMD.getString("IS_NULLABLE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_func", procColsMD.getString("SPECIFIC_NAME")); + + assertTrue(sd + "3rd of 3 rows expected.", procColsMD.next()); + } else { + assertTrue(sd + "one row expected.", procColsMD.next()); + } + + // procedure column: testBug69298_proc.param_proc + assertEquals(sd + "-> PROCEDURE_CAT", testConn.getCatalog(), procColsMD.getString("PROCEDURE_CAT")); + assertEquals(sd + "-> PROCEDURE_SCHEM", null, procColsMD.getString("PROCEDURE_SCHEM")); + assertEquals(sd + "-> PROCEDURE_NAME", "testBug69298_proc", procColsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + "-> COLUMN_NAME", "param_proc", procColsMD.getString("COLUMN_NAME")); + assertEquals(sd + "-> COLUMN_TYPE", DatabaseMetaData.procedureColumnIn, procColsMD.getShort("COLUMN_TYPE")); + assertEquals(sd + "-> DATA_TYPE", Types.INTEGER, procColsMD.getInt("DATA_TYPE")); + assertEquals(sd + "-> TYPE_NAME", "INT", procColsMD.getString("TYPE_NAME")); + assertEquals(sd + "-> PRECISION", 10, procColsMD.getInt("PRECISION")); + assertEquals(sd + "-> LENGTH", 10, procColsMD.getInt("LENGTH")); + assertEquals(sd + "-> SCALE", 0, procColsMD.getShort("SCALE")); + assertEquals(sd + "-> RADIX", 10, procColsMD.getShort("RADIX")); + assertEquals(sd + "-> NULLABLE", DatabaseMetaData.procedureNullable, procColsMD.getShort("NULLABLE")); + assertEquals(sd + "-> REMARKS", null, procColsMD.getString("REMARKS")); + assertEquals(sd + "-> COLUMN_DEF", null, procColsMD.getString("COLUMN_DEF")); + assertEquals(sd + "-> SQL_DATA_TYPE", 0, procColsMD.getInt("SQL_DATA_TYPE")); + assertEquals(sd + "-> SQL_DATETIME_SUB", 0, procColsMD.getInt("SQL_DATETIME_SUB")); + assertEquals(sd + "-> CHAR_OCTET_LENGTH", 0, procColsMD.getInt("CHAR_OCTET_LENGTH")); + assertEquals(sd + "-> ORDINAL_POSITION", 1, procColsMD.getInt("ORDINAL_POSITION")); + assertEquals(sd + "-> IS_NULLABLE", "YES", procColsMD.getString("IS_NULLABLE")); + assertEquals(sd + "-> SPECIFIC_NAME", "testBug69298_proc", procColsMD.getString("SPECIFIC_NAME")); + + assertFalse(sd + "no more rows expected.", procColsMD.next()); + } + + /** + * Tests fix for BUG#17248345 - GETFUNCTIONCOLUMNS() METHOD RETURNS COLUMNS OF PROCEDURE. (this happens when + * functions and procedures have a common name) + * + * @throws Exception + * if the test fails. + */ + public void testBug17248345() throws Exception { + Connection testConn; + + // create one stored procedure and one function with same name + createProcedure("testBug17248345", "(IN proccol INT) SELECT 1"); + createFunction("testBug17248345", "(funccol INT) RETURNS INT DETERMINISTIC RETURN 1"); + + // test with standard connection (getProceduresReturnsFunctions=true & useInformationSchema=false) + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) this.conn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) this.conn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(this.conn); + + // test with property useInformationSchema=true (getProceduresReturnsFunctions=true) + testConn = getConnectionWithProps("useInformationSchema=true"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertTrue("Property getProceduresReturnsFunctions should be true", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + + // test with property getProceduresReturnsFunctions=false (useInformationSchema=false) + testConn = getConnectionWithProps("getProceduresReturnsFunctions=false"); + assertFalse("Property useInformationSchema should be false", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + + // test with property useInformationSchema=true & getProceduresReturnsFunctions=false + testConn = getConnectionWithProps("useInformationSchema=true,getProceduresReturnsFunctions=false"); + assertTrue("Property useInformationSchema should be true", ((ConnectionProperties) testConn).getUseInformationSchema()); + assertFalse("Property getProceduresReturnsFunctions should be false", ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions()); + checkMetaDataInfoForBug17248345(testConn); + testConn.close(); + } + + private void checkMetaDataInfoForBug17248345(Connection testConn) throws Exception { + DatabaseMetaData testDbMetaData = testConn.getMetaData(); + ResultSet rsMD; + boolean useInfoSchema = ((ConnectionProperties) testConn).getUseInformationSchema(); + boolean getProcRetFunc = ((ConnectionProperties) testConn).getGetProceduresReturnsFunctions(); + String stepDescription = "Prop. useInfoSchema(" + (useInfoSchema ? 1 : 0) + ") + getProcRetFunc(" + (getProcRetFunc ? 1 : 0) + "):"; + String sd; + + // getFunctions() must return 1 record. + sd = stepDescription + " getFunctions() "; + rsMD = testDbMetaData.getFunctions(null, null, "testBug17248345"); + assertTrue(sd + "one row expected.", rsMD.next()); + assertEquals(sd + " -> FUNCTION_NAME", "testBug17248345", rsMD.getString("FUNCTION_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + + // getFunctionColumns() must return 2 records (func return + func param). + sd = stepDescription + " getFunctionColumns() "; + rsMD = testDbMetaData.getFunctionColumns(null, null, "testBug17248345", "%"); + assertTrue(sd + "1st of 2 rows expected.", rsMD.next()); + assertEquals(sd + " -> FUNCTION_NAME", "testBug17248345", rsMD.getString("FUNCTION_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "", rsMD.getString("COLUMN_NAME")); + assertTrue(sd + "2nd of 2 rows expected.", rsMD.next()); + assertEquals(sd + " -> FUNCTION_NAME", "testBug17248345", rsMD.getString("FUNCTION_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "funccol", rsMD.getString("COLUMN_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + + // getProcedures() must return 1 or 2 records, depending on if getProceduresReturnsFunctions is false or true + // respectively. When exists a procedure and a function with same name, function is returned first. + sd = stepDescription + " getProcedures() "; + rsMD = testDbMetaData.getProcedures(null, null, "testBug17248345"); + if (getProcRetFunc) { + assertTrue(sd + "1st of 2 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertTrue(sd + "2nd of 2 rows expected.", rsMD.next()); + } else { + assertTrue(sd + "one row expected.", rsMD.next()); + } + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + + // getProcedureColumns() must return 1 or 3 records, depending on if getProceduresReturnsFunctions is false or + // true respectively. When exists a procedure and a function with same name, function is returned first. + sd = stepDescription + " getProcedureColumns() "; + rsMD = testDbMetaData.getProcedureColumns(null, null, "testBug17248345", "%"); + if (getProcRetFunc) { + assertTrue(sd + "1st of 3 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "", rsMD.getString("COLUMN_NAME")); + assertTrue(sd + "2nd of 3 rows expected.", rsMD.next()); + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "funccol", rsMD.getString("COLUMN_NAME")); + assertTrue(sd + "3rd of 3 rows expected.", rsMD.next()); + } else { + assertTrue(sd + "one row expected.", rsMD.next()); + } + assertEquals(sd + " -> PROCEDURE_NAME", "testBug17248345", rsMD.getString("PROCEDURE_NAME")); + assertEquals(sd + " -> COLUMN_NAME", "proccol", rsMD.getString("COLUMN_NAME")); + assertFalse(sd + "no more rows expected.", rsMD.next()); + } + + /* + * Tests DatabaseMetaData.getSQLKeywords(). + * (Related to BUG#70701 - DatabaseMetaData.getSQLKeywords() doesn't match MySQL 5.6 reserved words) + * + * The keywords list that this method returns depends on JDBC version. + * + * @throws Exception if the test fails. + */ + public void testReservedWords() throws Exception { + final String mysqlKeywords = "ACCESSIBLE,ADD,ANALYZE,ASC,BEFORE,CASCADE,CHANGE,CONTINUE,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE," + + "DAY_SECOND,DELAYED,DESC,DISTINCTROW,DIV,DUAL,ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERATED,HIGH_PRIORITY," + + "HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,INDEX,INFILE,INT1,INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,ITERATE,KEY,KEYS," + + "KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MASTER_BIND,MASTER_SSL_VERIFY_SERVER_CERT,MAXVALUE," + + "MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIMIZER_COSTS,OPTION,OPTIONALLY," + + "OUTFILE,PURGE,READ,READ_WRITE,REGEXP,RENAME,REPEAT,REPLACE,REQUIRE,RESIGNAL,RESTRICT,RLIKE,SCHEMA,SCHEMAS,SECOND_MICROSECOND,SEPARATOR,SHOW," + + "SIGNAL,SPATIAL,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,STARTING,STORED,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT," + + "UNDO,UNLOCK,UNSIGNED,USAGE,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,VIRTUAL,WHILE,WRITE,XOR,YEAR_MONTH,ZEROFILL"; + assertEquals("MySQL keywords don't match expected.", mysqlKeywords, this.conn.getMetaData().getSQLKeywords()); + } + + /** + * Tests fix for BUG#20504139 - GETFUNCTIONCOLUMNS() AND GETPROCEDURECOLUMNS() RETURNS ERROR FOR VALID INPUTS. + * + * Test duplicated in testsuite.regression.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug20504139() throws Exception { + createFunction("testBug20504139f", "(namef CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT('Hello, ', namef, '!')"); + createFunction("`testBug20504139``f`", "(namef CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT('Hello, ', namef, '!')"); + createProcedure("testBug20504139p", "(INOUT namep CHAR(50)) SELECT CONCAT('Hello, ', namep, '!') INTO namep"); + createProcedure("`testBug20504139``p`", "(INOUT namep CHAR(50)) SELECT CONCAT('Hello, ', namep, '!') INTO namep"); + + for (int testCase = 0; testCase < 8; testCase++) {// 3 props, 8 combinations: 2^3 = 8 + boolean usePedantic = (testCase & 1) == 1; + boolean useInformationSchema = (testCase & 2) == 2; + boolean useFuncsInProcs = (testCase & 4) == 4; + + String connProps = String.format("pedantic=%s,useInformationSchema=%s,getProceduresReturnsFunctions=%s", usePedantic, useInformationSchema, + useFuncsInProcs); + System.out.printf("testBug20504139_%d: %s%n", testCase, connProps); + + Connection testConn = getConnectionWithProps(connProps); + DatabaseMetaData dbmd = testConn.getMetaData(); + + ResultSet testRs = null; + + try { + /* + * test DatabaseMetadata.getProcedureColumns for function + */ + int i = 1; + try { + for (String name : new String[] { "testBug20504139f", "testBug20504139`f" }) { + testRs = dbmd.getProcedureColumns(null, "", name, "%"); + + if (useFuncsInProcs) { + assertTrue(testRs.next()); + assertEquals(testCase + "." + i + ". expected function column name (empty)", "", testRs.getString(4)); + assertEquals(testCase + "." + i + ". expected function column type (empty)", DatabaseMetaData.procedureColumnReturn, + testRs.getInt(5)); + assertTrue(testRs.next()); + assertEquals(testCase + "." + i + ". expected function column name", "namef", testRs.getString(4)); + assertEquals(testCase + "." + i + ". expected function column type (empty)", DatabaseMetaData.procedureColumnIn, testRs.getInt(5)); + assertFalse(testRs.next()); + } else { + assertFalse(testRs.next()); + } + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("FUNCTION `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve function columns, with getProcedureColumns(), from database meta data."); + } + throw e; + } + + /* + * test DatabaseMetadata.getProcedureColumns for procedure + */ + i = 1; + try { + for (String name : new String[] { "testBug20504139p", "testBug20504139`p" }) { + testRs = dbmd.getProcedureColumns(null, "", name, "%"); + + assertTrue(testRs.next()); + assertEquals(testCase + ". expected procedure column name", "namep", testRs.getString(4)); + assertEquals(testCase + ". expected procedure column type (empty)", DatabaseMetaData.procedureColumnInOut, testRs.getInt(5)); + assertFalse(testRs.next()); + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("PROCEDURE `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve prodedure columns, with getProcedureColumns(), from database meta data."); + } + throw e; + } + + /* + * test DatabaseMetadata.getFunctionColumns for function + */ + i = 1; + try { + for (String name : new String[] { "testBug20504139f", "testBug20504139`f" }) { + testRs = dbmd.getFunctionColumns(null, "", name, "%"); + + assertTrue(testRs.next()); + assertEquals(testCase + ". expected function column name (empty)", "", testRs.getString(4)); + assertEquals(testCase + ". expected function column type (empty)", DatabaseMetaData.functionReturn, testRs.getInt(5)); + assertTrue(testRs.next()); + assertEquals(testCase + ". expected function column name", "namef", testRs.getString(4)); + assertEquals(testCase + ". expected function column type (empty)", DatabaseMetaData.functionColumnIn, testRs.getInt(5)); + assertFalse(testRs.next()); + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("FUNCTION `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve function columns, with getFunctionColumns(), from database meta data."); + } + throw e; + } + + /* + * test DatabaseMetadata.getFunctionColumns for procedure + */ + i = 1; + try { + for (String name : new String[] { "testBug20504139p", "testBug20504139`p" }) { + testRs = dbmd.getFunctionColumns(null, "", name, "%"); + + assertFalse(testRs.next()); + + testRs.close(); + i++; + } + } catch (SQLException e) { + if (e.getMessage().matches("PROCEDURE `testBug20504139(:?`{2})?[fp]` does not exist")) { + fail(testCase + "." + i + ". failed to retrieve procedure columns, with getFunctionColumns(), from database meta data."); + } + throw e; + } + } finally { + testConn.close(); + } + } + } + + /** + * Tests fix for BUG#19803348 - GETPROCEDURES() RETURNS INCORRECT O/P WHEN USEINFORMATIONSCHEMA=FALSE. + * + * Composed by two parts: + * 1. Confirm that getProcedures() and getProcedureColumns() aren't returning more results than expected (as per reported bug). + * 2. Confirm that the results from getProcedures() and getProcedureColumns() are in the right order (secondary bug). + * + * Test duplicated in testsuite.regression.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug19803348() throws Exception { + Connection testConn = null; + try { + testConn = getConnectionWithProps("useInformationSchema=false,getProceduresReturnsFunctions=false,nullCatalogMeansCurrent=false"); + DatabaseMetaData dbmd = testConn.getMetaData(); + + String testDb1 = "testBug19803348_db1"; + String testDb2 = "testBug19803348_db2"; + + if (!dbmd.supportsMixedCaseIdentifiers()) { + testDb1 = testDb1.toLowerCase(); + testDb2 = testDb2.toLowerCase(); + } + + createDatabase(testDb1); + createDatabase(testDb2); + + // 1. Check if getProcedures() and getProcedureColumns() aren't returning more results than expected (as per reported bug). + createFunction(testDb1 + ".testBug19803348_f", "(d INT) RETURNS INT BEGIN RETURN d; END"); + createProcedure(testDb1 + ".testBug19803348_p", "(d int) BEGIN SELECT d; END"); + + this.rs = dbmd.getFunctions(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getFunctionColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_p", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + dropFunction(testDb1 + ".testBug19803348_f"); + dropProcedure(testDb1 + ".testBug19803348_p"); + + // 2. Check if the results from getProcedures() and getProcedureColumns() are in the right order (secondary bug). + createFunction(testDb1 + ".testBug19803348_B_f", "(d INT) RETURNS INT BEGIN RETURN d; END"); + createProcedure(testDb1 + ".testBug19803348_B_p", "(d int) BEGIN SELECT d; END"); + createFunction(testDb2 + ".testBug19803348_A_f", "(d INT) RETURNS INT BEGIN RETURN d; END"); + createProcedure(testDb2 + ".testBug19803348_A_p", "(d int) BEGIN SELECT d; END"); + + this.rs = dbmd.getFunctions(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getFunctionColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_f", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedures(null, null, "testBug19803348_%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_p", this.rs.getString(3)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_p", this.rs.getString(3)); + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug19803348_%", "%"); + assertTrue(this.rs.next()); + assertEquals(testDb1, this.rs.getString(1)); + assertEquals("testBug19803348_B_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertTrue(this.rs.next()); + assertEquals(testDb2, this.rs.getString(1)); + assertEquals("testBug19803348_A_p", this.rs.getString(3)); + assertEquals("d", this.rs.getString(4)); + assertFalse(this.rs.next()); + + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + + /** + * Tests fix for BUG#20727196 - GETPROCEDURECOLUMNS() RETURNS EXCEPTION FOR FUNCTION WHICH RETURNS ENUM/SET TYPE. + * + * Test duplicated in testsuite.regression.MetaDataRegressionTest. + * + * @throws Exception + * if the test fails. + */ + public void testBug20727196() throws Exception { + createFunction("testBug20727196_f1", "(p ENUM ('Yes', 'No')) RETURNS VARCHAR(10) BEGIN RETURN IF(p='Yes', 'Yay!', if(p='No', 'Ney!', 'What?')); END"); + createFunction("testBug20727196_f2", "(p CHAR(1)) RETURNS ENUM ('Yes', 'No') BEGIN RETURN IF(p='y', 'Yes', if(p='n', 'No', '?')); END"); + createFunction("testBug20727196_f3", "(p ENUM ('Yes', 'No')) RETURNS ENUM ('Yes', 'No') BEGIN RETURN IF(p='Yes', 'Yes', if(p='No', 'No', '?')); END"); + createProcedure("testBug20727196_p1", "(p ENUM ('Yes', 'No')) BEGIN SELECT IF(p='Yes', 'Yay!', if(p='No', 'Ney!', 'What?')); END"); + + for (String connProps : new String[] { "getProceduresReturnsFunctions=false,useInformationSchema=false", + "getProceduresReturnsFunctions=false,useInformationSchema=true" }) { + + Connection testConn = null; + try { + testConn = getConnectionWithProps(connProps); + DatabaseMetaData dbmd = testConn.getMetaData(); + + this.rs = dbmd.getFunctionColumns(null, null, "testBug20727196_%", "%"); + + // testBug20727196_f1 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f1", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("VARCHAR", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f1", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + // testBug20727196_f2 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f2", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f2", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("CHAR", this.rs.getString(7)); + + // testBug20727196_f3 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f3", this.rs.getString(3)); + assertEquals("", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + assertTrue(this.rs.next()); + assertEquals("testBug20727196_f3", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + assertFalse(this.rs.next()); + + this.rs = dbmd.getProcedureColumns(null, null, "testBug20727196_%", "%"); + + // testBug20727196_p1 columns: + assertTrue(this.rs.next()); + assertEquals("testBug20727196_p1", this.rs.getString(3)); + assertEquals("p", this.rs.getString(4)); + assertEquals("ENUM", this.rs.getString(7)); + + assertFalse(this.rs.next()); + } finally { + if (testConn != null) { + testConn.close(); + } + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/StatementRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/StatementRegressionTest.java new file mode 100644 index 0000000..824be0e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc4/StatementRegressionTest.java @@ -0,0 +1,1471 @@ +/* + Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression.jdbc4; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.mysql.jdbc.CallableStatement; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.PreparedStatement; +import com.mysql.jdbc.ReplicationConnection; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.Util; + +import testsuite.BaseTestCase; + +public class StatementRegressionTest extends BaseTestCase { + /** + * Creates a new StatementRegressionTest. + * + * @param name + * the name of the test + */ + public StatementRegressionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StatementRegressionTest.class); + } + + /** + * Tests fix for BUG#68916 - closeOnCompletion doesn't work. + * + * @throws Exception + * if the test fails. + */ + public void testBug68916() throws Exception { + // Prepare common test objects + createProcedure("testBug68916_proc", "() BEGIN SELECT 1; SELECT 2; SELECT 3; END"); + createTable("testBug68916_tbl", "(fld1 INT NOT NULL AUTO_INCREMENT, fld2 INT, PRIMARY KEY(fld1))"); + + // STEP 1: Test using standard connection (no properties) + subTestBug68916ForStandardConnection(); + + // STEP 2: Test using connection property holdResultsOpenOverStatementClose=true + subTestBug68916ForHoldResultsOpenOverStatementClose(); + + // STEP 3: Test using connection property dontTrackOpenResources=true + subTestBug68916ForDontTrackOpenResources(); + + // STEP 4: Test using connection property allowMultiQueries=true + subTestBug68916ForAllowMultiQueries(); + + // STEP 5: Test concurrent Statement/ResultSet sharing same Connection + subTestBug68916ForConcurrency(); + } + + private void subTestBug68916ForStandardConnection() throws Exception { + Connection testConnection = this.conn; + String testStep; + ResultSet testResultSet1, testResultSet2, testResultSet3; + + // We are testing against code that was compiled with Java 6, so methods isCloseOnCompletion() and + // closeOnCompletion() aren't available in the Statement interface. We need to test directly our implementations. + StatementImpl testStatement = null; + PreparedStatement testPrepStatement = null; + CallableStatement testCallStatement = null; + + /* + * Testing with standard connection (no properties) + */ + testStep = "Standard Connection"; + + /* + * SUB-STEP 0: The basics (connection without properties) + */ + // **testing Statement** + // ResultSets should be closed when owning Statement is closed + testStatement = (StatementImpl) testConnection.createStatement(); + + assertFalse(testStep + ".ST:0. Statement.isCloseOnCompletion(): false by default.", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + + // test Statement.close() + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.close(); + + assertTrue(testStep + ".ST:0. ResultSet.isClosed(): true after Statement.Close().", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:0. Statement.isClosed(): true after Statement.Close().", testStatement.isClosed()); + + // **testing PreparedStatement** + // ResultSets should be closed when owning PreparedStatement is closed + testPrepStatement = (com.mysql.jdbc.PreparedStatement) testConnection.prepareStatement("SELECT 1"); + + assertFalse(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): false by default.", testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + + // test PreparedStatement.close() + testPrepStatement.execute(); + testResultSet1 = testPrepStatement.getResultSet(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.close(); + + assertTrue(testStep + ".PS:0. ResultSet.isClosed(): true after PreparedStatement.close().", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:0. PreparedStatement.isClosed(): true after PreparedStatement.close().", testPrepStatement.isClosed()); + + /* + * SUB-STEP 1: One ResultSet (connection without properties) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close, keeping statement open, when following with an executeBatch() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.addBatch("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)"); + testStatement.executeBatch(); + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true after executeBatch() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close keeping statement open, when following with an executeUpdate() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", com.mysql.jdbc.Statement.RETURN_GENERATED_KEYS); + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true after executeUpdate() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing PreparedStatement** + // PreparedStatement using closeOnCompletion should be closed when last ResultSet is closed + testPrepStatement = (PreparedStatement) testConnection.prepareStatement("SELECT 1"); + testPrepStatement.closeOnCompletion(); + + testResultSet1 = testPrepStatement.executeQuery(); + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".PS:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:1. PreparedStatement.isClosed(): true when last ResultSet is closed.", testPrepStatement.isClosed()); + + /* + * SUB-STEP 2: Multiple ResultSets, sequentially (connection without properties) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testResultSet2 = testStatement.executeQuery("SELECT 2"); // closes testResultSet1 + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true after 2nd Statement.executeQuery().", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet2.next()) { + } + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet3 = testStatement.executeQuery("SELECT 3"); // closes testResultSet2 + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true after 3rd Statement.executeQuery().", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet3.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:2. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + /* + * SUB-STEP 3: Multiple ResultSets, returned at once (connection without properties) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:3. There should be some ResultSets.", testStatement.execute("CALL testBug68916_proc")); + testResultSet1 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testStatement.getResultSet(); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".ST:3. There should be no more ResultSets.", testStatement.getMoreResults()); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:3. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing CallableStatement** + // CallableStatement using closeOnCompletion should be closed when last ResultSet is closed + testCallStatement = (CallableStatement) testConnection.prepareCall("CALL testBug68916_proc"); + testCallStatement.closeOnCompletion(); + + assertTrue(testStep + ".CS:3. There should be some ResultSets.", testCallStatement.execute()); + testResultSet1 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testCallStatement.getResultSet(); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".CS:3. There should be no more ResultSets.", testCallStatement.getMoreResults()); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".CS:3. CallableStatement.isClosed(): true when last ResultSet is closed.", testCallStatement.isClosed()); + + /* + * SUB-STEP 4: Generated Keys ResultSet (connection without properties) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test again and combine with simple query + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (4), (5), (6)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + testResultSet2 = testStatement.executeQuery("SELECT 2"); + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true after executeQuery() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet2.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + } + + private void subTestBug68916ForHoldResultsOpenOverStatementClose() throws Exception { + Connection testConnection; + String testStep; + ResultSet testResultSet1, testResultSet2, testResultSet3; + + // We are testing against code that was compiled with Java 6, so methods isCloseOnCompletion() and + // closeOnCompletion() aren't available in the Statement interface. We need to test directly our + // implementations. + StatementImpl testStatement = null; + PreparedStatement testPrepStatement = null; + CallableStatement testCallStatement = null; + + /* + * Testing with connection property holdResultsOpenOverStatementClose=true + */ + testStep = "Conn. Prop. 'holdResultsOpenOverStatementClose'"; + testConnection = getConnectionWithProps("holdResultsOpenOverStatementClose=true"); + + /* + * SUB-STEP 0: The basics (holdResultsOpenOverStatementClose=true) + */ + // **testing Statement** + // ResultSets should stay open when owning Statement is closed + testStatement = (StatementImpl) testConnection.createStatement(); + + assertFalse(testStep + ".ST:0. Statement.isCloseOnCompletion(): false dy default.", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + + // test Statement.close() + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.close(); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false after Statement.Close().", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:0. Statement.isClosed(): true after Statement.Close().", testStatement.isClosed()); + + // **testing PreparedStatement** + // ResultSets should stay open when owning PreparedStatement is closed + testPrepStatement = (com.mysql.jdbc.PreparedStatement) testConnection.prepareStatement("SELECT 1"); + + assertFalse(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): false by default.", testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + + // test PreparedStatement.close() + testPrepStatement.execute(); + testResultSet1 = testPrepStatement.getResultSet(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.close(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false after PreparedStatement.close().", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:0. PreparedStatement.isClosed(): true after PreparedStatement.close().", testPrepStatement.isClosed()); + + /* + * SUB-STEP 1: One ResultSet (holdResultsOpenOverStatementClose=true) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close keeping statement open, when following with an executeBatch() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.addBatch("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)"); + testStatement.executeBatch(); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after executeBatch() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close keeping statement open, when following with an executeUpdate() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", Statement.RETURN_GENERATED_KEYS); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after executeUpdate() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing PreparedStatement** + // PreparedStatement using closeOnCompletion should be closed when last ResultSet is closed + testPrepStatement = (PreparedStatement) testConnection.prepareStatement("SELECT 1"); + testPrepStatement.closeOnCompletion(); + + testResultSet1 = testPrepStatement.executeQuery(); + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".PS:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:1. PreparedStatement.isClosed(): true when last ResultSet is closed.", testPrepStatement.isClosed()); + + /* + * SUB-STEP 2: Multiple ResultSets, sequentially (holdResultsOpenOverStatementClose=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testResultSet2 = testStatement.executeQuery("SELECT 2"); // mustn't close testResultSet1 + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after 2nd Statement.executeQuery().", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet2.next()) { + } + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet3 = testStatement.executeQuery("SELECT 3"); // mustn't close testResultSet1 nor testResultSet2 + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after 3rd Statement.executeQuery().", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet2.close(); + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); + testResultSet3.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:2. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + /* + * SUB-STEP 3: Multiple ResultSets, returned at once (holdResultsOpenOverStatementClose=true) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:3. There should be some ResultSets.", testStatement.execute("CALL testBug68916_proc")); + testResultSet1 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testStatement.getResultSet(); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".ST:3. There should be no more ResultSets.", testStatement.getMoreResults()); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:3. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing CallableStatement** + // CallableStatement using closeOnCompletion should be closed when last ResultSet is closed + testCallStatement = (CallableStatement) testConnection.prepareCall("CALL testBug68916_proc"); + testCallStatement.closeOnCompletion(); + + assertTrue(testStep + ".CS:3. There should be some ResultSets.", testCallStatement.execute()); + testResultSet1 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testCallStatement.getResultSet(); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".CS:3. There should be no more ResultSets.", testCallStatement.getMoreResults()); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".CS:3. CallableStatement.isClosed(): true when last ResultSet is closed.", testCallStatement.isClosed()); + + /* + * SUB-STEP 4: Generated Keys ResultSet (holdResultsOpenOverStatementClose=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test again and combine with simple query + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (4), (5), (6)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + testResultSet2 = testStatement.executeQuery("SELECT 2"); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false after executeQuery() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet2.close(); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false when last ResultSet is closed (still one open).", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + testConnection.close(); + } + + private void subTestBug68916ForDontTrackOpenResources() throws Exception { + Connection testConnection; + String testStep; + ResultSet testResultSet1, testResultSet2, testResultSet3; + + // We are testing against code that was compiled with Java 6, so methods isCloseOnCompletion() and + // closeOnCompletion() aren't available in the Statement interface. We need to test directly our + // implementations. + StatementImpl testStatement = null; + PreparedStatement testPrepStatement = null; + CallableStatement testCallStatement = null; + + /* + * Testing with connection property dontTrackOpenResources=true + */ + testStep = "Conn. Prop. 'dontTrackOpenResources'"; + testConnection = getConnectionWithProps("dontTrackOpenResources=true"); + + /* + * SUB-STEP 0: The basics (dontTrackOpenResources=true) + */ + // **testing Statement** + // ResultSets should stay open when owning Statement is closed + testStatement = (StatementImpl) testConnection.createStatement(); + + assertFalse(testStep + ".ST:0. Statement.isCloseOnCompletion(): false by default.", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + + // test Statement.close() + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.close(); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false after Statement.Close().", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:0. Statement.isClosed(): true after Statement.Close().", testStatement.isClosed()); + + // **testing PreparedStatement** + // ResultSets should stay open when owning PreparedStatement is closed + testPrepStatement = (com.mysql.jdbc.PreparedStatement) testConnection.prepareStatement("SELECT 1"); + + assertFalse(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): false by default.", testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + + // test PreparedStatement.close() + testPrepStatement.execute(); + testResultSet1 = testPrepStatement.getResultSet(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.close(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false after PreparedStatement.close().", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:0. PreparedStatement.isClosed(): true after PreparedStatement.close().", testPrepStatement.isClosed()); + + /* + * SUB-STEP 1: One ResultSet (dontTrackOpenResources=true) + */ + // **testing Statement** + // Statement, although using closeOnCompletion, shouldn't be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset (not) close, keeping statement open, when following with an executeBatch() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.addBatch("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)"); + testStatement.executeBatch(); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after executeBatch() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset (not) close keeping statement open, when following with an executeUpdate() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", Statement.RETURN_GENERATED_KEYS); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after executeUpdate() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + // **testing PreparedStatement** + // PreparedStatement, although using closeOnCompletion, shouldn't be closed when last ResultSet is closed + testPrepStatement = (PreparedStatement) testConnection.prepareStatement("SELECT 1"); + testPrepStatement.closeOnCompletion(); + + testResultSet1 = testPrepStatement.executeQuery(); + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".PS:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false when last ResultSet is closed.", testPrepStatement.isClosed()); + + /* + * SUB-STEP 2: Multiple ResultSets, sequentially (dontTrackOpenResources=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testResultSet2 = testStatement.executeQuery("SELECT 2"); // mustn't close testResultSet1 + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after 2nd Statement.executeQuery().", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet2.next()) { + } + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet3 = testStatement.executeQuery("SELECT 3"); // mustn't close testResultSet1 nor testResultSet2 + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after 3rd Statement.executeQuery().", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet2.close(); + + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); + testResultSet3.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + /* + * SUB-STEP 3: Multiple ResultSets, returned at once (dontTrackOpenResources=true) + */ + // **testing Statement** + // Statement, although using closeOnCompletion, shouldn't be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:3. There should be some ResultSets.", testStatement.execute("CALL testBug68916_proc")); + testResultSet1 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertFalse(testStep + ".ST:3. There should be no more ResultSets.", testStatement.getMoreResults()); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false after last Satement.getMoreResults().", testStatement.isClosed()); + + // since open ResultSets aren't tracked, we need to close all manually + testResultSet1.close(); + testResultSet2.close(); + testResultSet3.close(); + // although there are no more ResultSets, Statement mustn't be closed + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + // **testing CallableStatement** + // CallableStatement, although using closeOnCompletion, shouldn't be closed when last ResultSet is closed + testCallStatement = (CallableStatement) testConnection.prepareCall("CALL testBug68916_proc"); + testCallStatement.closeOnCompletion(); + + assertTrue(testStep + ".CS:3. There should be some ResultSets.", testCallStatement.execute()); + testResultSet1 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertFalse(testStep + ".CS:3. There should be no more ResultSets.", testCallStatement.getMoreResults()); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false after last Satement.getMoreResults().", testCallStatement.isClosed()); + + // since open ResultSets aren't tracked, we need to close all manually + testResultSet1.close(); + testResultSet2.close(); + testResultSet3.close(); + // although there are no more ResultSets, Statement mustn't be closed + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false when last ResultSet is closed.", testCallStatement.isClosed()); + + /* + * SUB-STEP 4: Generated Keys ResultSet (dontTrackOpenResources=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + // test again and combine with simple query + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (4), (5), (6)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + testResultSet2 = testStatement.executeQuery("SELECT 2"); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false after executeQuery() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet2.close(); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false when last ResultSet is closed (still one open).", testStatement.isClosed()); + + testResultSet1.close(); // although it's last open ResultSet, Statement mustn't be closed + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false when last ResultSet is closed.", testStatement.isClosed()); + + testConnection.close(); + } + + private void subTestBug68916ForAllowMultiQueries() throws Exception { + Connection testConnection; + String testStep; + ResultSet testResultSet1, testResultSet2, testResultSet3; + + // We are testing against code that was compiled with Java 6, so methods isCloseOnCompletion() and + // closeOnCompletion() aren't available in the Statement interface. We need to test directly our + // implementations. + StatementImpl testStatement = null; + PreparedStatement testPrepStatement = null; + CallableStatement testCallStatement = null; + + /* + * Testing with connection property allowMultiQueries=true + */ + testStep = "Conn. Prop. 'allowMultiQueries'"; + testConnection = getConnectionWithProps("allowMultiQueries=true"); + + /* + * SUB-STEP 0: The basics (allowMultiQueries=true) + */ + // **testing Statement** + // ResultSets should be closed when owning Statement is closed + testStatement = (StatementImpl) testConnection.createStatement(); + + assertFalse(testStep + ".ST:0. Statement.isCloseOnCompletion(): false by default.", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.closeOnCompletion(); + + assertTrue(testStep + ".ST:0. Statement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", testStatement.isCloseOnCompletion()); + + // test Statement.close() + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:0. Statement.isClosed(): false.", testStatement.isClosed()); + + testStatement.close(); + + assertTrue(testStep + ".ST:0. ResultSet.isClosed(): true after Statement.Close().", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:0. Statement.isClosed(): true after Statement.Close().", testStatement.isClosed()); + + // **testing PreparedStatement** + // ResultSets should be closed when owning PreparedStatement is closed + testPrepStatement = (com.mysql.jdbc.PreparedStatement) testConnection.prepareStatement("SELECT 1"); + + assertFalse(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): false by default.", testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.closeOnCompletion(); + + assertTrue(testStep + ".PS:0. PreparedStatement.isCloseOnCompletion(): true after 2nd Statement.closeOnCompletion().", + testPrepStatement.isCloseOnCompletion()); + + // test PreparedStatement.close() + testPrepStatement.execute(); + testResultSet1 = testPrepStatement.getResultSet(); + + assertFalse(testStep + ".PS:0. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:0. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testPrepStatement.close(); + + assertTrue(testStep + ".PS:0. ResultSet.isClosed(): true after PreparedStatement.close().", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:0. PreparedStatement.isClosed(): true after PreparedStatement.close().", testPrepStatement.isClosed()); + + /* + * SUB-STEP 1: One ResultSet (allowMultiQueries=true) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".ST:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close, keeping statement open, when following with an executeBatch() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.addBatch("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)"); + testStatement.executeBatch(); + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true after executeBatch() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test implicit resultset close keeping statement open, when following with an executeUpdate() + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3)", com.mysql.jdbc.Statement.RETURN_GENERATED_KEYS); + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true after executeUpdate() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:1. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1 = testStatement.getGeneratedKeys(); + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:1. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing PreparedStatement** + // PreparedStatement using closeOnCompletion should be closed when last ResultSet is closed + testPrepStatement = (PreparedStatement) testConnection.prepareStatement("SELECT 1"); + testPrepStatement.closeOnCompletion(); + + testResultSet1 = testPrepStatement.executeQuery(); + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + while (testResultSet1.next()) { + } + + assertFalse(testStep + ".PS:1. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet1.isClosed()); + assertFalse(testStep + ".PS:1. PreparedStatement.isClosed(): false.", testPrepStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".PS:1. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".PS:1. PreparedStatement.isClosed(): true when last ResultSet is closed.", testPrepStatement.isClosed()); + + /* + * SUB-STEP 2: Multiple ResultSets, sequentially (allowMultiQueries=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1"); + testResultSet2 = testStatement.executeQuery("SELECT 2; SELECT 3"); // closes testResultSet1 + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true after 2nd Statement.executeQuery().", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + while (testResultSet2.next()) { + } + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false after ResultSet have reached the end.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults()); // closes + // testResultSet2 + testResultSet3 = testStatement.getResultSet(); + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true after Statement.getMoreResults().", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:2. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:2. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet3.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:2. ResultSet.isClosed(): true.", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:2. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + /* + * SUB-STEP 3: Multiple ResultSets, returned at once (allowMultiQueries=true) + */ + // **testing Statement** + // Statement using closeOnCompletion should be closed when last ResultSet is closed + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testResultSet1 = testStatement.executeQuery("SELECT 1; SELECT 2; SELECT 3"); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testStatement.getResultSet(); + + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + assertTrue(testStep + ".ST:3. There should be more ResultSets.", testStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testStatement.getResultSet(); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".ST:3. Statement.isClosed(): false.", testStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".ST:3. There should be no more ResultSets.", testStatement.getMoreResults()); + + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".ST:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".ST:3. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // **testing CallableStatement** + // CallableStatement using closeOnCompletion should be closed when last ResultSet is closed + testCallStatement = (CallableStatement) testConnection.prepareCall("CALL testBug68916_proc"); + testCallStatement.closeOnCompletion(); + + assertTrue(testStep + ".CS:3. There should be some ResultSets.", testCallStatement.execute()); + testResultSet1 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.KEEP_CURRENT_RESULT)); + testResultSet2 = testCallStatement.getResultSet(); + + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false after Statement.getMoreResults(Statement.KEEP_CURRENT_RESULT).", testResultSet1.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + assertTrue(testStep + ".CS:3. There should be more ResultSets.", testCallStatement.getMoreResults(Statement.CLOSE_ALL_RESULTS)); + testResultSet3 = testCallStatement.getResultSet(); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after Statement.getMoreResults(Statement.CLOSE_ALL_RESULTS).", testResultSet2.isClosed()); + assertFalse(testStep + ".CS:3. ResultSet.isClosed(): false.", testResultSet3.isClosed()); + assertFalse(testStep + ".CS:3. CallableStatement.isClosed(): false.", testCallStatement.isClosed()); + + // no more ResultSets, must close Statement + assertFalse(testStep + ".CS:3. There should be no more ResultSets.", testCallStatement.getMoreResults()); + + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertTrue(testStep + ".CS:3. ResultSet.isClosed(): true after last Satement.getMoreResults().", testResultSet3.isClosed()); + assertTrue(testStep + ".CS:3. CallableStatement.isClosed(): true when last ResultSet is closed.", testCallStatement.isClosed()); + + /* + * SUB-STEP 4: Generated Keys ResultSet (allowMultiQueries=true) + */ + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (1), (2), (3); INSERT INTO testBug68916_tbl (fld2) VALUES (4), (5), (6)", + Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + testResultSet1.close(); // last open ResultSet, must close Statement + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + + // test again and combine with simple query + testStatement = (StatementImpl) testConnection.createStatement(); + testStatement.closeOnCompletion(); + + testStatement.executeUpdate("INSERT INTO testBug68916_tbl (fld2) VALUES (4), (5), (6)", Statement.RETURN_GENERATED_KEYS); + + testResultSet1 = testStatement.getGeneratedKeys(); + assertTrue(testStep + ".ST:4. Statement.getGeneratedKeys(): should return some values.", testResultSet1.next()); + + testResultSet2 = testStatement.executeQuery("SELECT 2; SELECT 3"); + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true after executeQuery() in same Statement.", testResultSet1.isClosed()); + assertFalse(testStep + ".ST:4. ResultSet.isClosed(): false.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): false.", testStatement.isClosed()); + + // last open ResultSet won't close the Statement + // because we didn't fetch the next one (SELECT 3) + testResultSet2.close(); + + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet1.isClosed()); + assertTrue(testStep + ".ST:4. ResultSet.isClosed(): true.", testResultSet2.isClosed()); + assertFalse(testStep + ".ST:4. Statement.isClosed(): true when last ResultSet is closed.", testStatement.isClosed()); + testStatement.close(); + + testConnection.close(); + } + + private void subTestBug68916ForConcurrency() throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + CompletionService complService = new ExecutorCompletionService(executor); + + String[] connectionProperties = new String[] { "", "holdResultsOpenOverStatementClose=true", "dontTrackOpenResources=true" }; + // overridesCloseOnCompletion[n] refers to the effect of connectionProperties[n] on + // Statement.closeOnCompletion() + boolean[] overridesCloseOnCompletion = new boolean[] { false, false, true }; + String[] sampleQueries = new String[] { "SELECT * FROM mysql.help_topic", "SELECT SLEEP(1)", + "SELECT * FROM mysql.time_zone tz INNER JOIN mysql.time_zone_name tzn ON tz.time_zone_id = tzn.time_zone_id " + + "INNER JOIN mysql.time_zone_transition tzt ON tz.time_zone_id = tzt.time_zone_id " + + "INNER JOIN mysql.time_zone_transition_type tztt ON tzt.time_zone_id = tztt.time_zone_id " + + "AND tzt.transition_type_id = tztt.transition_type_id ORDER BY tzn.name , tztt.abbreviation , tzt.transition_time", + "SELECT 1" }; + int threadCount = sampleQueries.length; + + for (int c = 0; c < connectionProperties.length; c++) { + System.out.println("Test Connection with property '" + connectionProperties[c] + "'"); + Connection testConnection = getConnectionWithProps(connectionProperties[c]); + + for (int t = 0; t < threadCount; t++) { + complService.submit(new subTestBug68916ConcurrentTask(testConnection, sampleQueries[t], overridesCloseOnCompletion[c])); + } + + for (int t = 0; t < threadCount; t++) { + try { + System.out.println(" " + complService.take().get()); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (ExecutionException ex) { + if (ex.getCause() instanceof Error) { + // let JUnit try to report as Failure instead of Error + throw (Error) ex.getCause(); + } + } + } + + testConnection.close(); + } + } + + private class subTestBug68916ConcurrentTask implements Callable { + Connection testConnection = null; + String query = null; + boolean closeOnCompletionIsOverriden = false; + + subTestBug68916ConcurrentTask(Connection testConnection, String query, boolean closeOnCompletionIsOverriden) { + this.testConnection = testConnection; + this.query = query; + this.closeOnCompletionIsOverriden = closeOnCompletionIsOverriden; + } + + public String call() throws Exception { + String threadName = Thread.currentThread().getName(); + long startTime = System.currentTimeMillis(); + long stopTime = startTime; + StatementImpl testStatement = null; + int count = 0; + + try { + testStatement = (StatementImpl) this.testConnection.createStatement(); + testStatement.closeOnCompletion(); + + System.out.println(threadName + " is executing: " + this.query); + ResultSet testResultSet = testStatement.executeQuery(this.query); + while (testResultSet.next()) { + count++; + } + assertTrue(threadName + ": Query should return some values.", count > 0); + assertFalse(threadName + ": Statement shouldn't be closed.", testStatement.isClosed()); + + testResultSet.close(); // should close statement if not closeOnCompletionIsOverriden + if (this.closeOnCompletionIsOverriden) { + assertFalse(threadName + ": Statement shouldn't be closed.", testStatement.isClosed()); + } else { + assertTrue(threadName + ": Statement should be closed.", testStatement.isClosed()); + } + + } catch (SQLException e) { + e.printStackTrace(); + fail(threadName + ": Something went wrong, maybe Connection or Statement was closed before its time."); + + } finally { + try { + testStatement.close(); + } catch (SQLException e) { + } + stopTime = System.currentTimeMillis(); + } + return threadName + ": processed " + count + " rows in " + (stopTime - startTime) + " milliseconds."; + } + } + + /** + * Tests fix for BUG#73163 - IndexOutOfBoundsException thrown preparing statement. + * + * This bug occurs only if running with Java6+. Duplicated in testsuite.regression.StatementRegressionTest.testBug73163(). + * + * @throws Exception + * if the test fails. + */ + public void testBug73163() throws Exception { + try { + stmt = conn.prepareStatement("LOAD DATA INFILE ? INTO TABLE testBug73163"); + } catch (SQLException e) { + if (e.getCause() instanceof IndexOutOfBoundsException && Util.isJdbc4()) { + fail("IOOBE thrown in Java6+ while preparing a LOAD DATA statement with placeholders."); + } else { + throw e; + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc42/StatementRegressionTest.java b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc42/StatementRegressionTest.java new file mode 100644 index 0000000..6630b1c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/regression/jdbc42/StatementRegressionTest.java @@ -0,0 +1,64 @@ +/* + Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.regression.jdbc42; + +import java.sql.Connection; + +import com.mysql.jdbc.JDBC42PreparedStatement; +import com.mysql.jdbc.JDBC42ServerPreparedStatement; + +import testsuite.BaseTestCase; + +public class StatementRegressionTest extends BaseTestCase { + + public StatementRegressionTest(String name) { + super(name); + } + + /** + * Tests fix for Bug#79598 - Client side Prepared Statement caching bypasses JDBC42 Java 8 Time conversion. + * + * Although in the bug report subject is mentioned a Java 8 Time conversion issue, the actual problem occurs because of wrong types being returned after + * preparing statements when prepared statements cache is enabled. The Java 8 Time data has no relation to this bug. + */ + public void testBug79598() throws Exception { + Connection testConn = getConnectionWithProps("cachePrepStmts=true"); + this.pstmt = testConn.prepareStatement("SELECT 'testBug79598'"); + assertTrue(JDBC42PreparedStatement.class.isAssignableFrom(this.pstmt.getClass())); + this.pstmt.close(); + this.pstmt = testConn.prepareStatement("SELECT 'testBug79598'"); + assertTrue(JDBC42PreparedStatement.class.isAssignableFrom(this.pstmt.getClass())); + this.pstmt.close(); + testConn.close(); + + testConn = getConnectionWithProps("cachePrepStmts=true,useServerPrepStmts=true"); + this.pstmt = testConn.prepareStatement("SELECT 'testBug79598'"); + assertTrue(JDBC42ServerPreparedStatement.class.isAssignableFrom(this.pstmt.getClass())); + this.pstmt.close(); + this.pstmt = testConn.prepareStatement("SELECT 'testBug79598'"); + assertTrue(JDBC42ServerPreparedStatement.class.isAssignableFrom(this.pstmt.getClass())); + this.pstmt.close(); + testConn.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/BlobTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/BlobTest.java new file mode 100644 index 0000000..b1253b2 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/BlobTest.java @@ -0,0 +1,259 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.sql.Connection; + +import testsuite.BaseTestCase; + +/** + * Tests BLOB functionality in the driver. + */ +public class BlobTest extends BaseTestCase { + + protected static File testBlobFile; + + static { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + for (int i = 0; i < 5; i++) { + try { + if (testBlobFile.delete()) { + break; + } + } catch (Throwable t) { + } + } + } + }); + } + + /** + * Creates a new BlobTest object. + * + * @param name + * the test to run + */ + public BlobTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(BlobTest.class); + } + + /** + * Setup the test case + * + * @throws Exception + * if an error occurs + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + if (versionMeetsMinimum(4, 0)) { + int requiredSize = 32 * 1024 * 1024; + + if (testBlobFile == null || testBlobFile.length() != requiredSize) { + createBlobFile(requiredSize); + } + + } else { + int requiredSize = 8 * 1024 * 1024; + + if (testBlobFile == null || testBlobFile.length() != requiredSize) { + createBlobFile(requiredSize); + } + } + + createTestTable(); + } + + public void testByteStreamInsert() throws Exception { + if (versionMeetsMinimum(5, 6, 20) && !versionMeetsMinimum(5, 7)) { + /* + * The 5.6.20 patch for Bug #16963396, Bug #19030353, Bug #69477 limits the size of redo log BLOB writes + * to 10% of the redo log file size. The 5.7.5 patch addresses the bug without imposing a limitation. + * As a result of the redo log BLOB write limit introduced for MySQL 5.6, innodb_log_file_size should be set to a value + * greater than 10 times the largest BLOB data size found in the rows of your tables plus the length of other variable length + * fields (VARCHAR, VARBINARY, and TEXT type fields). + */ + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'innodb_log_file_size'"); + this.rs.next(); + if (this.rs.getInt(2) < 10 * testBlobFile.length()) { + fail("You need to increase innodb_log_file_size to at least " + (10 * testBlobFile.length()) + " before running this test!"); + } + } + testByteStreamInsert(this.conn); + } + + /** + * Tests inserting blob data as a stream + * + * @throws Exception + * if an error occurs + */ + private void testByteStreamInsert(Connection c) throws Exception { + BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(testBlobFile)); + this.pstmt = c.prepareStatement("INSERT INTO BLOBTEST(blobdata) VALUES (?)"); + this.pstmt.setBinaryStream(1, bIn, (int) testBlobFile.length()); + this.pstmt.execute(); + + this.pstmt.clearParameters(); + doRetrieval(); + } + + private boolean checkBlob(byte[] retrBytes) throws Exception { + boolean passed = false; + BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(testBlobFile)); + + try { + int fileLength = (int) testBlobFile.length(); + if (retrBytes.length == fileLength) { + for (int i = 0; i < fileLength; i++) { + byte fromFile = (byte) (bIn.read() & 0xff); + + if (retrBytes[i] != fromFile) { + passed = false; + System.out.println("Byte pattern differed at position " + i + " , " + retrBytes[i] + " != " + fromFile); + + for (int j = 0; (j < (i + 10)) /* && (j < i) */; j++) { + System.out.print(Integer.toHexString(retrBytes[j] & 0xff) + " "); + } + + break; + } + + passed = true; + } + } else { + passed = false; + System.out.println("retrBytes.length(" + retrBytes.length + ") != testBlob.length(" + fileLength + ")"); + } + + return passed; + } finally { + if (bIn != null) { + bIn.close(); + } + } + } + + private void createTestTable() throws Exception { + createTable("BLOBTEST", "(pos int PRIMARY KEY auto_increment, blobdata LONGBLOB)"); + } + + /** + * Mark this as deprecated to avoid warnings from compiler... + * + * @deprecated + * + * @throws Exception + * if an error occurs retrieving the value + */ + @Deprecated + private void doRetrieval() throws Exception { + boolean passed = false; + this.rs = this.stmt.executeQuery("SELECT blobdata from BLOBTEST LIMIT 1"); + this.rs.next(); + + byte[] retrBytes = this.rs.getBytes(1); + passed = checkBlob(retrBytes); + assertTrue("Inserted BLOB data did not match retrieved BLOB data for getBytes().", passed); + retrBytes = this.rs.getBlob(1).getBytes(1L, (int) this.rs.getBlob(1).length()); + passed = checkBlob(retrBytes); + assertTrue("Inserted BLOB data did not match retrieved BLOB data for getBlob().", passed); + + InputStream inStr = this.rs.getBinaryStream(1); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int b; + + while ((b = inStr.read()) != -1) { + bOut.write((byte) b); + } + + retrBytes = bOut.toByteArray(); + passed = checkBlob(retrBytes); + assertTrue("Inserted BLOB data did not match retrieved BLOB data for getBinaryStream().", passed); + inStr = this.rs.getAsciiStream(1); + bOut = new ByteArrayOutputStream(); + + while ((b = inStr.read()) != -1) { + bOut.write((byte) b); + } + + retrBytes = bOut.toByteArray(); + passed = checkBlob(retrBytes); + assertTrue("Inserted BLOB data did not match retrieved BLOB data for getAsciiStream().", passed); + inStr = this.rs.getUnicodeStream(1); + bOut = new ByteArrayOutputStream(); + + while ((b = inStr.read()) != -1) { + bOut.write((byte) b); + } + + retrBytes = bOut.toByteArray(); + passed = checkBlob(retrBytes); + assertTrue("Inserted BLOB data did not match retrieved BLOB data for getUnicodeStream().", passed); + } + + private final static String TEST_BLOB_FILE_PREFIX = "cmj-testblob"; + + private void createBlobFile(int size) throws Exception { + if (testBlobFile != null && testBlobFile.length() != size) { + testBlobFile.delete(); + } + + testBlobFile = File.createTempFile(TEST_BLOB_FILE_PREFIX, ".dat"); + testBlobFile.deleteOnExit(); + + // TODO: following cleanup doesn't work correctly during concurrent execution of testsuite + // cleanupTempFiles(testBlobFile, TEST_BLOB_FILE_PREFIX); + + BufferedOutputStream bOut = new BufferedOutputStream(new FileOutputStream(testBlobFile)); + + int dataRange = Byte.MAX_VALUE - Byte.MIN_VALUE; + + for (int i = 0; i < size; i++) { + bOut.write((byte) ((Math.random() * dataRange) + Byte.MIN_VALUE)); + } + + bOut.flush(); + bOut.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/CallableStatementTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/CallableStatementTest.java new file mode 100644 index 0000000..cbfd8ac --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/CallableStatementTest.java @@ -0,0 +1,453 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.ParameterMetaData; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Properties; + +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.log.StandardLogger; + +import testsuite.BaseTestCase; + +/** + * Tests callable statement functionality. + */ +public class CallableStatementTest extends BaseTestCase { + public CallableStatementTest(String name) { + super(name); + } + + /** + * Tests functioning of inout parameters + * + * @throws Exception + * if the test fails + */ + + public void testInOutParams() throws Exception { + if (versionMeetsMinimum(5, 0)) { + CallableStatement storedProc = null; + + createProcedure("testInOutParam", "(IN p1 VARCHAR(255), INOUT p2 INT)\nbegin\n DECLARE z INT;\nSET z = p2 + 1;\nSET p2 = z;\n" + + "SELECT p1;\nSELECT CONCAT('zyxw', p1);\nend\n"); + + storedProc = this.conn.prepareCall("{call testInOutParam(?, ?)}"); + + storedProc.setString(1, "abcd"); + storedProc.setInt(2, 4); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + assertEquals(5, storedProc.getInt(2)); + + } + } + + public void testBatch() throws Exception { + if (versionMeetsMinimum(5, 0)) { + Connection batchedConn = null; + + try { + createTable("testBatchTable", "(field1 INT)"); + createProcedure("testBatch", "(IN foo VARCHAR(15))\nbegin\nINSERT INTO testBatchTable VALUES (foo);\nend\n"); + + executeBatchedStoredProc(this.conn); + + batchedConn = getConnectionWithProps("logger=StandardLogger,rewriteBatchedStatements=true,profileSQL=true"); + + StandardLogger.startLoggingToBuffer(); + executeBatchedStoredProc(batchedConn); + String[] log = StandardLogger.getBuffer().toString().split(";"); + assertTrue(log.length > 20); + } finally { + StandardLogger.dropBuffer(); + + if (batchedConn != null) { + batchedConn.close(); + } + } + } + } + + private void executeBatchedStoredProc(Connection c) throws Exception { + this.stmt.executeUpdate("TRUNCATE TABLE testBatchTable"); + + CallableStatement storedProc = c.prepareCall("{call testBatch(?)}"); + + try { + int numBatches = 300; + + for (int i = 0; i < numBatches; i++) { + storedProc.setInt(1, i + 1); + storedProc.addBatch(); + } + + int[] counts = storedProc.executeBatch(); + + assertEquals(numBatches, counts.length); + + for (int i = 0; i < numBatches; i++) { + assertEquals(1, counts[i]); + } + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBatchTable ORDER BY field1 ASC"); + + for (int i = 0; i < numBatches; i++) { + assertTrue(this.rs.next()); + assertEquals(i + 1, this.rs.getInt(1)); + } + } finally { + + if (storedProc != null) { + storedProc.close(); + } + } + } + + /** + * Tests functioning of output parameters. + * + * @throws Exception + * if the test fails. + */ + public void testOutParams() throws Exception { + if (versionMeetsMinimum(5, 0)) { + CallableStatement storedProc = null; + + createProcedure("testOutParam", "(x int, out y int)\nbegin\ndeclare z int;\nset z = x+1, y = z;\nend\n"); + + storedProc = this.conn.prepareCall("{call testOutParam(?, ?)}"); + + storedProc.setInt(1, 5); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + System.out.println(storedProc); + + int indexedOutParamToTest = storedProc.getInt(2); + + int namedOutParamToTest = storedProc.getInt("y"); + + assertTrue("Named and indexed parameter are not the same", indexedOutParamToTest == namedOutParamToTest); + assertTrue("Output value not returned correctly", indexedOutParamToTest == 6); + + // Start over, using named parameters, this time + storedProc.clearParameters(); + storedProc.setInt("x", 32); + storedProc.registerOutParameter("y", Types.INTEGER); + + storedProc.execute(); + + indexedOutParamToTest = storedProc.getInt(2); + namedOutParamToTest = storedProc.getInt("y"); + + assertTrue("Named and indexed parameter are not the same", indexedOutParamToTest == namedOutParamToTest); + assertTrue("Output value not returned correctly", indexedOutParamToTest == 33); + + try { + storedProc.registerOutParameter("x", Types.INTEGER); + assertTrue("Should not be able to register an out parameter on a non-out parameter", true); + } catch (SQLException sqlEx) { + if (!SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } + + try { + storedProc.getInt("x"); + assertTrue("Should not be able to retreive an out parameter on a non-out parameter", true); + } catch (SQLException sqlEx) { + if (!SQLError.SQL_STATE_COLUMN_NOT_FOUND.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } + + try { + storedProc.registerOutParameter(1, Types.INTEGER); + assertTrue("Should not be able to register an out parameter on a non-out parameter", true); + } catch (SQLException sqlEx) { + if (!SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } + } + } + + /** + * Tests functioning of output parameters. + * + * @throws Exception + * if the test fails. + */ + public void testResultSet() throws Exception { + if (versionMeetsMinimum(5, 0)) { + CallableStatement storedProc = null; + + createTable("testSpResultTbl1", "(field1 INT)"); + this.stmt.executeUpdate("INSERT INTO testSpResultTbl1 VALUES (1), (2)"); + createTable("testSpResultTbl2", "(field2 varchar(255))"); + this.stmt.executeUpdate("INSERT INTO testSpResultTbl2 VALUES ('abc'), ('def')"); + + createProcedure("testSpResult", "()\nBEGIN\nSELECT field2 FROM testSpResultTbl2 WHERE field2='abc';\n" + + "UPDATE testSpResultTbl1 SET field1=2;\nSELECT field2 FROM testSpResultTbl2 WHERE field2='def';\nend\n"); + + storedProc = this.conn.prepareCall("{call testSpResult()}"); + + storedProc.execute(); + + this.rs = storedProc.getResultSet(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + assertTrue(rsmd.getColumnCount() == 1); + assertTrue("field2".equals(rsmd.getColumnName(1))); + assertTrue(rsmd.getColumnType(1) == Types.VARCHAR); + + assertTrue(this.rs.next()); + + assertTrue("abc".equals(this.rs.getString(1))); + + // TODO: This does not yet work in MySQL 5.0 + // assertTrue(!storedProc.getMoreResults()); + // assertTrue(storedProc.getUpdateCount() == 2); + assertTrue(storedProc.getMoreResults()); + + ResultSet nextResultSet = storedProc.getResultSet(); + + rsmd = nextResultSet.getMetaData(); + + assertTrue(rsmd.getColumnCount() == 1); + assertTrue("field2".equals(rsmd.getColumnName(1))); + assertTrue(rsmd.getColumnType(1) == Types.VARCHAR); + + assertTrue(nextResultSet.next()); + + assertTrue("def".equals(nextResultSet.getString(1))); + + nextResultSet.close(); + + this.rs.close(); + + storedProc.execute(); + } + } + + /** + * Tests parsing of stored procedures + * + * @throws Exception + * if an error occurs. + */ + public void testSPParse() throws Exception { + + if (versionMeetsMinimum(5, 0)) { + + CallableStatement storedProc = null; + + createProcedure("testSpParse", "(IN FOO VARCHAR(15))\nBEGIN\nSELECT 1;\nend\n"); + + storedProc = this.conn.prepareCall("{call testSpParse()}"); + storedProc.close(); + + } + } + + /** + * Tests parsing/execution of stored procedures with no parameters... + * + * @throws Exception + * if an error occurs. + */ + public void testSPNoParams() throws Exception { + + if (versionMeetsMinimum(5, 0)) { + + CallableStatement storedProc = null; + + createProcedure("testSPNoParams", "()\nBEGIN\nSELECT 1;\nend\n"); + + storedProc = this.conn.prepareCall("{call testSPNoParams()}"); + storedProc.execute(); + + } + } + + /** + * Tests parsing of stored procedures + * + * @throws Exception + * if an error occurs. + */ + public void testSPCache() throws Exception { + if (versionMeetsMinimum(5, 0)) { + + CallableStatement storedProc = null; + + createProcedure("testSpParse", "(IN FOO VARCHAR(15))\nBEGIN\nSELECT 1;\nend\n"); + + int numIterations = 10; + + long startTime = System.currentTimeMillis(); + + for (int i = 0; i < numIterations; i++) { + storedProc = this.conn.prepareCall("{call testSpParse(?)}"); + storedProc.close(); + } + + long elapsedTime = System.currentTimeMillis() - startTime; + + System.out.println("Standard parsing/execution: " + elapsedTime + " ms"); + + storedProc = this.conn.prepareCall("{call testSpParse(?)}"); + storedProc.setString(1, "abc"); + this.rs = storedProc.executeQuery(); + + assertTrue(this.rs.next()); + assertTrue(this.rs.getInt(1) == 1); + + Properties props = new Properties(); + props.setProperty("cacheCallableStmts", "true"); + + Connection cachedSpConn = getConnectionWithProps(props); + + startTime = System.currentTimeMillis(); + + for (int i = 0; i < numIterations; i++) { + storedProc = cachedSpConn.prepareCall("{call testSpParse(?)}"); + storedProc.close(); + } + + elapsedTime = System.currentTimeMillis() - startTime; + + System.out.println("Cached parse stage: " + elapsedTime + " ms"); + + storedProc = cachedSpConn.prepareCall("{call testSpParse(?)}"); + storedProc.setString(1, "abc"); + this.rs = storedProc.executeQuery(); + + assertTrue(this.rs.next()); + assertTrue(this.rs.getInt(1) == 1); + + } + } + + public void testOutParamsNoBodies() throws Exception { + if (versionMeetsMinimum(5, 0)) { + CallableStatement storedProc = null; + + Properties props = new Properties(); + props.setProperty("noAccessToProcedureBodies", "true"); + + Connection spConn = getConnectionWithProps(props); + + createProcedure("testOutParam", "(x int, out y int)\nbegin\ndeclare z int;\nset z = x+1, y = z;\nend\n"); + + storedProc = spConn.prepareCall("{call testOutParam(?, ?)}"); + + storedProc.setInt(1, 5); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + int indexedOutParamToTest = storedProc.getInt(2); + + assertTrue("Output value not returned correctly", indexedOutParamToTest == 6); + + storedProc.clearParameters(); + storedProc.setInt(1, 32); + storedProc.registerOutParameter(2, Types.INTEGER); + + storedProc.execute(); + + indexedOutParamToTest = storedProc.getInt(2); + + assertTrue("Output value not returned correctly", indexedOutParamToTest == 33); + } + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(CallableStatementTest.class); + } + + /** + * Tests the new parameter parser that doesn't require "BEGIN" or "\n" at + * end of parameter declaration + * + * @throws Exception + */ + public void testParameterParser() throws Exception { + + if (!versionMeetsMinimum(5, 0)) { + return; + } + + CallableStatement cstmt = null; + + try { + + createTable("t1", "(id char(16) not null default '', data int not null)"); + createTable("t2", "(s char(16), i int, d double)"); + + createProcedure("foo42", "() insert into test.t1 values ('foo', 42);"); + this.conn.prepareCall("{CALL foo42()}"); + this.conn.prepareCall("{CALL foo42}"); + + createProcedure("bar", "(x char(16), y int, z DECIMAL(10)) insert into test.t1 values (x, y);"); + cstmt = this.conn.prepareCall("{CALL bar(?, ?, ?)}"); + + ParameterMetaData md = cstmt.getParameterMetaData(); + assertEquals(3, md.getParameterCount()); + assertEquals(Types.CHAR, md.getParameterType(1)); + assertEquals(Types.INTEGER, md.getParameterType(2)); + assertEquals(Types.DECIMAL, md.getParameterType(3)); + + createProcedure("p", "() label1: WHILE @a=0 DO SET @a=1; END WHILE"); + this.conn.prepareCall("{CALL p()}"); + + createFunction("f", "() RETURNS INT NO SQL return 1; "); + cstmt = this.conn.prepareCall("{? = CALL f()}"); + + md = cstmt.getParameterMetaData(); + assertEquals(Types.INTEGER, md.getParameterType(1)); + } finally { + if (cstmt != null) { + cstmt.close(); + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/CharsetTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/CharsetTest.java new file mode 100644 index 0000000..5a2cc3e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/CharsetTest.java @@ -0,0 +1,578 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.SortedMap; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.Util; + +import testsuite.BaseTestCase; + +public class CharsetTest extends BaseTestCase { + + public CharsetTest(String name) { + super(name); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(CharsetTest.class); + } + + public void testCP932Backport() throws Exception { + if (versionMeetsMinimum(4, 1, 12)) { + if (versionMeetsMinimum(5, 0)) { + if (!versionMeetsMinimum(5, 0, 3)) { + return; + } + } + + try { + "".getBytes("WINDOWS-31J"); + } catch (UnsupportedEncodingException uee) { + return; + } + + Properties props = new Properties(); + props.put("useUnicode", "true"); + props.put("characterEncoding", "WINDOWS-31J"); + getConnectionWithProps(props).close(); + } + } + + public void testNECExtendedCharsByEUCJPSolaris() throws Exception { + try { + "".getBytes("EUC_JP_Solaris"); + } catch (UnsupportedEncodingException uee) { + return; + } + + if (versionMeetsMinimum(5, 0, 5)) { + char necExtendedChar = 0x3231; // 0x878A of WINDOWS-31J, NEC + // special(row13). + String necExtendedCharString = String.valueOf(necExtendedChar); + + Properties props = new Properties(); + + props.put("useUnicode", "true"); + props.put("characterEncoding", "EUC_JP_Solaris"); + + Connection conn2 = getConnectionWithProps(props); + Statement stmt2 = conn2.createStatement(); + + createTable("t_eucjpms", "(c1 char(1)) default character set = eucjpms"); + stmt2.executeUpdate("INSERT INTO t_eucjpms VALUES ('" + necExtendedCharString + "')"); + this.rs = stmt2.executeQuery("SELECT c1 FROM t_eucjpms"); + this.rs.next(); + assertEquals(necExtendedCharString, this.rs.getString("c1")); + + this.rs.close(); + stmt2.close(); + conn2.close(); + + props.put("characterSetResults", "EUC_JP_Solaris"); + conn2 = getConnectionWithProps(props); + stmt2 = this.conn.createStatement(); + + this.rs = stmt2.executeQuery("SELECT c1 FROM t_eucjpms"); + this.rs.next(); + assertEquals(necExtendedCharString, this.rs.getString("c1")); + + this.rs.close(); + stmt2.close(); + conn2.close(); + } + } + + /** + * Test data of sjis. sjis consists of ASCII, JIS-Roman, JISX0201 and + * JISX0208. + */ + public static final char[] SJIS_CHARS = new char[] { 0xFF71, // halfwidth katakana letter A, 0xB100 of SJIS, one of JISX0201. + 0x65E5, // CJK unified ideograph, 0x93FA of SJIS, one of JISX0208. + 0x8868, // CJK unified ideograph, 0x955C of SJIS, one of '5c' character. + 0x2016 // 0x8161 of SJIS/WINDOWS-31J, converted to differently to/from ucs2 + }; + + /** + * Test data of cp932. WINDOWS-31J consists of ASCII, JIS-Roman, JISX0201, + * JISX0208, NEC special characters(row13), NEC selected IBM special + * characters, and IBM special characters. + */ + private static final char[] CP932_CHARS = new char[] { 0xFF71, // halfwidth katakana letter A, 0xB100 of WINDOWS-31J, one of JISX0201. + 0x65E5, // CJK unified ideograph, 0x93FA of WINDOWS-31J, one of JISX0208. + 0x3231, // parenthesized ideograph stok, 0x878B of WINDOWS-31J, one of NEC special characters(row13). + 0x67BB, // CJK unified ideograph, 0xEDC6 of WINDOWS-31J, one of NEC selected IBM special characters. + 0x6D6F, // CJK unified ideograph, 0xFAFC of WINDOWS-31J, one of IBM special characters. + 0x8868, // one of CJK unified ideograph, 0x955C of WINDOWS-31J, one of '5c' characters. + 0x2225 // 0x8161 of SJIS/WINDOWS-31J, converted to differently to/from ucs2 + }; + + /** + * Test data of ujis. ujis consists of ASCII, JIS-Roman, JISX0201, JISX0208, + * JISX0212. + */ + public static final char[] UJIS_CHARS = new char[] { 0xFF71, // halfwidth katakana letter A, 0x8EB1 of ujis, one of JISX0201. + 0x65E5, // CJK unified ideograph, 0xC6FC of ujis, one of JISX0208. + 0x7B5D, // CJK unified ideograph, 0xE4B882 of ujis, one of JISX0212 + 0x301C // wave dash, 0xA1C1 of ujis, convertion rule is different from ujis + }; + + /** + * Test data of eucjpms. ujis consists of ASCII, JIS-Roman, JISX0201, + * JISX0208, JISX0212, NEC special characters(row13) + */ + public static final char[] EUCJPMS_CHARS = new char[] { 0xFF71, // halfwidth katakana letter A, 0x8EB1 of ujis, one of JISX0201. + 0x65E5, // CJK unified ideograph, 0xC6FC of ujis, one of JISX0208. + 0x7B5D, // CJK unified ideograph, 0xE4B882 of ujis, one of JISX0212 + 0x3231, // parenthesized ideograph stok, 0x878A of WINDOWS-31J, one of NEC special characters(row13). + 0xFF5E // wave dash, 0xA1C1 of eucjpms, convertion rule is different from ujis + }; + + public void testInsertCharStatement() throws Exception { + try { + "".getBytes("SJIS"); + } catch (UnsupportedEncodingException uee) { + return; + } + + // SJIS is fully supported only in Java 1.5.0_38 and above. + if (versionMeetsMinimum(4, 1, 12) && Util.jvmMeetsMinimum(5, 38)) { + Map testDataMap = new HashMap(); + + List charsetList = new ArrayList(); + + Map connectionMap = new HashMap(); + + Map connectionWithResultMap = new HashMap(); + + Map statementMap = new HashMap(); + + Map statementWithResultMap = new HashMap(); + + Map javaToMysqlCharsetMap = new HashMap(); + + charsetList.add("SJIS"); + testDataMap.put("SJIS", SJIS_CHARS); + javaToMysqlCharsetMap.put("SJIS", "sjis"); + + charsetList.add("Shift_JIS"); + testDataMap.put("Shift_JIS", SJIS_CHARS); + javaToMysqlCharsetMap.put("Shift_JIS", "sjis"); + + charsetList.add("CP943"); + testDataMap.put("CP943", SJIS_CHARS); + javaToMysqlCharsetMap.put("CP943", "sjis"); + + if (versionMeetsMinimum(5, 0, 3)) { + charsetList.add("WINDOWS-31J"); + testDataMap.put("WINDOWS-31J", CP932_CHARS); + javaToMysqlCharsetMap.put("WINDOWS-31J", "cp932"); + + charsetList.add("MS932"); + testDataMap.put("MS932", CP932_CHARS); + javaToMysqlCharsetMap.put("MS932", "cp932"); + + charsetList.add("EUC_JP"); + testDataMap.put("EUC_JP", UJIS_CHARS); + // testDataHexMap.put("EUC_JP", UJIS_CHARS_HEX); + javaToMysqlCharsetMap.put("EUC_JP", "ujis"); + + charsetList.add("EUC_JP_Solaris"); + testDataMap.put("EUC_JP_Solaris", EUCJPMS_CHARS); + // testDataHexMap.put("EUC_JP_Solaris", EUCJPMS_CHARS_HEX); + javaToMysqlCharsetMap.put("EUC_JP_Solaris", "eucjpms"); + + } else { + charsetList.add("EUC_JP"); + testDataMap.put("EUC_JP", UJIS_CHARS); + javaToMysqlCharsetMap.put("EUC_JP", "ujis"); + } + + for (String charset : charsetList) { + Properties props = new Properties(); + + props.put("useUnicode", "true"); + props.put("characterEncoding", charset); + Connection conn2 = getConnectionWithProps(props); + connectionMap.put(charset.toLowerCase(Locale.ENGLISH), conn2); + statementMap.put(charset.toLowerCase(Locale.ENGLISH), conn2.createStatement()); + + props.put("characterSetResult", charset); + Connection connWithResult = getConnectionWithProps(props); + connectionWithResultMap.put(charset, connWithResult); + statementWithResultMap.put(charset, connWithResult.createStatement()); + } + + for (String charset : charsetList) { + String mysqlCharset = javaToMysqlCharsetMap.get(charset); + Statement stmt2 = statementMap.get(charset.toLowerCase(Locale.ENGLISH)); + String query1 = "DROP TABLE IF EXISTS t1"; + String query2 = "CREATE TABLE t1 (c1 int, c2 char(1)) DEFAULT CHARACTER SET = " + mysqlCharset; + stmt2.executeUpdate(query1); + stmt2.executeUpdate(query2); + char[] testData = testDataMap.get(charset); + for (int i = 0; i < testData.length; i++) { + String query3 = "INSERT INTO t1 values(" + i + ", '" + testData[i] + "')"; + stmt2.executeUpdate(query3); + String query4 = "SELECT c2 FROM t1 WHERE c1 = " + i; + this.rs = stmt2.executeQuery(query4); + this.rs.next(); + String value = this.rs.getString(1); + + assertEquals("For character set " + charset + "/ " + mysqlCharset, String.valueOf(testData[i]), value); + } + String query5 = "DROP TABLE t1"; + stmt2.executeUpdate(query5); + } + } + } + + public void testUtf8OutsideBMPInBlob() throws Exception { + createTable("utf8Test", + "(include_blob BLOB, include_tinyblob TINYBLOB, include_longblob LONGBLOB, exclude_tinyblob TINYBLOB, exclude_blob BLOB, exclude_longblob LONGBLOB)"); + + // We know this gets truncated in MySQL currently, even though it's valid UTF-8, it's just 4 bytes encoded + String outsideBmp = new String(new byte[] { (byte) 0xF0, (byte) 0x90, (byte) 0x80, (byte) 0x80 }, "UTF-8"); + byte[] outsideBmpBytes = outsideBmp.getBytes("UTF-8"); + System.out.println(outsideBmpBytes.length); + + Connection utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8"); + + String insertStatement = "INSERT INTO utf8Test VALUES (?, ?, ?, ?, ?, ?)"; + + this.pstmt = utf8Conn.prepareStatement(insertStatement); + + this.pstmt.setString(1, outsideBmp); + this.pstmt.setString(2, outsideBmp); + this.pstmt.setString(3, outsideBmp); + this.pstmt.setString(4, outsideBmp); + this.pstmt.setString(5, outsideBmp); + this.pstmt.setString(6, outsideBmp); + this.pstmt.executeUpdate(); + + String query = "SELECT include_blob, include_tinyblob, include_longblob, exclude_tinyblob, exclude_blob, exclude_longblob FROM utf8Test"; + ResultSet rset = utf8Conn.createStatement().executeQuery(query); + rset.next(); + + assertEquals(rset.getObject(1).toString(), outsideBmp); + assertEquals(rset.getObject(2).toString(), outsideBmp); + assertEquals(rset.getObject(3).toString(), outsideBmp); + assertEquals(rset.getObject(4).toString(), outsideBmp); + assertEquals(rset.getObject(5).toString(), outsideBmp); + assertEquals(rset.getObject(6).toString(), outsideBmp); + + assertEquals("java.lang.String", rset.getObject(1).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(1)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(1)); + + assertEquals("java.lang.String", rset.getObject(2).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(2)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(2)); + + assertEquals("java.lang.String", rset.getObject(3).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(3)); + assertEquals(Types.LONGVARCHAR, rset.getMetaData().getColumnType(3)); + + assertEquals("java.lang.String", rset.getObject(4).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(4)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(4)); + + assertEquals("java.lang.String", rset.getObject(5).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(5)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(5)); + + assertEquals("java.lang.String", rset.getObject(6).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(6)); + assertEquals(Types.LONGVARCHAR, rset.getMetaData().getColumnType(6)); + + utf8Conn = getConnectionWithProps( + "useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern=.*include.*,utf8OutsideBmpExcludedColumnNamePattern=.*blob"); + + rset = utf8Conn.createStatement().executeQuery(query); + rset.next(); + + // Should walk/talk like a string, encoded in utf-8 on the server (4-byte) + assertEquals(rset.getObject(1).toString(), outsideBmp); + assertEquals(rset.getObject(2).toString(), outsideBmp); + assertEquals(rset.getObject(3).toString(), outsideBmp); + + assertEquals("java.lang.String", rset.getObject(1).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(1)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(1)); + + assertEquals("java.lang.String", rset.getObject(2).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(2)); + assertEquals(Types.VARCHAR, rset.getMetaData().getColumnType(2)); + + assertEquals("java.lang.String", rset.getObject(3).getClass().getName()); + assertEquals("java.lang.String", rset.getMetaData().getColumnClassName(3)); + assertEquals(Types.LONGVARCHAR, rset.getMetaData().getColumnType(3)); + + // These should be left as a blob, since it matches the exclusion regex + assertTrue(bytesAreSame(rset.getBytes(4), outsideBmpBytes)); + assertEquals("[B", rset.getObject(4).getClass().getName()); + assertEquals("[B", rset.getMetaData().getColumnClassName(4)); + assertEquals(Types.VARBINARY, rset.getMetaData().getColumnType(4)); + + // Should behave types-wise just like BLOB, including LONGVARBINARY type mapping + assertTrue(bytesAreSame(rset.getBytes(5), outsideBmpBytes)); + assertEquals("[B", rset.getObject(5).getClass().getName()); + assertEquals("[B", rset.getMetaData().getColumnClassName(5)); + assertEquals(Types.LONGVARBINARY, rset.getMetaData().getColumnType(5)); + + assertTrue(bytesAreSame(rset.getBytes(6), outsideBmpBytes)); + assertEquals("[B", rset.getObject(6).getClass().getName()); + assertEquals("[B", rset.getMetaData().getColumnClassName(6)); + assertEquals(Types.LONGVARBINARY, rset.getMetaData().getColumnType(6)); + + // + // Check error handling + // + + utf8Conn = getConnectionWithProps( + "useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern={{"); + + try { + rset = utf8Conn.createStatement().executeQuery(query); + fail("Expected an exception"); + } catch (SQLException sqlEx) { + assertNotNull(sqlEx.getCause()); + assertEquals("java.util.regex.PatternSyntaxException", sqlEx.getCause().getClass().getName()); + } + + utf8Conn = getConnectionWithProps( + "useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern=.*"); + + try { + rset = utf8Conn.createStatement().executeQuery(query); + fail("Expected an exception"); + } catch (SQLException sqlEx) { + assertNotNull(sqlEx.getCause()); + assertEquals("java.util.regex.PatternSyntaxException", sqlEx.getCause().getClass().getName()); + } + + utf8Conn = getConnectionWithProps( + "useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern={{,paranoid=true"); + + try { + rset = utf8Conn.createStatement().executeQuery(query); + fail("Expected an exception"); + } catch (SQLException sqlEx) { + assertNull(sqlEx.getCause()); + } + + utf8Conn = getConnectionWithProps( + "useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern=.*,paranoid=true"); + + try { + rset = utf8Conn.createStatement().executeQuery(query); + fail("Expected an exception"); + } catch (SQLException sqlEx) { + assertNull(sqlEx.getCause()); + } + } + + private boolean bytesAreSame(byte[] byte1, byte[] byte2) { + if (byte1.length != byte2.length) { + return false; + } + + for (int i = 0; i < byte1.length; i++) { + if (byte1[i] != byte2[i]) { + return false; + } + } + + return true; + } + + /** + * Prints static mappings for analysis. + * + * @throws Exception + */ + public void testCharsetMapping() throws Exception { + SortedMap availableCharsets = Charset.availableCharsets(); + Set k = availableCharsets.keySet(); + System.out.println("Java encoding --> Initial encoding (Can encode), Encoding by index, Index by encoding, collation by index, charset by index..."); + System.out.println("==================================="); + Iterator i1 = k.iterator(); + while (i1.hasNext()) { + String canonicalName = i1.next(); + java.nio.charset.Charset cs = availableCharsets.get(canonicalName); + canonicalName = cs.name(); + + int index = CharsetMapping.getCollationIndexForJavaEncoding(canonicalName, this.conn); + String csname = CharsetMapping.getMysqlCharsetNameForCollationIndex(index); + + System.out.println((canonicalName + " ").substring(0, 26) + " (" + cs.canEncode() + ") --> " + + CharsetMapping.getJavaEncodingForCollationIndex(index) + " : " + index + " : " + + CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[index] + " : " + CharsetMapping.getMysqlCharsetNameForCollationIndex(index) + " : " + + CharsetMapping.CHARSET_NAME_TO_CHARSET.get(csname) + " : " + CharsetMapping.getJavaEncodingForMysqlCharset(csname) + " : " + + CharsetMapping.getMysqlCharsetForJavaEncoding(canonicalName, (com.mysql.jdbc.Connection) this.conn) + " : " + + CharsetMapping.getCollationIndexForJavaEncoding(canonicalName, this.conn) + " : " + CharsetMapping.isMultibyteCharset(canonicalName)); + + Set s = cs.aliases(); + Iterator j = s.iterator(); + while (j.hasNext()) { + String alias = j.next(); + index = CharsetMapping.getCollationIndexForJavaEncoding(alias, this.conn); + csname = CharsetMapping.getMysqlCharsetNameForCollationIndex(index); + System.out.println(" " + (alias + " ").substring(0, 30) + " --> " + + CharsetMapping.getJavaEncodingForCollationIndex(index) + " : " + index + " : " + + CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[index] + " : " + CharsetMapping.getMysqlCharsetNameForCollationIndex(index) + + " : " + CharsetMapping.CHARSET_NAME_TO_CHARSET.get(csname) + " : " + CharsetMapping.getJavaEncodingForMysqlCharset(csname) + + " : " + CharsetMapping.getMysqlCharsetForJavaEncoding(alias, (com.mysql.jdbc.Connection) this.conn) + " : " + + CharsetMapping.getCollationIndexForJavaEncoding(alias, this.conn) + " : " + CharsetMapping.isMultibyteCharset(alias)); + } + System.out.println("==================================="); + } + for (int i = 1; i < CharsetMapping.MAP_SIZE; i++) { + String csname = CharsetMapping.getMysqlCharsetNameForCollationIndex(i); + String enc = CharsetMapping.getJavaEncodingForCollationIndex(i); + System.out.println((i + " ").substring(0, 4) + " by index--> " + + (CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[i] + " ").substring(0, 20) + " : " + + (csname + " ").substring(0, 10) + " : " + (enc + " ").substring(0, 20) + + + " by charset--> " + (CharsetMapping.getJavaEncodingForMysqlCharset(csname) + " ").substring(0, 20) + + + " by encoding--> " + (CharsetMapping.getCollationIndexForJavaEncoding(enc, this.conn) + " ").substring(0, 4) + " : " + + (CharsetMapping.getMysqlCharsetForJavaEncoding(enc, (com.mysql.jdbc.Connection) this.conn) + " ").substring(0, 15)); + } + } + + /** + * Test for the gb18030 character set + * + * @throws Exception + */ + public void testGB18030() throws Exception { + // check that server supports this character set + this.rs = this.stmt.executeQuery("show collation like 'gb18030_chinese_ci'"); + if (!this.rs.next()) { + return; + } + + // phrases to check + String[][] str = new String[][] { + { "C4EEC5ABBDBFA1A4B3E0B1DABBB3B9C520A1A4CBD5B6ABC6C2", "\u5FF5\u5974\u5A07\u00B7\u8D64\u58C1\u6000\u53E4 \u00B7\u82CF\u4E1C\u5761" }, + { "B4F3BDADB6ABC8A5A3ACC0CBCCD4BEA1A1A2C7A7B9C5B7E7C1F7C8CBCEEFA1A3", + "\u5927\u6C5F\u4E1C\u53BB\uFF0C\u6D6A\u6DD8\u5C3D\u3001\u5343\u53E4\u98CE\u6D41\u4EBA\u7269\u3002" }, + { "B9CAC0DDCEF7B1DFA3ACC8CBB5C0CAC7A1A2C8FDB9FAD6DCC0C9B3E0B1DAA1A3", + "\u6545\u5792\u897F\u8FB9\uFF0C\u4EBA\u9053\u662F\u3001\u4E09\u56FD\u5468\u90CE\u8D64\u58C1\u3002" }, + { "C2D2CAAFB1C0D4C6A3ACBEAACCCEC1D1B0B6A3ACBEEDC6F0C7A7B6D1D1A9A1A3", + "\u4E71\u77F3\u5D29\u4E91\uFF0C\u60CA\u6D9B\u88C2\u5CB8\uFF0C\u5377\u8D77\u5343\u5806\u96EA\u3002" }, + { "BDADC9BDC8E7BBADA3ACD2BBCAB1B6E0C9D9BAC0BDDCA3A1", "\u6C5F\u5C71\u5982\u753B\uFF0C\u4E00\u65F6\u591A\u5C11\u8C6A\u6770\uFF01" }, + { "D2A3CFEBB9ABE8AAB5B1C4EAA3ACD0A1C7C7B3F5BCDEC1CBA3ACD0DBD7CBD3A2B7A2A1A3", + "\u9065\u60F3\u516C\u747E\u5F53\u5E74\uFF0C\u5C0F\u4E54\u521D\u5AC1\u4E86\uFF0C\u96C4\u59FF\u82F1\u53D1\u3002" }, + { "D3F0C9C8C2DABDEDA3ACCCB8D0A6BCE4A1A2E9C9E9D6BBD2B7C9D1CCC3F0A1A3", + "\u7FBD\u6247\u7EB6\u5DFE\uFF0C\u8C08\u7B11\u95F4\u3001\u6A2F\u6A79\u7070\u98DE\u70DF\u706D\u3002" }, + { "B9CAB9FAC9F1D3CEA3ACB6E0C7E9D3A6D0A6CED2A1A2D4E7C9FABBAAB7A2A1A3", + "\u6545\u56FD\u795E\u6E38\uFF0C\u591A\u60C5\u5E94\u7B11\u6211\u3001\u65E9\u751F\u534E\u53D1\u3002" }, + { "C8CBBCE4C8E7C3CEA3ACD2BBE9D7BBB9F5AABDADD4C2A1A3", "\u4EBA\u95F4\u5982\u68A6\uFF0C\u4E00\u6A3D\u8FD8\u9179\u6C5F\u6708\u3002" }, + { "5373547483329330", "SsTt\uC23F" }, { "8239AB318239AB358239AF3583308132833087348335EB39", "\uB46C\uB470\uB498\uB7B5\uB7F3\uD47C" }, + { "97339631973396339733A6359831C0359831C536", "\uD85A\uDC1F\uD85A\uDC21\uD85A\uDCC3\uD864\uDD27\uD864\uDD5A" }, + { "9835CF329835CE359835F336", "\uD869\uDD6A\uD869\uDD63\uD869\uDED6" }, { "833988318339883283398539", "\uF45A\uF45B\uF444" }, + { "823398318233973582339A3882348A32", "\u4460\u445A\u447B\u48C8" }, { "8134D5318134D6328134D832", "\u1817\u1822\u1836" }, + { "4A7320204B82339A35646566", "Js K\u4478def" }, { "8130883281308833", "\u00CE\u00CF" }, { "E05FE06A777682339230", "\u90F7\u9107wv\u4423" }, + { "814081418139FE30", "\u4E02\u4E04\u3499" }, { "81308130FEFE", "\u0080\uE4C5" }, { "E3329A35E3329A34", "\uDBFF\uDFFF\uDBFF\uDFFE" } }; + HashMap expected = new HashMap(); + + // check variables + Connection con = getConnectionWithProps("characterEncoding=GB18030"); + Statement st = con.createStatement(); + ResultSet rset = st.executeQuery("show variables like 'character_set_client'"); + rset.next(); + assertEquals("gb18030", rset.getString(2)); + rset = st.executeQuery("show variables like 'character_set_connection'"); + rset.next(); + assertEquals("gb18030", rset.getString(2)); + rset = st.executeQuery("show variables like 'collation_connection'"); + rset.next(); + assertEquals("gb18030_chinese_ci", rset.getString(2)); + + st.executeUpdate("DROP TABLE IF EXISTS testGB18030"); + st.executeUpdate("CREATE TABLE testGB18030(C VARCHAR(100) CHARACTER SET gb18030)"); + + // insert phrases + PreparedStatement pst = null; + pst = con.prepareStatement("INSERT INTO testGB18030 VALUES(?)"); + for (int i = 0; i < str.length; i++) { + expected.put(str[i][0], str[i][1]); + pst.setString(1, str[i][1]); + pst.addBatch(); + } + pst.executeBatch(); + + // read phrases + rset = st.executeQuery("SELECT c, HEX(c), CONVERT(c USING utf8mb4) FROM testGB18030"); + int resCount = 0; + while (rset.next()) { + resCount++; + String hex = rset.getString(2); + assertTrue("HEX value " + hex + " for char " + rset.getString(1) + " is unexpected", expected.containsKey(hex)); + assertEquals(expected.get(hex), rset.getString(1)); + assertEquals(expected.get(hex), rset.getString(3)); + } + assertEquals(str.length, resCount); + + // chars that can't be converted to utf8/utf16 + st.executeUpdate("TRUNCATE TABLE testGB18030"); + st.executeUpdate("INSERT INTO testGB18030 VALUES(0xFE39FE39FE38FE38),(0xFE39FE38A976)"); + rset = st.executeQuery("SELECT c, HEX(c), CONVERT(c USING utf8mb4) FROM testGB18030"); + while (rset.next()) { + String hex = rset.getString(2); + if ("FE39FE39FE38FE38".equals(hex)) { + assertEquals("\uFFFD\uFFFD", rset.getString(1)); + assertEquals("??", rset.getString(3)); + } else if ("FE39FE38A976".equals(hex)) { + assertEquals("\uFFFD\uFE59", rset.getString(1)); + assertEquals("?\uFE59", rset.getString(3)); + } else { + fail("HEX value " + hex + " unexpected"); + } + } + + st.executeUpdate("DROP TABLE IF EXISTS testGB18030"); + con.close(); + + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/ConnectionTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/ConnectionTest.java new file mode 100644 index 0000000..40bc337 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/ConnectionTest.java @@ -0,0 +1,1963 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.TimeZone; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NonRegisteringDriver; +import com.mysql.jdbc.ResultSetInternalMethods; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.log.StandardLogger; + +import testsuite.BaseStatementInterceptor; +import testsuite.BaseTestCase; + +/** + * Tests java.sql.Connection functionality + */ +public class ConnectionTest extends BaseTestCase { + /** + * Constructor for ConnectionTest. + * + * @param name + * the name of the test to run + */ + public ConnectionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ConnectionTest.class); + } + + /** + * Tests catalog functionality + * + * @throws Exception + * if an error occurs + */ + public void testCatalog() throws Exception { + String currentCatalog = this.conn.getCatalog(); + this.conn.setCatalog(currentCatalog); + assertTrue(currentCatalog.equals(this.conn.getCatalog())); + } + + /** + * Tests a cluster connection for failover, requires a two-node cluster URL + * specfied in com.mysql.jdbc.testsuite.ClusterUrl system proeprty. + * + * @throws Exception + */ + public void testClusterConnection() throws Exception { + String url = System.getProperty("com.mysql.jdbc.testsuite.ClusterUrl"); + + if ((url != null) && (url.length() > 0)) { + Object versionNumObj = getSingleValueWithQuery("SHOW VARIABLES LIKE 'version'"); + + if ((versionNumObj != null) && (versionNumObj.toString().indexOf("cluster") != -1)) { + Connection clusterConn = null; + Statement clusterStmt = null; + + try { + clusterConn = new NonRegisteringDriver().connect(url, null); + + clusterStmt = clusterConn.createStatement(); + clusterStmt.executeUpdate("DROP TABLE IF EXISTS testClusterConn"); + clusterStmt.executeUpdate("CREATE TABLE testClusterConn (field1 INT) " + getTableTypeDecl() + " =ndbcluster"); + clusterStmt.executeUpdate("INSERT INTO testClusterConn VALUES (1)"); + + clusterConn.setAutoCommit(false); + + clusterStmt.execute("SELECT * FROM testClusterConn"); + clusterStmt.executeUpdate("UPDATE testClusterConn SET field1=4"); + + // Kill the connection + @SuppressWarnings("unused") + String connectionId = getSingleValueWithQuery("SELECT CONNECTION_ID()").toString(); + + System.out.println("Please kill the MySQL server now and press return..."); + System.in.read(); + + System.out.println("Waiting for TCP/IP timeout..."); + Thread.sleep(10); + + System.out.println("Attempting auto reconnect"); + + try { + clusterConn.setAutoCommit(true); + clusterConn.setAutoCommit(false); + } catch (SQLException sqlEx) { + System.out.println(sqlEx); + } + + // + // Test that this 'new' connection is not read-only + // + clusterStmt.executeUpdate("UPDATE testClusterConn SET field1=5"); + + ResultSet rset = clusterStmt.executeQuery("SELECT * FROM testClusterConn WHERE field1=5"); + + assertTrue("One row should be returned", rset.next()); + } finally { + if (clusterStmt != null) { + clusterStmt.executeUpdate("DROP TABLE IF EXISTS testClusterConn"); + clusterStmt.close(); + } + + if (clusterConn != null) { + clusterConn.close(); + } + } + } + } + } + + /** + * @throws Exception + * Old test was passing due to + * http://bugs.mysql.com/bug.php?id=989 which is fixed for 5.5+ + */ + public void testDeadlockDetection() throws Exception { + try { + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'innodb_lock_wait_timeout'"); + this.rs.next(); + + int timeoutSecs = this.rs.getInt(2); + + createTable("t1", "(id INTEGER, x INTEGER) ", "INNODB"); + this.stmt.executeUpdate("INSERT INTO t1 VALUES(0, 0)"); + this.conn.setAutoCommit(false); + + Properties props = new Properties(); + props.setProperty("includeInnodbStatusInDeadlockExceptions", "true"); + props.setProperty("includeThreadDumpInDeadlockExceptions", "true"); + + Connection deadlockConn = getConnectionWithProps(props); + deadlockConn.setAutoCommit(false); + + try { + this.conn.createStatement().execute("SELECT * FROM t1 WHERE id=0 FOR UPDATE"); + + // The following query should hang because con1 is locking the page + deadlockConn.createStatement().executeUpdate("UPDATE t1 SET x=2 WHERE id=0"); + } finally { + if (versionMeetsMinimum(5, 5)) { + this.conn.commit(); + deadlockConn.commit(); + } + } + + Thread.sleep(timeoutSecs * 2 * 1000); + } catch (SQLException sqlEx) { + System.out.println("Caught SQLException due to deadlock/lock timeout"); + System.out.println("SQLState: " + sqlEx.getSQLState()); + System.out.println("Vendor error: " + sqlEx.getErrorCode()); + System.out.println("Message: " + sqlEx.getMessage()); + + // + // Check whether the driver thinks it really is deadlock... + // + assertTrue(SQLError.SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE.equals(sqlEx.getSQLState())); + assertTrue(sqlEx.getErrorCode() == 1205); + // Make sure INNODB Status is getting dumped into error message + + if (sqlEx.getMessage().indexOf("PROCESS privilege") != -1) { + fail("This test requires user with process privilege"); + } + + assertTrue("Can't find INNODB MONITOR in:\n\n" + sqlEx.getMessage(), sqlEx.getMessage().indexOf("INNODB MONITOR") != -1); + + assertTrue("Can't find thread dump in:\n\n" + sqlEx.getMessage(), + sqlEx.getMessage().indexOf("testsuite.simple.ConnectionTest.testDeadlockDetection") != -1); + + } finally { + this.conn.setAutoCommit(true); + } + } + + public void testCharsets() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Properties props = new Properties(); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "UTF-8"); + + Connection utfConn = getConnectionWithProps(props); + + this.stmt = utfConn.createStatement(); + + createTable("t1", "(comment CHAR(32) ASCII NOT NULL,koi8_ru_f CHAR(32) CHARACTER SET koi8r NOT NULL) CHARSET=latin5"); + + this.stmt.executeUpdate("ALTER TABLE t1 CHANGE comment comment CHAR(32) CHARACTER SET latin2 NOT NULL"); + this.stmt.executeUpdate("ALTER TABLE t1 ADD latin5_f CHAR(32) NOT NULL"); + this.stmt.executeUpdate("ALTER TABLE t1 CHARSET=latin2"); + this.stmt.executeUpdate("ALTER TABLE t1 ADD latin2_f CHAR(32) NOT NULL"); + this.stmt.executeUpdate("ALTER TABLE t1 DROP latin2_f, DROP latin5_f"); + + this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) VALUES ('a','LAT SMALL A')"); + /* + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('b','LAT SMALL B')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('c','LAT SMALL C')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('d','LAT SMALL D')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('e','LAT SMALL E')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('f','LAT SMALL F')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('g','LAT SMALL G')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('h','LAT SMALL H')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('i','LAT SMALL I')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('j','LAT SMALL J')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('k','LAT SMALL K')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('l','LAT SMALL L')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('m','LAT SMALL M')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('n','LAT SMALL N')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('o','LAT SMALL O')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('p','LAT SMALL P')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('q','LAT SMALL Q')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('r','LAT SMALL R')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('s','LAT SMALL S')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('t','LAT SMALL T')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('u','LAT SMALL U')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('v','LAT SMALL V')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('w','LAT SMALL W')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('x','LAT SMALL X')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('y','LAT SMALL Y')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('z','LAT SMALL Z')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('A','LAT CAPIT A')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('B','LAT CAPIT B')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('C','LAT CAPIT C')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('D','LAT CAPIT D')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('E','LAT CAPIT E')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('F','LAT CAPIT F')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('G','LAT CAPIT G')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('H','LAT CAPIT H')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('I','LAT CAPIT I')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('J','LAT CAPIT J')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('K','LAT CAPIT K')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('L','LAT CAPIT L')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('M','LAT CAPIT M')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('N','LAT CAPIT N')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('O','LAT CAPIT O')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('P','LAT CAPIT P')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('Q','LAT CAPIT Q')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('R','LAT CAPIT R')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('S','LAT CAPIT S')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('T','LAT CAPIT T')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('U','LAT CAPIT U')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('V','LAT CAPIT V')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('W','LAT CAPIT W')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('X','LAT CAPIT X')"); this.stmt.executeUpdate("INSERT + * INTO t1 (koi8_ru_f,comment) VALUES ('Y','LAT CAPIT Y')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES ('Z','LAT CAPIT Z')"); + */ + + String cyrillicSmallA = "\u0430"; + this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) VALUES ('" + cyrillicSmallA + "','CYR SMALL A')"); + + /* + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL BE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL VE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL GE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL DE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL IE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL IO')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL ZHE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL ZE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL I')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL KA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL EL')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL EM')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL EN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL O')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL PE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL ER')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL ES')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL TE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL U')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL EF')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL HA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL TSE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL CHE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL SHA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL SCHA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL HARD SIGN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL YERU')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL SOFT SIGN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL E')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL YU')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR SMALL YA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT A')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT BE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT VE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT GE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT DE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT IE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT IO')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT ZHE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT ZE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT I')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT KA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT EL')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT EM')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT EN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT O')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT PE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT ER')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT ES')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT TE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT U')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT EF')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT HA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT TSE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT CHE')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT SHA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT SCHA')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT HARD SIGN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT YERU')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT SOFT SIGN')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT E')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT YU')"); + * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) + * VALUES (_koi8r'?��','CYR CAPIT YA')"); + */ + + this.stmt.executeUpdate("ALTER TABLE t1 ADD utf8_f CHAR(32) CHARACTER SET utf8 NOT NULL"); + this.stmt.executeUpdate("UPDATE t1 SET utf8_f=CONVERT(koi8_ru_f USING utf8)"); + this.stmt.executeUpdate("SET CHARACTER SET koi8r"); + // this.stmt.executeUpdate("SET CHARACTER SET UTF8"); + this.rs = this.stmt.executeQuery("SELECT * FROM t1"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + int numColumns = rsmd.getColumnCount(); + + for (int i = 0; i < numColumns; i++) { + System.out.print(rsmd.getColumnName(i + 1)); + System.out.print("\t\t"); + } + + System.out.println(); + + while (this.rs.next()) { + System.out.println(this.rs.getString(1) + "\t\t" + this.rs.getString(2) + "\t\t" + this.rs.getString(3)); + + if (this.rs.getString(1).equals("CYR SMALL A")) { + this.rs.getString(2); + } + } + + System.out.println(); + + this.stmt.executeUpdate("SET NAMES utf8"); + this.rs = this.stmt.executeQuery("SELECT _koi8r 0xC1;"); + + rsmd = this.rs.getMetaData(); + + numColumns = rsmd.getColumnCount(); + + for (int i = 0; i < numColumns; i++) { + System.out.print(rsmd.getColumnName(i + 1)); + System.out.print("\t\t"); + } + + System.out.println(); + + while (this.rs.next()) { + System.out.println(this.rs.getString(1).equals("\u0430") + "\t\t"); + System.out.println(new String(this.rs.getBytes(1), "KOI8_R")); + + } + + char[] c = new char[] { 0xd0b0 }; + + System.out.println(new String(c)); + System.out.println("\u0430"); + } + } + + /** + * Tests isolation level functionality + * + * @throws Exception + * if an error occurs + */ + public void testIsolationLevel() throws Exception { + if (versionMeetsMinimum(4, 0)) { + String[] isoLevelNames = new String[] { "Connection.TRANSACTION_NONE", "Connection.TRANSACTION_READ_COMMITTED", + "Connection.TRANSACTION_READ_UNCOMMITTED", "Connection.TRANSACTION_REPEATABLE_READ", "Connection.TRANSACTION_SERIALIZABLE" }; + + int[] isolationLevels = new int[] { Connection.TRANSACTION_NONE, Connection.TRANSACTION_READ_COMMITTED, Connection.TRANSACTION_READ_UNCOMMITTED, + Connection.TRANSACTION_REPEATABLE_READ, Connection.TRANSACTION_SERIALIZABLE }; + + DatabaseMetaData dbmd = this.conn.getMetaData(); + + for (int i = 0; i < isolationLevels.length; i++) { + if (dbmd.supportsTransactionIsolationLevel(isolationLevels[i])) { + this.conn.setTransactionIsolation(isolationLevels[i]); + + assertTrue( + "Transaction isolation level that was set (" + isoLevelNames[i] + + ") was not returned, nor was a more restrictive isolation level used by the server", + this.conn.getTransactionIsolation() == isolationLevels[i] || this.conn.getTransactionIsolation() > isolationLevels[i]); + } + } + } + } + + /** + * Tests the savepoint functionality in MySQL. + * + * @throws Exception + * if an error occurs. + */ + public void testSavepoint() throws Exception { + DatabaseMetaData dbmd = this.conn.getMetaData(); + + if (dbmd.supportsSavepoints()) { + System.out.println("Testing SAVEPOINTs"); + + try { + this.conn.setAutoCommit(true); + + createTable("testSavepoints", "(field1 int)", "InnoDB"); + + // Try with named save points + this.conn.setAutoCommit(false); + this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); + + Savepoint afterInsert = this.conn.setSavepoint("afterInsert"); + this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); + + Savepoint afterUpdate = this.conn.setSavepoint("afterUpdate"); + this.stmt.executeUpdate("DELETE FROM testSavepoints"); + + assertTrue("Row count should be 0", getRowCount("testSavepoints") == 0); + this.conn.rollback(afterUpdate); + assertTrue("Row count should be 1", getRowCount("testSavepoints") == 1); + assertTrue("Value should be 2", "2".equals(getSingleValue("testSavepoints", "field1", null).toString())); + this.conn.rollback(afterInsert); + assertTrue("Value should be 1", "1".equals(getSingleValue("testSavepoints", "field1", null).toString())); + this.conn.rollback(); + assertTrue("Row count should be 0", getRowCount("testSavepoints") == 0); + + // Try with 'anonymous' save points + this.conn.rollback(); + + this.stmt.executeUpdate("INSERT INTO testSavepoints VALUES (1)"); + afterInsert = this.conn.setSavepoint(); + this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2"); + afterUpdate = this.conn.setSavepoint(); + this.stmt.executeUpdate("DELETE FROM testSavepoints"); + + assertTrue("Row count should be 0", getRowCount("testSavepoints") == 0); + this.conn.rollback(afterUpdate); + assertTrue("Row count should be 1", getRowCount("testSavepoints") == 1); + assertTrue("Value should be 2", "2".equals(getSingleValue("testSavepoints", "field1", null).toString())); + this.conn.rollback(afterInsert); + assertTrue("Value should be 1", "1".equals(getSingleValue("testSavepoints", "field1", null).toString())); + this.conn.rollback(); + + this.conn.releaseSavepoint(this.conn.setSavepoint()); + } finally { + this.conn.setAutoCommit(true); + } + } else { + System.out.println("MySQL version does not support SAVEPOINTs"); + } + } + + /** + * Tests the ability to set the connection collation via properties. + * + * @throws Exception + * if an error occurs or the test fails + */ + public void testNonStandardConnectionCollation() throws Exception { + if (versionMeetsMinimum(4, 1)) { + String collationToSet = "utf8_bin"; + String characterSet = "utf-8"; + + Properties props = new Properties(); + props.setProperty("connectionCollation", collationToSet); + props.setProperty("characterEncoding", characterSet); + + Connection collConn = null; + Statement collStmt = null; + ResultSet collRs = null; + + try { + collConn = getConnectionWithProps(props); + + collStmt = collConn.createStatement(); + + collRs = collStmt.executeQuery("SHOW VARIABLES LIKE 'collation_connection'"); + + assertTrue(collRs.next()); + assertTrue(collationToSet.equalsIgnoreCase(collRs.getString(2))); + } finally { + if (collConn != null) { + collConn.close(); + } + } + } + } + + public void testDumpQueriesOnException() throws Exception { + Properties props = new Properties(); + props.setProperty("dumpQueriesOnException", "true"); + String bogusSQL = "SELECT 1 TO BAZ"; + Connection dumpConn = getConnectionWithProps(props); + + try { + dumpConn.createStatement().executeQuery(bogusSQL); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1); + } + + try { + ((com.mysql.jdbc.Connection) dumpConn).clientPrepareStatement(bogusSQL).executeQuery(); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1); + } + + try { + createTable("testDumpQueriesOnException", "(field1 int UNIQUE)"); + this.stmt.executeUpdate("INSERT INTO testDumpQueriesOnException VALUES (1)"); + + PreparedStatement pStmt = dumpConn.prepareStatement("INSERT INTO testDumpQueriesOnException VALUES (?)"); + pStmt.setInt(1, 1); + pStmt.executeUpdate(); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf("INSERT INTO testDumpQueriesOnException") != -1); + } + + try { + dumpConn.prepareStatement(bogusSQL); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1); + } + } + + /** + * Tests functionality of the ConnectionPropertiesTransform interface. + * + * @throws Exception + * if the test fails. + */ + public void testConnectionPropertiesTransform() throws Exception { + String transformClassName = SimpleTransformer.class.getName(); + + Properties props = new Properties(); + + props.setProperty(NonRegisteringDriver.PROPERTIES_TRANSFORM_KEY, transformClassName); + + NonRegisteringDriver driver = new NonRegisteringDriver(); + + Properties transformedProps = driver.parseURL(BaseTestCase.dbUrl, props); + + assertTrue("albequerque".equals(transformedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY))); + } + + /** + * Tests functionality of using URLs in 'LOAD DATA LOCAL INFILE' statements. + * + * @throws Exception + * if the test fails. + */ + public void testLocalInfileWithUrl() throws Exception { + File infile = File.createTempFile("foo", "txt"); + infile.deleteOnExit(); + String url = infile.toURL().toExternalForm(); + FileWriter output = new FileWriter(infile); + output.write("Test"); + output.flush(); + output.close(); + + createTable("testLocalInfileWithUrl", "(field1 LONGTEXT)"); + + Properties props = new Properties(); + props.setProperty("allowUrlInLocalInfile", "true"); + + Connection loadConn = getConnectionWithProps(props); + Statement loadStmt = loadConn.createStatement(); + + String charset = " CHARACTER SET " + + CharsetMapping.getMysqlCharsetForJavaEncoding(((MySQLConnection) loadConn).getEncoding(), (com.mysql.jdbc.Connection) loadConn); + + try { + loadStmt.executeQuery("LOAD DATA LOCAL INFILE '" + url + "' INTO TABLE testLocalInfileWithUrl" + charset); + } catch (SQLException sqlEx) { + sqlEx.printStackTrace(); + + throw sqlEx; + } + + this.rs = this.stmt.executeQuery("SELECT * FROM testLocalInfileWithUrl"); + assertTrue(this.rs.next()); + assertTrue("Test".equals(this.rs.getString(1))); + int count = this.stmt.executeUpdate("DELETE FROM testLocalInfileWithUrl"); + assertTrue(count == 1); + + StringBuilder escapedPath = new StringBuilder(); + String path = infile.getCanonicalPath(); + + for (int i = 0; i < path.length(); i++) { + char c = path.charAt(i); + + if (c == '\\') { + escapedPath.append('\\'); + } + + escapedPath.append(c); + } + + loadStmt.execute("LOAD DATA LOCAL INFILE '" + escapedPath.toString() + "' INTO TABLE testLocalInfileWithUrl" + charset); + this.rs = this.stmt.executeQuery("SELECT * FROM testLocalInfileWithUrl"); + assertTrue(this.rs.next()); + assertTrue("Test".equals(this.rs.getString(1))); + + try { + loadStmt.execute("LOAD DATA LOCAL INFILE 'foo:///' INTO TABLE testLocalInfileWithUrl" + charset); + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getMessage() != null); + assertTrue(sqlEx.getMessage().indexOf("FileNotFoundException") != -1); + } + } + + public void testLocalInfileDisabled() throws Exception { + createTable("testLocalInfileDisabled", "(field1 varchar(255))"); + + File infile = File.createTempFile("foo", "txt"); + infile.deleteOnExit(); + //String url = infile.toURL().toExternalForm(); + FileWriter output = new FileWriter(infile); + output.write("Test"); + output.flush(); + output.close(); + + Connection loadConn = getConnectionWithProps(new Properties()); + + try { + // have to do this after connect, otherwise it's the server that's enforcing it + ((com.mysql.jdbc.Connection) loadConn).setAllowLoadLocalInfile(false); + try { + loadConn.createStatement().execute("LOAD DATA LOCAL INFILE '" + infile.getCanonicalPath() + "' INTO TABLE testLocalInfileDisabled"); + fail("Should've thrown an exception."); + } catch (SQLException sqlEx) { + assertEquals(SQLError.SQL_STATE_GENERAL_ERROR, sqlEx.getSQLState()); + } + + assertFalse(loadConn.createStatement().executeQuery("SELECT * FROM testLocalInfileDisabled").next()); + } finally { + loadConn.close(); + } + } + + public void testServerConfigurationCache() throws Exception { + Properties props = new Properties(); + + props.setProperty("cacheServerConfiguration", "true"); + props.setProperty("profileSQL", "true"); + props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger"); + + Connection conn1 = null; + Connection conn2 = null; + try { + conn1 = getConnectionWithProps(props); + + try { + // eliminate side-effects when not run in isolation + StandardLogger.startLoggingToBuffer(); + + conn2 = getConnectionWithProps(props); + + assertTrue("Configuration wasn't cached", StandardLogger.getBuffer().toString().indexOf("SHOW VARIABLES") == -1); + + if (versionMeetsMinimum(4, 1)) { + assertTrue("Configuration wasn't cached", StandardLogger.getBuffer().toString().indexOf("SHOW COLLATION") == -1); + + } + } finally { + StandardLogger.dropBuffer(); + } + } finally { + if (conn1 != null) { + conn1.close(); + } + if (conn2 != null) { + conn2.close(); + } + } + } + + /** + * Tests whether or not the configuration 'useLocalSessionState' actually + * prevents non-needed 'set autocommit=', 'set session transaction isolation + * ...' and 'show variables like tx_isolation' queries. + * + * @throws Exception + * if the test fails. + */ + public void testUseLocalSessionState() throws Exception { + Properties props = new Properties(); + + props.setProperty("useLocalSessionState", "true"); + props.setProperty("profileSQL", "true"); + props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger"); + + Connection conn1 = getConnectionWithProps(props); + conn1.setAutoCommit(true); + conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + + StandardLogger.startLoggingToBuffer(); + + conn1.setAutoCommit(true); + conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); + conn1.getTransactionIsolation(); + + String logAsString = StandardLogger.getBuffer().toString(); + + assertTrue(logAsString.indexOf("SET SESSION") == -1 && logAsString.indexOf("SHOW VARIABLES LIKE 'tx_isolation'") == -1 + && logAsString.indexOf("SET autocommit=") == -1); + } + + /** + * Tests whether re-connect with non-read-only connection can happen. + * + * @throws Exception + * if the test fails. + */ + public void testFailoverConnection() throws Exception { + + if (!isServerRunningOnWindows()) { // windows sockets don't work for this test + Properties props = new Properties(); + props.setProperty("autoReconnect", "true"); + props.setProperty("failOverReadOnly", "false"); + + Properties urlProps = new NonRegisteringDriver().parseURL(dbUrl, null); + + String host = urlProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = urlProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY + ".1", host); + props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY + ".1", port); + props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY + ".2", host); + props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY + ".2", port); + props.setProperty(NonRegisteringDriver.NUM_HOSTS_PROPERTY_KEY, "2"); + + Connection failoverConnection = null; + + try { + failoverConnection = getConnectionWithProps(props); + + String originalConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT connection_id()").toString(); + System.out.println("Original Connection Id = " + originalConnectionId); + + assertTrue("Connection should not be in READ_ONLY state", !failoverConnection.isReadOnly()); + + // Kill the connection + this.stmt.executeUpdate("KILL " + originalConnectionId); + + // This takes a bit to occur + + Thread.sleep(3000); + + try { + failoverConnection.createStatement().execute("SELECT 1"); + fail("We expect an exception here, because the connection should be gone until the reconnect code picks it up again"); + } catch (SQLException sqlEx) { + // do-nothing + } + + // Tickle re-connect + + failoverConnection.setAutoCommit(true); + + String newConnectionId = getSingleIndexedValueWithQuery(failoverConnection, 1, "SELECT connection_id()").toString(); + System.out.println("new Connection Id = " + newConnectionId); + + assertTrue("We should have a new connection to the server in this case", !newConnectionId.equals(originalConnectionId)); + assertTrue("Connection should not be read-only", !failoverConnection.isReadOnly()); + } finally { + if (failoverConnection != null) { + failoverConnection.close(); + } + } + } + } + + public void testCannedConfigs() throws Exception { + String url = "jdbc:mysql:///?useConfigs=clusterBase"; + + Properties cannedProps = new NonRegisteringDriver().parseURL(url, null); + + assertTrue("true".equals(cannedProps.getProperty("autoReconnect"))); + assertTrue("false".equals(cannedProps.getProperty("failOverReadOnly"))); + assertTrue("true".equals(cannedProps.getProperty("roundRobinLoadBalance"))); + + // this will fail, but we test that too + url = "jdbc:mysql:///?useConfigs=clusterBase,clusterBase2"; + + try { + cannedProps = new NonRegisteringDriver().parseURL(url, null); + fail("should've bailed on that one!"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE.equals(sqlEx.getSQLState())); + } + } + + public void testUseOldUTF8Behavior() throws Exception { + + Properties props = new Properties(); + props.setProperty("useOldUTF8Behavior", "true"); + props.setProperty("useUnicode", "true"); + props.setProperty("characterEncoding", "UTF-8"); + props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger"); + props.setProperty("profileSQL", "true"); + StandardLogger.startLoggingToBuffer(); + + try { + getConnectionWithProps(props); + + assertTrue(StringUtils.indexOfIgnoreCase(StandardLogger.getBuffer().toString(), "SET NAMES utf8") == -1); + } finally { + StandardLogger.dropBuffer(); + } + } + + /** + * Checks implementation of 'dontTrackOpenResources' property. + * + * @throws Exception + * if the test fails. + */ + public void testDontTrackOpenResources() throws Exception { + Properties props = new Properties(); + + props.setProperty("dontTrackOpenResources", "true"); + Connection noTrackConn = null; + Statement noTrackStatement = null; + PreparedStatement noTrackPstmt = null; + ResultSet rs2 = null; + + try { + noTrackConn = getConnectionWithProps(props); + noTrackStatement = noTrackConn.createStatement(); + noTrackPstmt = noTrackConn.prepareStatement("SELECT 1"); + rs2 = noTrackPstmt.executeQuery(); + rs2.next(); + + this.rs = noTrackStatement.executeQuery("SELECT 1"); + this.rs.next(); + + noTrackConn.close(); + + // Under 'strict' JDBC requirements, these calls should fail + // (and _do_ if dontTrackOpenResources == false) + + this.rs.getString(1); + rs2.getString(1); + } finally { + if (rs2 != null) { + rs2.close(); + } + + if (noTrackStatement != null) { + noTrackStatement.close(); + } + + if (noTrackConn != null && !noTrackConn.isClosed()) { + noTrackConn.close(); + } + } + } + + public void testPing() throws SQLException { + Connection conn2 = getConnectionWithProps((String) null); + + ((com.mysql.jdbc.Connection) conn2).ping(); + conn2.close(); + + try { + ((com.mysql.jdbc.Connection) conn2).ping(); + fail("Should have failed with an exception"); + } catch (SQLException sqlEx) { + // ignore for now + } + + // + // This feature caused BUG#8975, so check for that too! + + Properties props = new Properties(); + props.setProperty("autoReconnect", "true"); + + getConnectionWithProps(props); + } + + public void testSessionVariables() throws Exception { + String getInitialWaitTimeout = getMysqlVariable("wait_timeout"); + + int newWaitTimeout = Integer.parseInt(getInitialWaitTimeout) + 10000; + + Properties props = new Properties(); + props.setProperty("sessionVariables", "wait_timeout=" + newWaitTimeout); + props.setProperty("profileSQL", "true"); + + Connection varConn = getConnectionWithProps(props); + + assertTrue(!getInitialWaitTimeout.equals(getMysqlVariable(varConn, "wait_timeout"))); + } + + /** + * Tests setting profileSql on/off in the span of one connection. + * + * @throws Exception + * if an error occurs. + */ + public void testSetProfileSql() throws Exception { + ((com.mysql.jdbc.Connection) this.conn).setProfileSql(false); + this.stmt.execute("SELECT 1"); + ((com.mysql.jdbc.Connection) this.conn).setProfileSql(true); + this.stmt.execute("SELECT 1"); + } + + public void testCreateDatabaseIfNotExist() throws Exception { + if (isAdminConnectionConfigured()) { + Properties props = new Properties(); + props.setProperty("createDatabaseIfNotExist", "true"); + props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, "testcreatedatabaseifnotexists"); + + Connection newConn = getAdminConnectionWithProps(props); + newConn.createStatement().executeUpdate("DROP DATABASE testcreatedatabaseifnotexists"); + } + } + + /** + * Tests if gatherPerfMetrics works. + * + * @throws Exception + * if the test fails + */ + public void testGatherPerfMetrics() throws Exception { + if (versionMeetsMinimum(4, 1)) { + try { + Properties props = new Properties(); + props.put("autoReconnect", "true"); + props.put("relaxAutoCommit", "true"); + props.put("logSlowQueries", "true"); + props.put("slowQueryThresholdMillis", "2000"); + // these properties were reported as the cause of NullPointerException + props.put("gatherPerfMetrics", "true"); + props.put("reportMetricsIntervalMillis", "3000"); + + Connection conn1 = getConnectionWithProps(props); + Statement stmt1 = conn1.createStatement(); + ResultSet rs1 = stmt1.executeQuery("SELECT 1"); + rs1.next(); + conn1.close(); + } catch (NullPointerException e) { + e.printStackTrace(); + fail(); + } + } + } + + /** + * Tests if useCompress works. + * + * @throws Exception + * if the test fails + */ + public void testUseCompress() throws Exception { + + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'"); + this.rs.next(); + if (this.rs.getInt(2) < 4 + 1024 * 1024 * 16 - 1) { + fail("You need to increase max_allowed_packet to at least " + (4 + 1024 * 1024 * 16 - 1) + " before running this test!"); + } + + if (versionMeetsMinimum(5, 6, 20) && !versionMeetsMinimum(5, 7)) { + /* + * The 5.6.20 patch for Bug #16963396, Bug #19030353, Bug #69477 limits the size of redo log BLOB writes + * to 10% of the redo log file size. The 5.7.5 patch addresses the bug without imposing a limitation. + * As a result of the redo log BLOB write limit introduced for MySQL 5.6, innodb_log_file_size should be set to a value + * greater than 10 times the largest BLOB data size found in the rows of your tables plus the length of other variable length + * fields (VARCHAR, VARBINARY, and TEXT type fields). + */ + this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'innodb_log_file_size'"); + this.rs.next(); + if (this.rs.getInt(2) < 1024 * 1024 * 32 * 10) { + fail("You need to increase innodb_log_file_size to at least " + (1024 * 1024 * 32 * 10) + " before running this test!"); + } + } + + testCompressionWith("false", 1024 * 1024 * 16 - 2); // no split + testCompressionWith("false", 1024 * 1024 * 16 - 1); // split with additional empty packet + testCompressionWith("false", 1024 * 1024 * 32); // big payload + + testCompressionWith("true", 1024 * 1024 * 16 - 2 - 3); // no split, one compressed packet + testCompressionWith("true", 1024 * 1024 * 16 - 2 - 2); // no split, two compressed packets + testCompressionWith("true", 1024 * 1024 * 16 - 1); // split with additional empty packet, two compressed packets + testCompressionWith("true", 1024 * 1024 * 32); // big payload + + } + + /** + * @param useCompression + * @param maxUncompressedPacketSize + * mysql header + payload + * @throws Exception + */ + private void testCompressionWith(String useCompression, int maxPayloadSize) throws Exception { + + String sqlToSend = "INSERT INTO BLOBTEST(blobdata) VALUES (?)"; + int requiredSize = maxPayloadSize - sqlToSend.length() - "_binary''".length(); + + File testBlobFile = File.createTempFile("cmj-testblob", ".dat"); + testBlobFile.deleteOnExit(); + + // TODO: following cleanup doesn't work correctly during concurrent execution of testsuite + // cleanupTempFiles(testBlobFile, "cmj-testblob"); + + BufferedOutputStream bOut = new BufferedOutputStream(new FileOutputStream(testBlobFile)); + + int dataRange = Byte.MAX_VALUE - Byte.MIN_VALUE; + + for (int i = 0; i < requiredSize; i++) { + bOut.write((byte) ((Math.random() * dataRange) + Byte.MIN_VALUE)); + } + + bOut.flush(); + bOut.close(); + + Properties props = new Properties(); + props.put("useCompression", useCompression); + Connection conn1 = getConnectionWithProps(props); + Statement stmt1 = conn1.createStatement(); + + createTable("BLOBTEST", "(pos int PRIMARY KEY auto_increment, blobdata LONGBLOB)"); + BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(testBlobFile)); + + this.pstmt = conn1.prepareStatement(sqlToSend); + + this.pstmt.setBinaryStream(1, bIn, (int) testBlobFile.length()); + this.pstmt.execute(); + this.pstmt.clearParameters(); + + this.rs = stmt1.executeQuery("SELECT blobdata from BLOBTEST LIMIT 1"); + this.rs.next(); + InputStream is = this.rs.getBinaryStream(1); + + bIn.close(); + bIn = new BufferedInputStream(new FileInputStream(testBlobFile)); + int blobbyte = 0; + int count = 0; + while ((blobbyte = is.read()) > -1) { + int filebyte = bIn.read(); + if (filebyte < 0 || filebyte != blobbyte) { + fail("Blob is not identical to initial data."); + } + count++; + } + assertEquals(requiredSize, count); + + is.close(); + if (bIn != null) { + bIn.close(); + } + } + + /** + * Tests feature of "localSocketAddress", by enumerating local IF's and + * trying each one in turn. This test might take a long time to run, since + * we can't set timeouts if we're using localSocketAddress. We try and keep + * the time down on the testcase by spawning the checking of each interface + * off into separate threads. + * + * @throws Exception + * if the test can't use at least one of the local machine's + * interfaces to make an outgoing connection to the server. + */ + public void testLocalSocketAddress() throws Exception { + Enumeration allInterfaces = NetworkInterface.getNetworkInterfaces(); + + SpawnedWorkerCounter counter = new SpawnedWorkerCounter(); + + List allChecks = new ArrayList(); + + while (allInterfaces.hasMoreElements()) { + NetworkInterface intf = allInterfaces.nextElement(); + + Enumeration allAddresses = intf.getInetAddresses(); + + allChecks.add(new LocalSocketAddressCheckThread(allAddresses, counter)); + } + + counter.setWorkerCount(allChecks.size()); + + for (LocalSocketAddressCheckThread t : allChecks) { + t.start(); + } + + // Wait for tests to complete.... + synchronized (counter) { + + while (counter.workerCount > 0 /* safety valve */) { + + counter.wait(); + + if (counter.workerCount == 0) { + System.out.println("Done!"); + break; + } + } + } + + boolean didOneWork = false; + boolean didOneFail = false; + + for (LocalSocketAddressCheckThread t : allChecks) { + if (t.atLeastOneWorked) { + didOneWork = true; + + break; + } + if (!didOneFail) { + didOneFail = true; + } + } + + assertTrue("At least one connection was made with the localSocketAddress set", didOneWork); + + NonRegisteringDriver d = new NonRegisteringDriver(); + + String hostname = d.host(d.parseURL(dbUrl, null)); + + if (!hostname.startsWith(":") && !hostname.startsWith("localhost")) { + + int indexOfColon = hostname.indexOf(":"); + + if (indexOfColon != -1) { + hostname = hostname.substring(0, indexOfColon); + } + + boolean isLocalIf = false; + + isLocalIf = (null != NetworkInterface.getByName(hostname)); + + if (!isLocalIf) { + try { + isLocalIf = (null != NetworkInterface.getByInetAddress(InetAddress.getByName(hostname))); + } catch (Throwable t) { + isLocalIf = false; + } + } + + if (!isLocalIf) { + assertTrue("At least one connection didn't fail with localSocketAddress set", didOneFail); + } + } + } + + class SpawnedWorkerCounter { + protected int workerCount = 0; + + synchronized void setWorkerCount(int i) { + this.workerCount = i; + } + + synchronized void decrementWorkerCount() { + this.workerCount--; + notify(); + } + } + + class LocalSocketAddressCheckThread extends Thread { + boolean atLeastOneWorked = false; + Enumeration allAddresses = null; + SpawnedWorkerCounter counter = null; + + LocalSocketAddressCheckThread(Enumeration e, SpawnedWorkerCounter c) { + this.allAddresses = e; + this.counter = c; + } + + @Override + public void run() { + + while (this.allAddresses.hasMoreElements()) { + InetAddress addr = this.allAddresses.nextElement(); + + try { + Properties props = new Properties(); + props.setProperty("localSocketAddress", addr.getHostAddress()); + props.setProperty("connectTimeout", "2000"); + getConnectionWithProps(props).close(); + + this.atLeastOneWorked = true; + + break; + } catch (SQLException sqlEx) { + // ignore, we're only seeing if one of these tests succeeds + } + } + + this.counter.decrementWorkerCount(); + } + } + + public void testUsageAdvisorTooLargeResultSet() throws Exception { + Connection uaConn = null; + + PrintStream stderr = System.err; + + StandardLogger.startLoggingToBuffer(); + + try { + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + props.setProperty("resultSetSizeThreshold", "4"); + props.setProperty("logger", "StandardLogger"); + + uaConn = getConnectionWithProps(props); + this.rs = uaConn.createStatement().executeQuery("SHOW VARIABLES"); + this.rs.close(); + + assertTrue("Result set threshold message not present", + StandardLogger.getBuffer().toString().indexOf("larger than \"resultSetSizeThreshold\" of 4 rows") != -1); + } finally { + StandardLogger.dropBuffer(); + System.setErr(stderr); + + if (uaConn != null) { + uaConn.close(); + } + } + } + + public void testUseLocalSessionStateRollback() throws Exception { + if (!versionMeetsMinimum(5, 5, 0)) { + return; + } + + Properties props = new Properties(); + props.setProperty("useLocalSessionState", "true"); + props.setProperty("useLocalTransactionState", "true"); + props.setProperty("profileSQL", "true"); + + StandardLogger.startLoggingToBuffer(); + + createTable("testUseLocalSessionState", "(field1 varchar(32))", "InnoDB"); + + Connection localStateConn = null; + Statement localStateStmt = null; + String searchIn = ""; + + try { + localStateConn = getConnectionWithProps(props); + localStateStmt = localStateConn.createStatement(); + + localStateConn.setAutoCommit(false); + localStateStmt.executeUpdate("INSERT INTO testUseLocalSessionState VALUES ('abc')"); + localStateConn.rollback(); + localStateConn.rollback(); + localStateStmt.executeUpdate("INSERT INTO testUseLocalSessionState VALUES ('abc')"); + localStateConn.commit(); + localStateConn.commit(); + localStateStmt.close(); + } finally { + searchIn = StandardLogger.getBuffer().toString(); + StandardLogger.dropBuffer(); + + if (localStateStmt != null) { + localStateStmt.close(); + } + + if (localStateConn != null) { + localStateConn.close(); + } + } + + int rollbackCount = 0; + int rollbackPos = 0; + + // space is important here, we don't want to count occurrences in stack traces + while (rollbackPos != -1) { + rollbackPos = searchIn.indexOf(" rollback", rollbackPos); + + if (rollbackPos != -1) { + rollbackPos += "rollback".length(); + rollbackCount++; + } + } + + assertEquals(1, rollbackCount); + + int commitCount = 0; + int commitPos = 0; + + // space is important here, we don't want to count "autocommit" nor occurrences in stack traces + while (commitPos != -1) { + commitPos = searchIn.indexOf(" commit", commitPos); + + if (commitPos != -1) { + commitPos += " commit".length(); + commitCount++; + } + } + + assertEquals(1, commitCount); + } + + /** + * Checks if setting useCursorFetch to "true" automatically enables + * server-side prepared statements. + */ + + public void testCouplingOfCursorFetch() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection fetchConn = null; + + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "false"); // force the issue + props.setProperty("useCursorFetch", "true"); + fetchConn = getConnectionWithProps(props); + + String classname = "com.mysql.jdbc.ServerPreparedStatement"; + + if (Util.isJdbc42()) { + classname = "com.mysql.jdbc.JDBC42ServerPreparedStatement"; + } else if (Util.isJdbc4()) { + classname = "com.mysql.jdbc.JDBC4ServerPreparedStatement"; + } + + assertEquals(classname, fetchConn.prepareStatement("SELECT 1").getClass().getName()); + } finally { + if (fetchConn != null) { + fetchConn.close(); + } + } + } + + public void testInterfaceImplementation() throws Exception { + testInterfaceImplementation(getConnectionWithProps((Properties) null)); + MysqlConnectionPoolDataSource cpds = new MysqlConnectionPoolDataSource(); + cpds.setUrl(dbUrl); + testInterfaceImplementation(cpds.getPooledConnection().getConnection()); + } + + private void testInterfaceImplementation(Connection connToCheck) throws Exception { + Method[] dbmdMethods = java.sql.DatabaseMetaData.class.getMethods(); + + // can't do this statically, as we return different + // implementations depending on JDBC version + DatabaseMetaData dbmd = connToCheck.getMetaData(); + + checkInterfaceImplemented(dbmdMethods, dbmd.getClass(), dbmd); + + Statement stmtToCheck = connToCheck.createStatement(); + + checkInterfaceImplemented(java.sql.Statement.class.getMethods(), stmtToCheck.getClass(), stmtToCheck); + + PreparedStatement pStmtToCheck = connToCheck.prepareStatement("SELECT 1"); + ParameterMetaData paramMd = pStmtToCheck.getParameterMetaData(); + + checkInterfaceImplemented(java.sql.PreparedStatement.class.getMethods(), pStmtToCheck.getClass(), pStmtToCheck); + checkInterfaceImplemented(java.sql.ParameterMetaData.class.getMethods(), paramMd.getClass(), paramMd); + + pStmtToCheck = ((com.mysql.jdbc.Connection) connToCheck).serverPrepareStatement("SELECT 1"); + + checkInterfaceImplemented(java.sql.PreparedStatement.class.getMethods(), pStmtToCheck.getClass(), pStmtToCheck); + ResultSet toCheckRs = connToCheck.createStatement().executeQuery("SELECT 1"); + checkInterfaceImplemented(java.sql.ResultSet.class.getMethods(), toCheckRs.getClass(), toCheckRs); + toCheckRs = connToCheck.createStatement().executeQuery("SELECT 1"); + checkInterfaceImplemented(java.sql.ResultSetMetaData.class.getMethods(), toCheckRs.getMetaData().getClass(), toCheckRs.getMetaData()); + + if (versionMeetsMinimum(5, 0, 0)) { + createProcedure("interfaceImpl", "(IN p1 INT)\nBEGIN\nSELECT 1;\nEND"); + + CallableStatement cstmt = connToCheck.prepareCall("{CALL interfaceImpl(?)}"); + + checkInterfaceImplemented(java.sql.CallableStatement.class.getMethods(), cstmt.getClass(), cstmt); + } + checkInterfaceImplemented(java.sql.Connection.class.getMethods(), connToCheck.getClass(), connToCheck); + } + + private void checkInterfaceImplemented(Method[] interfaceMethods, Class implementingClass, Object invokeOn) throws NoSuchMethodException { + for (int i = 0; i < interfaceMethods.length; i++) { + Method toFind = interfaceMethods[i]; + Method toMatch = implementingClass.getMethod(toFind.getName(), toFind.getParameterTypes()); + assertNotNull(toFind.toString(), toMatch); + Class paramTypes[] = toFind.getParameterTypes(); + + Object[] args = new Object[paramTypes.length]; + fillPrimitiveDefaults(paramTypes, args, paramTypes.length); + + try { + toMatch.invoke(invokeOn, args); + } catch (IllegalArgumentException e) { + + } catch (IllegalAccessException e) { + + } catch (InvocationTargetException e) { + + } catch (java.lang.AbstractMethodError e) { + throw e; + } + } + } + + public void testNonVerifyServerCert() throws Exception { + Properties props = new Properties(); + props.setProperty("useSSL", "true"); + props.setProperty("verifyServerCertificate", "false"); + props.setProperty("requireSSL", "true"); + getConnectionWithProps(props); + } + + public void testSelfDestruct() throws Exception { + Connection selfDestructingConn = getConnectionWithProps("selfDestructOnPingMaxOperations=2"); + + boolean failed = false; + + for (int i = 0; i < 20; i++) { + selfDestructingConn.createStatement().execute("SELECT 1"); + + try { + selfDestructingConn.createStatement().executeQuery("/* ping */ SELECT 1"); + } catch (SQLException sqlEx) { + String sqlState = sqlEx.getSQLState(); + + assertEquals("08S01", sqlState); + + failed = true; + + break; + } + } + + if (!failed) { + fail("Connection should've self-destructed"); + } + + failed = false; + + selfDestructingConn = getConnectionWithProps("selfDestructOnPingSecondsLifetime=1"); + + for (int i = 0; i < 20; i++) { + selfDestructingConn.createStatement().execute("SELECT SLEEP(1)"); + + try { + selfDestructingConn.createStatement().executeQuery("/* ping */ SELECT 1"); + } catch (SQLException sqlEx) { + String sqlState = sqlEx.getSQLState(); + + assertEquals("08S01", sqlState); + + failed = true; + + break; + } + } + + if (!failed) { + fail("Connection should've self-destructed"); + } + } + + public void testLifecyleInterceptor() throws Exception { + createTable("testLifecycleInterceptor", "(field1 int)", "InnoDB"); + Connection liConn = null; + + try { + liConn = getConnectionWithProps("connectionLifecycleInterceptors=testsuite.simple.TestLifecycleInterceptor"); + liConn.setAutoCommit(false); + + liConn.createStatement().executeUpdate("INSERT INTO testLifecycleInterceptor VALUES (1)"); + liConn.commit(); + assertEquals(TestLifecycleInterceptor.transactionsBegun, 1); + assertEquals(TestLifecycleInterceptor.transactionsCompleted, 1); + liConn.createStatement().execute("SELECT * FROM testLifecycleInterceptor"); + assertEquals(TestLifecycleInterceptor.transactionsBegun, 2); + // implicit commit + liConn.createStatement().executeUpdate("CREATE TABLE testLifecycleFoo (field1 int)"); + assertEquals(TestLifecycleInterceptor.transactionsCompleted, 2); + } finally { + if (liConn != null) { + liConn.createStatement().executeUpdate("DROP TABLE IF EXISTS testLifecycleFoo"); + liConn.close(); + } + } + + } + + public void testNewHostParsing() throws Exception { + Properties parsedProps = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = parsedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + String port = parsedProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + String user = parsedProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + String password = parsedProps.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + String database = parsedProps.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + String newUrl = String.format("jdbc:mysql://address=(protocol=tcp)(host=%s)(port=%s)(user=%s)(password=%s)/%s", host, port, user != null ? user : "", + password != null ? password : "", database); + + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.remove(NonRegisteringDriver.USER_PROPERTY_KEY); + props.remove(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + props.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY); + + try { + getConnectionWithProps(newUrl, props); + } catch (SQLException sqlEx) { + throw new RuntimeException("Failed to connect with URL " + newUrl, sqlEx); + } + } + + public void testCompression() throws Exception { + Connection compressedConn = getConnectionWithProps("useCompression=true,maxAllowedPacket=33554432"); + Statement compressedStmt = compressedConn.createStatement(); + compressedStmt.setFetchSize(Integer.MIN_VALUE); + this.rs = compressedStmt.executeQuery("select repeat('a', 256 * 256 * 256 - 5)"); + this.rs.next(); + String str = this.rs.getString(1); + + assertEquals((256 * 256 * 256 - 5), str.length()); + + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) != 'a') { + fail(); + } + } + } + + public void testIsLocal() throws Exception { + Properties parsedProps = new NonRegisteringDriver().parseURL(dbUrl, null); + String host = parsedProps.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "localhost"); + + if (host.equals("localhost") || host.equals("127.0.0.1")) { + // we can actually test this + assertTrue(((com.mysql.jdbc.ConnectionImpl) this.conn).isServerLocal()); + } + + } + + public void testReadOnly56() throws Exception { + if (versionMeetsMinimum(5, 6, 5)) { + try { + Connection notLocalState = getConnectionWithProps("profileSql=true"); + + for (int i = 0; i < 2; i++) { + StandardLogger.startLoggingToBuffer(); + notLocalState.setReadOnly(true); + assertTrue(StandardLogger.getBuffer().toString().indexOf("set session transaction read only") != -1); + notLocalState.createStatement().execute("set session transaction read write"); + assertFalse(notLocalState.isReadOnly()); + } + + for (int i = 0; i < 2; i++) { + StandardLogger.startLoggingToBuffer(); + notLocalState.setReadOnly(false); + assertTrue(StandardLogger.getBuffer().toString().indexOf("set session transaction read write") != -1); + notLocalState.createStatement().execute("set session transaction read only"); + assertTrue(notLocalState.isReadOnly()); + } + + Connection localState = getConnectionWithProps("profileSql=true,useLocalSessionState=true"); + + for (int i = 0; i < 2; i++) { + StandardLogger.startLoggingToBuffer(); + localState.setReadOnly(true); + if (i == 0) { + assertTrue(StandardLogger.getBuffer().toString().indexOf("set session transaction read only") != -1); + } else { + assertTrue(StandardLogger.getBuffer().toString().indexOf("set session transaction read only") == -1); + } + StandardLogger.startLoggingToBuffer(); + localState.isReadOnly(); + assertTrue(StandardLogger.getBuffer().toString().indexOf("select @@session.tx_read_only") == -1); + } + + Connection noOptimization = getConnectionWithProps("profileSql=true,readOnlyPropagatesToServer=false"); + + for (int i = 0; i < 2; i++) { + StandardLogger.startLoggingToBuffer(); + noOptimization.setReadOnly(true); + assertTrue(StandardLogger.getBuffer().toString().indexOf("set session transaction read only") == -1); + StandardLogger.startLoggingToBuffer(); + noOptimization.isReadOnly(); + assertTrue(StandardLogger.getBuffer().toString().indexOf("select @@session.tx_read_only") == -1); + } + } finally { + StandardLogger.dropBuffer(); + } + } + } + + /** + * IPv6 Connection test. + * + * @throws SQLException + */ + public void testIPv6() throws Exception { + + if (!versionMeetsMinimum(5, 6)) { + return; + // this test could work with MySQL 5.5 but requires specific server configuration, e.g. "--bind-address=::" + } + + Properties connProps = getPropertiesFromTestsuiteUrl(); + + String host = "::1"; // IPv6 loopback + int port = Integer.parseInt(connProps.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY)); + String username = connProps.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + + String ipv6Url = String.format("jdbc:mysql://address=(protocol=tcp)(host=%s)(port=%d)", host, port); + + Connection testConn = null; + Statement testStmt = null; + ResultSet testRS = null; + + connProps = getHostFreePropertiesFromTestsuiteUrl(); + + testConn = DriverManager.getConnection(ipv6Url, connProps); + testStmt = testConn.createStatement(); + testRS = testStmt.executeQuery("SELECT USER()"); + + assertTrue(testRS.next()); + assertTrue(testRS.getString(1).startsWith(username)); + + testRS.close(); + testStmt.close(); + testConn.close(); + } + + /** + * Test connection property cacheDefaultTimezone. + * + * @throws SQLException + */ + public void testCacheDefaultTimezone() throws Exception { + final TimeZone defaultTZ = TimeZone.getDefault(); + final TimeZone testTZ1 = TimeZone.getTimeZone("GMT-2"); + final TimeZone testTZ2 = TimeZone.getTimeZone("GMT+2"); + + createTable("testCacheDefTZ", "(test TINYINT, dt DATETIME)"); + + Properties connProps = new Properties(); + connProps.setProperty("useTimezone", "true"); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + for (boolean cacheDefTZ : new boolean[] { true, false }) { + try { + String testMsg = "Test case [cacheDefaultTimezone=" + cacheDefTZ + "],"; + connProps.setProperty("cacheDefaultTimezone", Boolean.toString(cacheDefTZ)); + + Connection testConn = getConnectionWithProps(connProps); + PreparedStatement testPstmt = testConn.prepareStatement("INSERT INTO testCacheDefTZ VALUES (?, ?)"); + + sdf.setTimeZone(testTZ1); + java.sql.Timestamp tsIn = new java.sql.Timestamp(sdf.parse("1998-05-21 12:00:00").getTime()); + + /* + * Insert same date/time instant twice using different default time zones. + * Same values must be stored when default time zone is cached. Different values otherwise. + */ + TimeZone.setDefault(testTZ1); + testPstmt.setBoolean(1, cacheDefTZ); + testPstmt.setTimestamp(2, tsIn); + assertEquals(testMsg, 1, testPstmt.executeUpdate()); + + TimeZone.setDefault(testTZ2); + testPstmt.setBoolean(1, cacheDefTZ); + testPstmt.setTimestamp(2, tsIn); + assertEquals(testMsg, 1, testPstmt.executeUpdate()); + + testPstmt.close(); + + TimeZone.setDefault(defaultTZ); + + /* + * Verify that equal values are retrieved when default time zone is cached and different values otherwise, when default time zone doesn't + * change while reading data. + */ + Statement testStmt = testConn.createStatement(); + ResultSet testRs = testStmt.executeQuery("SELECT * FROM testCacheDefTZ WHERE test = " + cacheDefTZ); + + assertTrue(testMsg, testRs.next()); + java.sql.Timestamp timestampOut = testRs.getTimestamp(2); + + assertTrue(testMsg, testRs.next()); + assertEquals(testMsg, cacheDefTZ, timestampOut.equals(testRs.getTimestamp(2))); + + assertFalse(testMsg, testRs.next()); + + /* + * Verify that retrieving values from the ResultSet is also affected by default time zone caching setting, allowing to "convert" them to the + * original date value when time zone caching is disabled. + * When time zone caching is enabled then the values stored in the database were the same and changing the default time zone while retrieving + * them doesn't affect the result. + */ + testRs = testStmt.executeQuery("SELECT * FROM testCacheDefTZ WHERE test = " + cacheDefTZ); + + TimeZone.setDefault(testTZ1); + assertTrue(testMsg, testRs.next()); + timestampOut = testRs.getTimestamp(2); + + TimeZone.setDefault(testTZ2); + assertTrue(testMsg, testRs.next()); + assertEquals(testMsg, timestampOut, testRs.getTimestamp(2)); + + assertFalse(testMsg, testRs.next()); + + testRs.close(); + testStmt.close(); + + testConn.close(); + } finally { + TimeZone.setDefault(defaultTZ); + } + } + } + + /** + * Test the new connection property 'enableEscapeProcessing', as well as the old connection property 'processEscapeCodesForPrepStmts' and interrelation + * between both. + * + * This test uses a StatementInterceptor to capture the query sent to the server and assert whether escape processing has been done in the client side or if + * the query is sent untouched and escape processing will be done at server side, according to provided connection properties and type of Statement objects + * in use. + */ + public void testEnableEscapeProcessing() throws Exception { + // make sure the connection string doesn't contain 'enableEscapeProcessing' + String testUrl = BaseTestCase.dbUrl; + int b = testUrl.indexOf("enableEscapeProcessing"); + if (b != -1) { + int e = testUrl.indexOf('&', b); + if (e == -1) { + e = testUrl.length(); + b--; + } else { + e++; + } + testUrl = testUrl.substring(0, b) + testUrl.substring(e, testUrl.length()); + } + String query = "SELECT /* testEnableEscapeProcessing: (%d) */ {fn sin(pi()/2)}, {ts '2015-08-16 11:22:33'}, {fn ucase('this is mysql')}"; + Timestamp testTimestamp = new Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2015-08-16 11:22:33").getTime()); + + for (int tst = 0; tst < 8; tst++) { + boolean enableEscapeProcessing = (tst & 0x1) != 0; + boolean processEscapeCodesForPrepStmts = (tst & 0x2) != 0; + boolean useServerPrepStmts = (tst & 0x4) != 0; + + Properties props = new Properties(); + props.setProperty("statementInterceptors", TestEnableEscapeProcessingStatementInterceptor.class.getName()); + props.setProperty("enableEscapeProcessing", Boolean.toString(enableEscapeProcessing)); + props.setProperty("processEscapeCodesForPrepStmts", Boolean.toString(processEscapeCodesForPrepStmts)); + props.setProperty("useServerPrepStmts", Boolean.toString(useServerPrepStmts)); + + Connection testConn = getConnectionWithProps(testUrl, props); + this.stmt = testConn.createStatement(); + this.rs = this.stmt.executeQuery(String.format(query, tst)); + + String testCase = String.format("Case: %d [ %s | %s | %s ]/Statement", tst, enableEscapeProcessing ? "enEscProc" : "-", + processEscapeCodesForPrepStmts ? "procEscProcPS" : "-", useServerPrepStmts ? "useSSPS" : "-"); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, 1d, this.rs.getDouble(1)); + assertEquals(testCase, testTimestamp, this.rs.getTimestamp(2)); + assertEquals(testCase, "THIS IS MYSQL", this.rs.getString(3)); + assertFalse(testCase, this.rs.next()); + + this.pstmt = testConn.prepareStatement(String.format(query, tst)); + this.rs = this.pstmt.executeQuery(); + + testCase = String.format("Case: %d [ %s | %s | %s ]/PreparedStatement", tst, enableEscapeProcessing ? "enEscProc" : "-", + processEscapeCodesForPrepStmts ? "procEscProcPS" : "-", useServerPrepStmts ? "useSSPS" : "-"); + assertTrue(testCase, this.rs.next()); + assertEquals(testCase, 1d, this.rs.getDouble(1)); + assertEquals(testCase, testTimestamp, this.rs.getTimestamp(2)); + assertEquals(testCase, "THIS IS MYSQL", this.rs.getString(3)); + assertFalse(testCase, this.rs.next()); + + testConn.close(); + } + } + + public static class TestEnableEscapeProcessingStatementInterceptor extends BaseStatementInterceptor { + @Override + public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection) + throws SQLException { + if (sql == null && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) { + sql = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).asSql(); + } + + int p; + if (sql != null && (p = sql.indexOf("testEnableEscapeProcessing:")) != -1) { + int tst = Integer.parseInt(sql.substring(sql.indexOf('(', p) + 1, sql.indexOf(')', p))); + boolean enableEscapeProcessing = (tst & 0x1) != 0; + boolean processEscapeCodesForPrepStmts = (tst & 0x2) != 0; + boolean useServerPrepStmts = (tst & 0x4) != 0; + boolean isPreparedStatement = interceptedStatement instanceof PreparedStatement; + + String testCase = String.format("Case: %d [ %s | %s | %s ]/%s", tst, enableEscapeProcessing ? "enEscProc" : "-", + processEscapeCodesForPrepStmts ? "procEscProcPS" : "-", useServerPrepStmts ? "useSSPS" : "-", + isPreparedStatement ? "PreparedStatement" : "Statement"); + + boolean escapeProcessingDone = sql.indexOf('{') == -1; + assertTrue(testCase, isPreparedStatement && processEscapeCodesForPrepStmts == escapeProcessingDone + || !isPreparedStatement && enableEscapeProcessing == escapeProcessingDone); + } + return super.preProcess(sql, interceptedStatement, connection); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/DataSourceTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/DataSourceTest.java new file mode 100644 index 0000000..884abe1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/DataSourceTest.java @@ -0,0 +1,230 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.File; +import java.sql.Connection; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; +import javax.sql.DataSource; +import javax.sql.PooledConnection; + +import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; + +import testsuite.BaseTestCase; + +public class DataSourceTest extends BaseTestCase { + private Context ctx; + + private File tempDir; + + /** + * Creates a new DataSourceTest object. + * + * @param name + */ + public DataSourceTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(DataSourceTest.class); + } + + /** + * Sets up this test, calling registerDataSource() to bind a DataSource into + * JNDI, using the FSContext JNDI provider from Sun + * + * @throws Exception + * if an error occurs. + */ + @Override + public void setUp() throws Exception { + super.setUp(); + registerDataSource(); + } + + /** + * Un-binds the DataSource, and cleans up the filesystem + * + * @throws Exception + * if an error occurs + */ + @Override + public void tearDown() throws Exception { + try { + this.ctx.unbind(this.tempDir.getAbsolutePath() + "/test"); + this.ctx.close(); + this.tempDir.delete(); + } finally { + super.tearDown(); + } + } + + /** + * Tests that we can get a connection from the DataSource bound in JNDI + * during test setup + * + * @throws Exception + * if an error occurs + */ + public void testDataSource() throws Exception { + NameParser nameParser = this.ctx.getNameParser(""); + Name datasourceName = nameParser.parse("_test"); + Object obj = this.ctx.lookup(datasourceName); + DataSource boundDs = null; + + if (obj instanceof DataSource) { + boundDs = (DataSource) obj; + } else if (obj instanceof Reference) { + // + // For some reason, this comes back as a Reference instance under CruiseControl !? + // + Reference objAsRef = (Reference) obj; + ObjectFactory factory = (ObjectFactory) Class.forName(objAsRef.getFactoryClassName()).newInstance(); + boundDs = (DataSource) factory.getObjectInstance(objAsRef, datasourceName, this.ctx, new Hashtable()); + } + + assertTrue("Datasource not bound", boundDs != null); + + Connection con = boundDs.getConnection(); + con.close(); + assertTrue("Connection can not be obtained from data source", con != null); + } + + /** + * Tests whether Connection.changeUser() (and thus pooled connections) + * restore character set information correctly. + * + * @throws Exception + * if the test fails. + */ + public void testChangeUserAndCharsets() throws Exception { + if (versionMeetsMinimum(4, 1)) { + MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource(); + ds.setURL(BaseTestCase.dbUrl); + ds.setCharacterEncoding("utf-8"); + PooledConnection pooledConnection = ds.getPooledConnection(); + + Connection connToMySQL = pooledConnection.getConnection(); + this.rs = connToMySQL.createStatement().executeQuery("SELECT @@character_set_results"); + assertTrue(this.rs.next()); + + String toCheck = null; + + if (versionMeetsMinimum(4, 1, 15)) { + if (versionMeetsMinimum(5, 0)) { + if (versionMeetsMinimum(5, 0, 13)) { + toCheck = null; + } else { + toCheck = "NULL"; + } + } else { + toCheck = null; + } + } else { + toCheck = "NULL"; + } + + assertEquals(toCheck, this.rs.getString(1)); + + this.rs = connToMySQL.createStatement().executeQuery("SHOW SESSION VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + + //Cause of utf8mb4 + assertEquals(0, this.rs.getString(2).indexOf("utf8")); + + connToMySQL.close(); + + connToMySQL = pooledConnection.getConnection(); + this.rs = connToMySQL.createStatement().executeQuery("SELECT @@character_set_results"); + assertTrue(this.rs.next()); + assertEquals(toCheck, this.rs.getString(1)); + + this.rs = connToMySQL.createStatement().executeQuery("SHOW SESSION VARIABLES LIKE 'character_set_client'"); + assertTrue(this.rs.next()); + + //Cause of utf8mb4 + assertEquals(0, this.rs.getString(2).indexOf("utf8")); + + pooledConnection.getConnection().close(); + } + } + + /** + * Tests whether XADataSources can be bound into JNDI + * + * @throws Exception + * if the test fails. + */ + public void testXADataSource() throws Exception { + + MysqlXADataSource ds = new MysqlXADataSource(); + ds.setUrl(dbUrl); + + String name = "XA"; + this.ctx.rebind(name, ds); + + Object result = this.ctx.lookup(name); + + assertNotNull("XADataSource not bound into JNDI", result); + } + + /** + * This method is separated from the rest of the example since you normally + * would NOT register a JDBC driver in your code. It would likely be + * configered into your naming and directory service using some GUI. + * + * @throws Exception + * if an error occurs + */ + private void registerDataSource() throws Exception { + this.tempDir = File.createTempFile("jnditest", null); + this.tempDir.delete(); + this.tempDir.mkdir(); + this.tempDir.deleteOnExit(); + + com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds; + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); + env.put(Context.PROVIDER_URL, this.tempDir.toURI().toString()); + this.ctx = new InitialContext(env); + assertTrue("Naming Context not created", this.ctx != null); + ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource(); + ds.setUrl(dbUrl); // from BaseTestCase + this.ctx.bind("_test", ds); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/DateTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/DateTest.java new file mode 100644 index 0000000..cafd054 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/DateTest.java @@ -0,0 +1,360 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; +import java.util.Properties; +import java.util.TimeZone; + +import com.mysql.jdbc.SQLError; + +import testsuite.BaseTestCase; + +public class DateTest extends BaseTestCase { + /** + * Creates a new DateTest object. + * + * @param name + */ + public DateTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(DateTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + } + + public void testTimestamp() throws SQLException { + createTable("DATETEST", "(tstamp TIMESTAMP, dt DATE, dtime DATETIME, tm TIME)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO DATETEST(tstamp, dt, dtime, tm) VALUES (?, ?, ?, ?)"); + + // TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.MONTH, 6); + cal.set(Calendar.DAY_OF_MONTH, 3); + cal.set(Calendar.YEAR, 2002); + cal.set(Calendar.HOUR, 7); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.AM_PM, Calendar.AM); + cal.getTime(); + System.out.println(cal); + + // DateFormat df = SimpleDateFormat.getInstance(); + DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z"); + + Calendar calGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + // df.setTimeZone(TimeZone.getTimeZone("GMT")); + Timestamp nowTstamp = new Timestamp(cal.getTime().getTime()); + java.sql.Date nowDate = new java.sql.Date(cal.getTime().getTime()); + Timestamp nowDatetime = new Timestamp(cal.getTime().getTime()); + java.sql.Time nowTime = new java.sql.Time(cal.getTime().getTime()); + System.out.println("** Times with given calendar (before storing) **\n"); + System.out.println("TIMESTAMP:\t" + nowTstamp.getTime() + " -> " + df.format(nowTstamp)); + System.out.println("DATE:\t\t" + nowDate.getTime() + " -> " + df.format(nowDate)); + System.out.println("DATETIME:\t" + nowDatetime.getTime() + " -> " + df.format(nowDatetime)); + System.out.println("DATE:\t\t" + nowDate.getTime() + " -> " + df.format(nowDate)); + System.out.println("TIME:\t\t" + nowTime.getTime() + " -> " + df.format(nowTime)); + System.out.println("\n"); + this.pstmt.setTimestamp(1, nowTstamp, calGMT); + // have to use the same TimeZone as used to create or there will be + // shift + this.pstmt.setDate(2, nowDate, cal); + this.pstmt.setTimestamp(3, nowDatetime, calGMT); + // have to use the same TimeZone as used to create or there will be + // shift + this.pstmt.setTime(4, nowTime, cal); + this.pstmt.execute(); + + this.pstmt.getUpdateCount(); + this.pstmt.clearParameters(); + this.rs = this.stmt.executeQuery("SELECT * from DATETEST"); + + java.sql.Date thenDate = null; + + while (this.rs.next()) { + Timestamp thenTstamp = this.rs.getTimestamp(1, calGMT); + thenDate = this.rs.getDate(2, cal); + + java.sql.Timestamp thenDatetime = this.rs.getTimestamp(3, calGMT); + + java.sql.Time thenTime = this.rs.getTime(4, cal); + System.out.println("** Times with given calendar (retrieved from database) **\n"); + System.out.println("TIMESTAMP:\t" + thenTstamp.getTime() + " -> " + df.format(thenTstamp)); + System.out.println("DATE:\t\t" + thenDate.getTime() + " -> " + df.format(thenDate)); + System.out.println("DATETIME:\t" + thenDatetime.getTime() + " -> " + df.format(thenDatetime)); + System.out.println("TIME:\t\t" + thenTime.getTime() + " -> " + df.format(thenTime)); + System.out.println("\n"); + } + + this.rs.close(); + this.rs = null; + } + + public void testNanosParsing() throws SQLException { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testNanosParsing"); + this.stmt.executeUpdate("CREATE TABLE testNanosParsing (dateIndex int, field1 VARCHAR(32))"); + this.stmt.executeUpdate("INSERT INTO testNanosParsing VALUES (1, '1969-12-31 18:00:00.0'), (2, '1969-12-31 18:00:00.000000090'), " + + "(3, '1969-12-31 18:00:00.000000900'), (4, '1969-12-31 18:00:00.000009000'), (5, '1969-12-31 18:00:00.000090000'), " + + "(6, '1969-12-31 18:00:00.000900000'), (7, '1969-12-31 18:00:00.')"); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testNanosParsing ORDER BY dateIndex ASC"); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() == 0); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() + " != 90", this.rs.getTimestamp(1).getNanos() == 90); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() + " != 900", this.rs.getTimestamp(1).getNanos() == 900); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() + " != 9000", this.rs.getTimestamp(1).getNanos() == 9000); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() + " != 90000", this.rs.getTimestamp(1).getNanos() == 90000); + assertTrue(this.rs.next()); + assertTrue(this.rs.getTimestamp(1).getNanos() + " != 900000", this.rs.getTimestamp(1).getNanos() == 900000); + assertTrue(this.rs.next()); + + try { + this.rs.getTimestamp(1); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testNanosParsing"); + } + } + + /** + * Tests the configurability of all-zero date/datetime/timestamp handling in the driver. + * + * @throws Exception + * if the test fails. + */ + public void testZeroDateBehavior() throws Exception { + Connection testConn = this.conn; + Connection roundConn = null; + Connection nullConn = null; + Connection exceptionConn = null; + try { + if (versionMeetsMinimum(5, 7, 4)) { + Properties props = new Properties(); + props.put("jdbcCompliantTruncation", "false"); + if (versionMeetsMinimum(5, 7, 5)) { + String sqlMode = getMysqlVariable("sql_mode"); + if (sqlMode.contains("STRICT_TRANS_TABLES")) { + sqlMode = removeSqlMode("STRICT_TRANS_TABLES", sqlMode); + props.put("sessionVariables", "sql_mode='" + sqlMode + "'"); + } + } + testConn = getConnectionWithProps(props); + this.stmt = testConn.createStatement(); + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testZeroDateBehavior"); + this.stmt.executeUpdate("CREATE TABLE testZeroDateBehavior(fieldAsString VARCHAR(32), fieldAsDateTime DATETIME)"); + this.stmt.executeUpdate("INSERT INTO testZeroDateBehavior VALUES ('0000-00-00 00:00:00', '0000-00-00 00:00:00')"); + + roundConn = getConnectionWithProps("zeroDateTimeBehavior=round"); + Statement roundStmt = roundConn.createStatement(); + this.rs = roundStmt.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + this.rs.next(); + + assertEquals("0001-01-01", this.rs.getDate(1).toString()); + assertEquals("0001-01-01 00:00:00.0", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(1))); + assertEquals("0001-01-01", this.rs.getDate(2).toString()); + assertEquals("0001-01-01 00:00:00.0", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(2))); + + PreparedStatement roundPrepStmt = roundConn.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + this.rs = roundPrepStmt.executeQuery(); + this.rs.next(); + + assertEquals("0001-01-01", this.rs.getDate(1).toString()); + assertEquals("0001-01-01 00:00:00.0", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(1))); + assertEquals("0001-01-01", this.rs.getDate(2).toString()); + assertEquals("0001-01-01 00:00:00.0", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(2))); + + nullConn = getConnectionWithProps("zeroDateTimeBehavior=convertToNull"); + Statement nullStmt = nullConn.createStatement(); + this.rs = nullStmt.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + + this.rs.next(); + + assertNull(this.rs.getDate(1)); + assertNull(this.rs.getTimestamp(1)); + assertNull(this.rs.getDate(2)); + assertNull(this.rs.getTimestamp(2)); + + PreparedStatement nullPrepStmt = nullConn.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + this.rs = nullPrepStmt.executeQuery(); + + this.rs.next(); + + assertNull(this.rs.getDate(1)); + assertNull(this.rs.getTimestamp(1)); + assertNull(this.rs.getDate(2)); + assertNull(this.rs.getTimestamp(2)); + assertNull(this.rs.getString(2)); + + exceptionConn = getConnectionWithProps("zeroDateTimeBehavior=exception"); + Statement exceptionStmt = exceptionConn.createStatement(); + this.rs = exceptionStmt.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + + this.rs.next(); + + try { + this.rs.getDate(1); + fail("Exception should have been thrown when trying to retrieve invalid date"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getTimestamp(1); + fail("Exception should have been thrown when trying to retrieve invalid date"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getDate(2); + fail("Exception should have been thrown when trying to retrieve invalid date"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getTimestamp(2); + fail("Exception should have been thrown when trying to retrieve invalid date"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + PreparedStatement exceptionPrepStmt = exceptionConn.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior"); + + try { + this.rs = exceptionPrepStmt.executeQuery(); + this.rs.next(); + this.rs.getDate(2); + fail("Exception should have been thrown when trying to retrieve invalid date"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx.getSQLState())); + } + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testZeroDateBehavior"); + if (exceptionConn != null) { + exceptionConn.close(); + } + + if (nullConn != null) { + nullConn.close(); + } + + if (roundConn != null) { + roundConn.close(); + } + + if (testConn != this.conn) { + testConn.close(); + } + } + } + + @SuppressWarnings("deprecation") + public void testReggieBug() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testReggieBug"); + this.stmt.executeUpdate("CREATE TABLE testReggieBug (field1 DATE)"); + + PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO testReggieBug VALUES (?)"); + pStmt.setDate(1, new Date(2004 - 1900, 07, 28)); + pStmt.executeUpdate(); + this.rs = this.stmt.executeQuery("SELECT * FROM testReggieBug"); + this.rs.next(); + System.out.println(this.rs.getDate(1)); + this.rs = this.conn.prepareStatement("SELECT * FROM testReggieBug").executeQuery(); + this.rs.next(); + System.out.println(this.rs.getDate(1)); + + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testReggieBug"); + } + } + + public void testNativeConversions() throws Exception { + Timestamp ts = new Timestamp(System.currentTimeMillis()); + Date dt = new Date(ts.getTime()); + Time tm = new Time(ts.getTime()); + + createTable("testNativeConversions", "(time_field TIME, date_field DATE, datetime_field DATETIME, timestamp_field TIMESTAMP)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testNativeConversions VALUES (?,?,?,?)"); + this.pstmt.setTime(1, tm); + this.pstmt.setDate(2, dt); + this.pstmt.setTimestamp(3, ts); + this.pstmt.setTimestamp(4, ts); + this.pstmt.execute(); + this.pstmt.close(); + + this.pstmt = this.conn.prepareStatement("SELECT time_field, date_field, datetime_field, timestamp_field FROM testNativeConversions"); + this.rs = this.pstmt.executeQuery(); + assertTrue(this.rs.next()); + System.out.println(this.rs.getTime(1)); + System.out.println(this.rs.getTime(2)); + System.out.println(this.rs.getTime(3)); + System.out.println(this.rs.getTime(4)); + System.out.println(); + System.out.println(this.rs.getDate(1)); + System.out.println(this.rs.getDate(2)); + System.out.println(this.rs.getDate(3)); + System.out.println(this.rs.getDate(4)); + System.out.println(); + System.out.println(this.rs.getTimestamp(1)); + System.out.println(this.rs.getTimestamp(2)); + System.out.println(this.rs.getTimestamp(3)); + System.out.println(this.rs.getTimestamp(4)); + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/EscapeProcessingTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/EscapeProcessingTest.java new file mode 100644 index 0000000..ff7500a --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/EscapeProcessingTest.java @@ -0,0 +1,157 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.Connection; +import java.util.Properties; +import java.util.TimeZone; + +import testsuite.BaseTestCase; + +/** + * Tests escape processing + */ +public class EscapeProcessingTest extends BaseTestCase { + /** + * Constructor for EscapeProcessingTest. + * + * @param name + * the test to run + */ + public EscapeProcessingTest(String name) { + super(name); + } + + /** + * Tests the escape processing functionality + * + * @throws Exception + * if an error occurs + */ + public void testEscapeProcessing() throws Exception { + String results = "select dayname (abs(now())), -- Today \n" // + + " '1997-05-24', -- a date \n" + " '10:30:29', -- a time \n" + + (versionMeetsMinimum(5, 6, 4) ? " '1997-05-24 10:30:29.123', -- a timestamp \n" + : " '1997-05-24 10:30:29', -- a timestamp \n") + + " '{string data with { or } will not be altered' \n" + "-- Also note that you can safely include { and } in comments"; + + String exSql = "select {fn dayname ({fn abs({fn now()})})}, -- Today \n" // + + " {d '1997-05-24'}, -- a date \n" + " {t '10:30:29' }, -- a time \n" + + " {ts '1997-05-24 10:30:29.123'}, -- a timestamp \n" + " '{string data with { or } will not be altered' \n" + + "-- Also note that you can safely include { and } in comments"; + + String escapedSql = this.conn.nativeSQL(exSql); + + assertTrue(results.equals(escapedSql)); + + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(EscapeProcessingTest.class); + } + + /** + * JDBC-4.0 spec will allow either SQL_ or not for type in {fn convert ...} + * + * @throws Exception + * if the test fails + */ + public void testConvertEscape() throws Exception { + assertEquals(this.conn.nativeSQL("{fn convert(abcd, SQL_INTEGER)}"), this.conn.nativeSQL("{fn convert(abcd, INTEGER)}")); + } + + /** + * Tests that the escape tokenizer converts timestamp values + * wrt. timezones when useTimezone=true. + * + * @throws Exception + * if the test fails. + */ + public void testTimestampConversion() throws Exception { + TimeZone currentTimezone = TimeZone.getDefault(); + String[] availableIds = TimeZone.getAvailableIDs(currentTimezone.getRawOffset() + (3600 * 1000 * 2)); + String newTimezone = null; + + if (availableIds.length > 0) { + newTimezone = availableIds[0]; + } else { + newTimezone = "UTC"; // punt + } + + Properties props = new Properties(); + + props.setProperty("useTimezone", "true"); + props.setProperty("serverTimezone", newTimezone); + Connection tzConn = null; + + try { + String escapeToken = "SELECT {ts '2002-11-12 10:00:00'} {t '05:11:02'}"; + tzConn = getConnectionWithProps(props); + assertTrue(!tzConn.nativeSQL(escapeToken).equals(this.conn.nativeSQL(escapeToken))); + } finally { + if (tzConn != null) { + tzConn.close(); + } + } + } + + /** + * Tests fix for BUG#51313 - Escape processing is confused by multiple backslashes. + * + * @throws Exception + */ + public void testBug51313() throws Exception { + this.stmt = this.conn.createStatement(); + + this.rs = this.stmt.executeQuery("SELECT {fn lcase('My{fn UCASE(sql)}} -- DATABASE')}, {fn ucase({fn lcase('SERVER')})}" + + " -- {escape } processing test\n -- this {fn ucase('comment') is in line 2\r\n" + + " -- this in line 3, and previous escape sequence was malformed\n"); + assertTrue(this.rs.next()); + assertEquals("my{fn ucase(sql)}} -- database", this.rs.getString(1)); + assertEquals("SERVER", this.rs.getString(2)); + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT 'MySQL \\\\\\' testing {long \\\\\\' escape -- { \\\\\\' sequences \\\\\\' } } with escape processing '"); + assertTrue(this.rs.next()); + assertEquals("MySQL \\\' testing {long \\\' escape -- { \\\' sequences \\\' } } with escape processing ", this.rs.getString(1)); + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT 'MySQL \\'', '{ testing doubled -- } ''\\\\\\''' quotes '"); + assertTrue(this.rs.next()); + assertEquals("MySQL \'", this.rs.getString(1)); + assertEquals("{ testing doubled -- } '\\\'' quotes ", this.rs.getString(2)); + this.rs.close(); + + this.rs = this.stmt.executeQuery("SELECT 'MySQL \\\\\\'''', '{ testing doubled -- } ''\\''' quotes '"); + assertTrue(this.rs.next()); + assertEquals("MySQL \\\''", this.rs.getString(1)); + assertEquals("{ testing doubled -- } '\'' quotes ", this.rs.getString(2)); + this.rs.close(); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/MetadataTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/MetadataTest.java new file mode 100644 index 0000000..8a2224c --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/MetadataTest.java @@ -0,0 +1,824 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import com.mysql.jdbc.StringUtils; + +import testsuite.BaseTestCase; + +/** + * Tests DatabaseMetaData methods. + */ +public class MetadataTest extends BaseTestCase { + /** + * Creates a new MetadataTest object. + * + * @param name + */ + public MetadataTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MetadataTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + } + + public void testForeignKeys() throws SQLException { + try { + createTestTable(); + + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getImportedKeys(null, null, "child"); + + while (this.rs.next()) { + String pkColumnName = this.rs.getString("PKCOLUMN_NAME"); + String fkColumnName = this.rs.getString("FKCOLUMN_NAME"); + assertTrue("Primary Key not returned correctly ('" + pkColumnName + "' != 'parent_id')", pkColumnName.equalsIgnoreCase("parent_id")); + assertTrue("Foreign Key not returned correctly ('" + fkColumnName + "' != 'parent_id_fk')", fkColumnName.equalsIgnoreCase("parent_id_fk")); + } + + this.rs.close(); + this.rs = dbmd.getExportedKeys(null, null, "parent"); + + while (this.rs.next()) { + String pkColumnName = this.rs.getString("PKCOLUMN_NAME"); + String fkColumnName = this.rs.getString("FKCOLUMN_NAME"); + String fkTableName = this.rs.getString("FKTABLE_NAME"); + assertTrue("Primary Key not returned correctly ('" + pkColumnName + "' != 'parent_id')", pkColumnName.equalsIgnoreCase("parent_id")); + assertTrue("Foreign Key table not returned correctly for getExportedKeys ('" + fkTableName + "' != 'child')", + fkTableName.equalsIgnoreCase("child")); + assertTrue("Foreign Key not returned correctly for getExportedKeys ('" + fkColumnName + "' != 'parent_id_fk')", + fkColumnName.equalsIgnoreCase("parent_id_fk")); + } + + this.rs.close(); + + this.rs = dbmd.getCrossReference(null, null, "cpd_foreign_3", null, null, "cpd_foreign_4"); + + assertTrue(this.rs.next()); + + String pkColumnName = this.rs.getString("PKCOLUMN_NAME"); + String pkTableName = this.rs.getString("PKTABLE_NAME"); + String fkColumnName = this.rs.getString("FKCOLUMN_NAME"); + String fkTableName = this.rs.getString("FKTABLE_NAME"); + String deleteAction = cascadeOptionToString(this.rs.getInt("DELETE_RULE")); + String updateAction = cascadeOptionToString(this.rs.getInt("UPDATE_RULE")); + + assertEquals(pkColumnName, "cpd_foreign_1_id"); + assertEquals(pkTableName, "cpd_foreign_3"); + assertEquals(fkColumnName, "cpd_foreign_1_id"); + assertEquals(fkTableName, "cpd_foreign_4"); + assertEquals(deleteAction, "NO ACTION"); + assertEquals(updateAction, "CASCADE"); + + this.rs.close(); + this.rs = null; + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS parent"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_4"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_3"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_2"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_1"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable2"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable1"); + } + + } + + public void testGetPrimaryKeys() throws SQLException { + try { + createTable("multikey", "(d INT NOT NULL, b INT NOT NULL, a INT NOT NULL, c INT NOT NULL, PRIMARY KEY (d, b, a, c))"); + DatabaseMetaData dbmd = this.conn.getMetaData(); + this.rs = dbmd.getPrimaryKeys(this.conn.getCatalog(), "", "multikey"); + + short[] keySeqs = new short[4]; + String[] columnNames = new String[4]; + int i = 0; + + while (this.rs.next()) { + this.rs.getString("TABLE_NAME"); + columnNames[i] = this.rs.getString("COLUMN_NAME"); + + this.rs.getString("PK_NAME"); + keySeqs[i] = this.rs.getShort("KEY_SEQ"); + i++; + } + + if ((keySeqs[0] != 3) && (keySeqs[1] != 2) && (keySeqs[2] != 4) && (keySeqs[3] != 1)) { + fail("Keys returned in wrong order"); + } + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (SQLException sqlEx) { + /* ignore */ + } + } + } + } + + private static String cascadeOptionToString(int option) { + switch (option) { + case DatabaseMetaData.importedKeyCascade: + return "CASCADE"; + + case DatabaseMetaData.importedKeySetNull: + return "SET NULL"; + + case DatabaseMetaData.importedKeyRestrict: + return "RESTRICT"; + + case DatabaseMetaData.importedKeyNoAction: + return "NO ACTION"; + } + + return "SET DEFAULT"; + } + + private void createTestTable() throws SQLException { + //Needed for previous runs that did not clean-up + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS parent"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS multikey"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_4"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_3"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_2"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_1"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable2"); + this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable1"); + + createTable("parent", "(parent_id INT NOT NULL, PRIMARY KEY (parent_id))", "INNODB"); + createTable("child", "(child_id INT, parent_id_fk INT, INDEX par_ind (parent_id_fk), FOREIGN KEY (parent_id_fk) REFERENCES parent(parent_id)) ", + "INNODB"); + + // Test compound foreign keys + try { + createTable("cpd_foreign_1", "(id int(8) not null auto_increment primary key,name varchar(255) not null unique,key (id))", "InnoDB"); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max key length") != -1) { + createTable("cpd_foreign_1", "(id int(8) not null auto_increment primary key,name varchar(180) not null unique,key (id))", "InnoDB"); + } + } + + createTable("cpd_foreign_2", "(id int(8) not null auto_increment primary key,key (id),name varchar(255)) ", "InnoDB"); + createTable("cpd_foreign_3", + "(cpd_foreign_1_id int(8) not null,cpd_foreign_2_id int(8) not null,key(cpd_foreign_1_id)," + + "key(cpd_foreign_2_id),primary key (cpd_foreign_1_id, cpd_foreign_2_id)," + + "foreign key (cpd_foreign_1_id) references cpd_foreign_1(id),foreign key (cpd_foreign_2_id) references cpd_foreign_2(id)) ", + "InnoDB"); + createTable("cpd_foreign_4", + "(cpd_foreign_1_id int(8) not null,cpd_foreign_2_id int(8) not null,key(cpd_foreign_1_id)," + + "key(cpd_foreign_2_id),primary key (cpd_foreign_1_id, cpd_foreign_2_id),foreign key (cpd_foreign_1_id, cpd_foreign_2_id) " + + "references cpd_foreign_3(cpd_foreign_1_id, cpd_foreign_2_id) ON DELETE RESTRICT ON UPDATE CASCADE) ", + "InnoDB"); + + createTable("fktable1", "(TYPE_ID int not null, TYPE_DESC varchar(32), primary key(TYPE_ID))", "InnoDB"); + createTable("fktable2", "(KEY_ID int not null, COF_NAME varchar(32), PRICE float, TYPE_ID int, primary key(KEY_ID), " + + "index(TYPE_ID), foreign key(TYPE_ID) references fktable1(TYPE_ID)) ", "InnoDB"); + } + + /** + * Tests the implementation of metadata for views. + * + * This test automatically detects whether or not the server it is running + * against supports the creation of views. + * + * @throws SQLException + * if the test fails. + */ + public void testViewMetaData() throws SQLException { + try { + this.rs = this.conn.getMetaData().getTableTypes(); + + while (this.rs.next()) { + if ("VIEW".equalsIgnoreCase(this.rs.getString(1))) { + + this.stmt.executeUpdate("DROP VIEW IF EXISTS vTestViewMetaData"); + createTable("testViewMetaData", "(field1 INT)"); + this.stmt.executeUpdate("CREATE VIEW vTestViewMetaData AS SELECT field1 FROM testViewMetaData"); + + ResultSet tablesRs = null; + + try { + tablesRs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, "%ViewMetaData", new String[] { "TABLE", "VIEW" }); + assertTrue(tablesRs.next()); + assertTrue("testViewMetaData".equalsIgnoreCase(tablesRs.getString(3))); + assertTrue(tablesRs.next()); + assertTrue("vTestViewMetaData".equalsIgnoreCase(tablesRs.getString(3))); + + } finally { + if (tablesRs != null) { + tablesRs.close(); + } + } + + try { + tablesRs = this.conn.getMetaData().getTables(this.conn.getCatalog(), null, "%ViewMetaData", new String[] { "TABLE" }); + assertTrue(tablesRs.next()); + assertTrue("testViewMetaData".equalsIgnoreCase(tablesRs.getString(3))); + assertTrue(!tablesRs.next()); + } finally { + if (tablesRs != null) { + tablesRs.close(); + } + } + break; + } + } + + } finally { + if (this.rs != null) { + this.rs.close(); + } + this.stmt.executeUpdate("DROP VIEW IF EXISTS vTestViewMetaData"); + } + } + + /** + * Tests detection of read-only fields when the server is 4.1.0 or newer. + * + * @throws Exception + * if the test fails. + */ + public void testRSMDIsReadOnly() throws Exception { + try { + this.rs = this.stmt.executeQuery("SELECT 1"); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + + if (versionMeetsMinimum(4, 1)) { + assertTrue(rsmd.isReadOnly(1)); + + try { + createTable("testRSMDIsReadOnly", "(field1 INT)"); + this.stmt.executeUpdate("INSERT INTO testRSMDIsReadOnly VALUES (1)"); + + this.rs = this.stmt.executeQuery("SELECT 1, field1 + 1, field1 FROM testRSMDIsReadOnly"); + rsmd = this.rs.getMetaData(); + + assertTrue(rsmd.isReadOnly(1)); + assertTrue(rsmd.isReadOnly(2)); + assertTrue(!rsmd.isReadOnly(3)); + } finally { + } + } else { + assertTrue(rsmd.isReadOnly(1) == false); + } + } finally { + if (this.rs != null) { + this.rs.close(); + } + } + } + + public void testBitType() throws Exception { + if (versionMeetsMinimum(5, 0, 3)) { + try { + createTable("testBitType", "(field1 BIT, field2 BIT, field3 BIT)"); + this.stmt.executeUpdate("INSERT INTO testBitType VALUES (1, 0, NULL)"); + this.rs = this.stmt.executeQuery("SELECT field1, field2, field3 FROM testBitType"); + this.rs.next(); + + assertTrue(((Boolean) this.rs.getObject(1)).booleanValue()); + assertTrue(!((Boolean) this.rs.getObject(2)).booleanValue()); + assertEquals(this.rs.getObject(3), null); + + System.out.println(this.rs.getObject(1) + ", " + this.rs.getObject(2) + ", " + this.rs.getObject(3)); + + this.rs = this.conn.prepareStatement("SELECT field1, field2, field3 FROM testBitType").executeQuery(); + this.rs.next(); + + assertTrue(((Boolean) this.rs.getObject(1)).booleanValue()); + assertTrue(!((Boolean) this.rs.getObject(2)).booleanValue()); + + assertEquals(this.rs.getObject(3), null); + byte[] asBytesTrue = this.rs.getBytes(1); + byte[] asBytesFalse = this.rs.getBytes(2); + byte[] asBytesNull = this.rs.getBytes(3); + + assertEquals(asBytesTrue[0], 1); + assertEquals(asBytesFalse[0], 0); + assertEquals(asBytesNull, null); + + createTable("testBitField", "(field1 BIT(9))"); + this.rs = this.stmt.executeQuery("SELECT field1 FROM testBitField"); + System.out.println(this.rs.getMetaData().getColumnClassName(1)); + } finally { + } + } + } + + public void testSupportsSelectForUpdate() throws Exception { + boolean supportsForUpdate = this.conn.getMetaData().supportsSelectForUpdate(); + + if (this.versionMeetsMinimum(4, 0)) { + assertTrue(supportsForUpdate); + } else { + assertTrue(!supportsForUpdate); + } + } + + public void testTinyint1IsBit() throws Exception { + String tableName = "testTinyint1IsBit"; + // Can't use 'BIT' or boolean + createTable(tableName, "(field1 TINYINT(1))"); + this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (1)"); + + Properties props = new Properties(); + props.setProperty("tinyint1IsBit", "true"); + props.setProperty("transformedBitIsBoolean", "true"); + Connection boolConn = getConnectionWithProps(props); + + this.rs = boolConn.createStatement().executeQuery("SELECT field1 FROM " + tableName); + checkBitOrBooleanType(false); + + this.rs = boolConn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + checkBitOrBooleanType(false); + + this.rs = boolConn.getMetaData().getColumns(boolConn.getCatalog(), null, tableName, "field1"); + assertTrue(this.rs.next()); + + if (versionMeetsMinimum(4, 1)) { + assertEquals(Types.BOOLEAN, this.rs.getInt("DATA_TYPE")); + } else { + assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE")); + } + + if (versionMeetsMinimum(4, 1)) { + assertEquals("BOOLEAN", this.rs.getString("TYPE_NAME")); + } else { + assertEquals("BIT", this.rs.getString("TYPE_NAME")); + } + + props.clear(); + props.setProperty("transformedBitIsBoolean", "false"); + props.setProperty("tinyint1IsBit", "true"); + + Connection bitConn = getConnectionWithProps(props); + + this.rs = bitConn.createStatement().executeQuery("SELECT field1 FROM " + tableName); + checkBitOrBooleanType(true); + + this.rs = bitConn.prepareStatement("SELECT field1 FROM " + tableName).executeQuery(); + checkBitOrBooleanType(true); + + this.rs = bitConn.getMetaData().getColumns(boolConn.getCatalog(), null, tableName, "field1"); + assertTrue(this.rs.next()); + + assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE")); + + assertEquals("BIT", this.rs.getString("TYPE_NAME")); + } + + private void checkBitOrBooleanType(boolean usingBit) throws SQLException { + + assertTrue(this.rs.next()); + assertEquals("java.lang.Boolean", this.rs.getObject(1).getClass().getName()); + if (!usingBit) { + if (versionMeetsMinimum(4, 1)) { + assertEquals(Types.BOOLEAN, this.rs.getMetaData().getColumnType(1)); + } else { + assertEquals(Types.BIT, this.rs.getMetaData().getColumnType(1)); + } + } else { + assertEquals(Types.BIT, this.rs.getMetaData().getColumnType(1)); + } + + assertEquals("java.lang.Boolean", this.rs.getMetaData().getColumnClassName(1)); + } + + /** + * Tests the implementation of Information Schema for primary keys. + */ + public void testGetPrimaryKeysUsingInfoShcema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + createTable("t1", "(c1 int(1) primary key)"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getPrimaryKeys(null, null, "t1"); + this.rs.next(); + assertEquals("t1", this.rs.getString("TABLE_NAME")); + assertEquals("c1", this.rs.getString("COLUMN_NAME")); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for index info. + */ + public void testGetIndexInfoUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + createTable("t1", "(c1 int(1))"); + this.stmt.executeUpdate("CREATE INDEX index1 ON t1 (c1)"); + + Connection conn1 = null; + + try { + conn1 = getConnectionWithProps("useInformationSchema=true"); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getIndexInfo(conn1.getCatalog(), null, "t1", false, true); + this.rs.next(); + assertEquals("t1", this.rs.getString("TABLE_NAME")); + assertEquals("c1", this.rs.getString("COLUMN_NAME")); + assertEquals("1", this.rs.getString("NON_UNIQUE")); + assertEquals("index1", this.rs.getString("INDEX_NAME")); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for columns. + */ + public void testGetColumnsUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + createTable("t1", "(c1 char(1))"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getColumns(null, null, "t1", null); + this.rs.next(); + assertEquals("t1", this.rs.getString("TABLE_NAME")); + assertEquals("c1", this.rs.getString("COLUMN_NAME")); + assertEquals("CHAR", this.rs.getString("TYPE_NAME")); + assertEquals("1", this.rs.getString("COLUMN_SIZE")); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for tables. + */ + public void testGetTablesUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + createTable("`t1-1`", "(c1 char(1))"); + createTable("`t1-2`", "(c1 char(1))"); + createTable("`t2`", "(c1 char(1))"); + Set tableNames = new HashSet(); + tableNames.add("t1-1"); + tableNames.add("t1-2"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + // pattern matching for table name + this.rs = metaData.getTables(null, null, "t1-_", null); + while (this.rs.next()) { + assertTrue(tableNames.remove(this.rs.getString("TABLE_NAME"))); + } + assertTrue(tableNames.isEmpty()); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for column privileges. + */ + public void testGetColumnPrivilegesUsingInfoSchema() throws Exception { + String dontRunPropertyName = "com.mysql.jdbc.testsuite.cantGrant"; + + if (!runTestIfSysPropDefined(dontRunPropertyName)) { + if (versionMeetsMinimum(5, 0, 7)) { + Properties props = new Properties(); + + props.put("useInformationSchema", "true"); + Connection conn1 = null; + Statement stmt1 = null; + String userHostQuoted = null; + + boolean grantFailed = true; + + try { + conn1 = getConnectionWithProps(props); + stmt1 = conn1.createStatement(); + createTable("t1", "(c1 int)"); + this.rs = stmt1.executeQuery("SELECT USER()"); + this.rs.next(); + String user = this.rs.getString(1); + List userHost = StringUtils.split(user, "@", false); + if (userHost.size() < 2) { + fail("This test requires a JDBC URL with a user, and won't work with the anonymous user. " + + "You can skip this test by setting the system property " + dontRunPropertyName); + } + userHostQuoted = "'" + userHost.get(0) + "'@'" + userHost.get(1) + "'"; + + try { + stmt1.executeUpdate("GRANT update (c1) on t1 to " + userHostQuoted); + + grantFailed = false; + + } catch (SQLException sqlEx) { + fail("This testcase needs to be run with a URL that allows the user to issue GRANTs " + + " in the current database. You can skip this test by setting the system property \"" + dontRunPropertyName + "\"."); + } + + if (!grantFailed) { + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getColumnPrivileges(null, null, "t1", null); + this.rs.next(); + assertEquals("t1", this.rs.getString("TABLE_NAME")); + assertEquals("c1", this.rs.getString("COLUMN_NAME")); + assertEquals(userHostQuoted, this.rs.getString("GRANTEE")); + assertEquals("UPDATE", this.rs.getString("PRIVILEGE")); + } + } finally { + if (stmt1 != null) { + + if (!grantFailed) { + stmt1.executeUpdate("REVOKE UPDATE (c1) ON t1 FROM " + userHostQuoted); + } + + stmt1.close(); + } + + if (conn1 != null) { + conn1.close(); + } + } + } + } + } + + /** + * Tests the implementation of Information Schema for description + * of stored procedures available in a catalog. + */ + public void testGetProceduresUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + createProcedure("sp1", "()\n BEGIN\nSELECT 1;end\n"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getProcedures(null, null, "sp1"); + this.rs.next(); + assertEquals("sp1", this.rs.getString("PROCEDURE_NAME")); + assertEquals("1", this.rs.getString("PROCEDURE_TYPE")); + } finally { + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for foreign key. + */ + public void testGetCrossReferenceUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB"); + this.stmt.executeUpdate( + "CREATE TABLE child(id INT, parent_id INT, " + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getCrossReference(null, null, "parent", null, null, "child"); + this.rs.next(); + assertEquals("parent", this.rs.getString("PKTABLE_NAME")); + assertEquals("id", this.rs.getString("PKCOLUMN_NAME")); + assertEquals("child", this.rs.getString("FKTABLE_NAME")); + assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME")); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for foreign key. + */ + public void testGetExportedKeysUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB"); + this.stmt.executeUpdate( + "CREATE TABLE child(id INT, parent_id INT, " + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getExportedKeys(null, null, "parent"); + this.rs.next(); + assertEquals("parent", this.rs.getString("PKTABLE_NAME")); + assertEquals("id", this.rs.getString("PKCOLUMN_NAME")); + assertEquals("child", this.rs.getString("FKTABLE_NAME")); + assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME")); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * Tests the implementation of Information Schema for foreign key. + */ + public void testGetImportedKeysUsingInfoSchema() throws Exception { + if (versionMeetsMinimum(5, 0, 7)) { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB"); + this.stmt.executeUpdate( + "CREATE TABLE child(id INT, parent_id INT, " + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB"); + Properties props = new Properties(); + props.put("useInformationSchema", "true"); + Connection conn1 = null; + try { + conn1 = getConnectionWithProps(props); + DatabaseMetaData metaData = conn1.getMetaData(); + this.rs = metaData.getImportedKeys(null, null, "child"); + this.rs.next(); + assertEquals("parent", this.rs.getString("PKTABLE_NAME")); + assertEquals("id", this.rs.getString("PKCOLUMN_NAME")); + assertEquals("child", this.rs.getString("FKTABLE_NAME")); + assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME")); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS child"); + this.stmt.executeUpdate("DROP TABLE If EXISTS parent"); + if (conn1 != null) { + conn1.close(); + } + } + } + } + + /** + * WL#411 - Generated columns. + * + * Test for new syntax and support in DatabaseMetaData.getColumns(). + * + * New syntax for CREATE TABLE, introduced in MySQL 5.7.6: + * -col_name data_type [GENERATED ALWAYS] AS (expression) [VIRTUAL | STORED] [UNIQUE [KEY]] [COMMENT comment] [[NOT] NULL] [[PRIMARY] KEY] + */ + public void testGeneratedColumns() throws Exception { + if (!versionMeetsMinimum(5, 7, 6)) { + return; + } + + // Test GENERATED columns syntax. + createTable("pythagorean_triple", + "(side_a DOUBLE NULL, side_b DOUBLE NULL, " + + "side_c_vir DOUBLE AS (SQRT(side_a * side_a + side_b * side_b)) VIRTUAL UNIQUE KEY COMMENT 'hypotenuse - virtual', " + + "side_c_sto DOUBLE GENERATED ALWAYS AS (SQRT(POW(side_a, 2) + POW(side_b, 2))) STORED UNIQUE KEY COMMENT 'hypotenuse - stored' NOT NULL " + + "PRIMARY KEY)"); + + // Test data for generated columns. + assertEquals(1, this.stmt.executeUpdate("INSERT INTO pythagorean_triple (side_a, side_b) VALUES (3, 4)")); + this.rs = this.stmt.executeQuery("SELECT * FROM pythagorean_triple"); + assertTrue(this.rs.next()); + assertEquals(3d, this.rs.getDouble(1)); + assertEquals(4d, this.rs.getDouble(2)); + assertEquals(5d, this.rs.getDouble(3)); + assertEquals(5d, this.rs.getDouble(4)); + assertEquals(3d, this.rs.getDouble("side_a")); + assertEquals(4d, this.rs.getDouble("side_b")); + assertEquals(5d, this.rs.getDouble("side_c_sto")); + assertEquals(5d, this.rs.getDouble("side_c_vir")); + assertFalse(this.rs.next()); + + for (String connProps : new String[] { "useInformationSchema=false", "useInformationSchema=true" }) { + Connection testConn = null; + testConn = getConnectionWithProps(connProps); + DatabaseMetaData dbmd = testConn.getMetaData(); + + String test = "Case [" + connProps + "]"; + + // Test columns metadata. + this.rs = dbmd.getColumns(null, null, "pythagorean_triple", "%"); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_a", this.rs.getString("COLUMN_NAME")); + assertEquals(test, "YES", this.rs.getString("IS_NULLABLE")); + assertEquals(test, "NO", this.rs.getString("IS_AUTOINCREMENT")); + assertEquals(test, "NO", this.rs.getString("IS_GENERATEDCOLUMN")); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_b", this.rs.getString("COLUMN_NAME")); + assertEquals(test, "YES", this.rs.getString("IS_NULLABLE")); + assertEquals(test, "NO", this.rs.getString("IS_AUTOINCREMENT")); + assertEquals(test, "NO", this.rs.getString("IS_GENERATEDCOLUMN")); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_c_vir", this.rs.getString("COLUMN_NAME")); + assertEquals(test, "YES", this.rs.getString("IS_NULLABLE")); + assertEquals(test, "NO", this.rs.getString("IS_AUTOINCREMENT")); + assertEquals(test, "YES", this.rs.getString("IS_GENERATEDCOLUMN")); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_c_sto", this.rs.getString("COLUMN_NAME")); + assertEquals(test, "NO", this.rs.getString("IS_NULLABLE")); + assertEquals(test, "NO", this.rs.getString("IS_AUTOINCREMENT")); + assertEquals(test, "YES", this.rs.getString("IS_GENERATEDCOLUMN")); + assertFalse(test, this.rs.next()); + + // Test primary keys metadata. + this.rs = dbmd.getPrimaryKeys(null, null, "pythagorean_triple"); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_c_sto", this.rs.getString("COLUMN_NAME")); + assertEquals(test, "PRIMARY", this.rs.getString("PK_NAME")); + assertFalse(test, this.rs.next()); + + // Test indexes metadata. + this.rs = dbmd.getIndexInfo(null, null, "pythagorean_triple", false, true); + assertTrue(test, this.rs.next()); + assertEquals(test, "PRIMARY", this.rs.getString("INDEX_NAME")); + assertEquals(test, "side_c_sto", this.rs.getString("COLUMN_NAME")); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_c_sto", this.rs.getString("INDEX_NAME")); + assertEquals(test, "side_c_sto", this.rs.getString("COLUMN_NAME")); + assertTrue(test, this.rs.next()); + assertEquals(test, "side_c_vir", this.rs.getString("INDEX_NAME")); + assertEquals(test, "side_c_vir", this.rs.getString("COLUMN_NAME")); + assertFalse(test, this.rs.next()); + + testConn.close(); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/MiniAdminTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/MiniAdminTest.java new file mode 100644 index 0000000..faf5c01 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/MiniAdminTest.java @@ -0,0 +1,81 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import com.mysql.jdbc.MiniAdmin; + +import testsuite.BaseTestCase; + +/** + * Testsuite for MiniAdmin functionality. + */ +public class MiniAdminTest extends BaseTestCase { + /** + * The system property that must exist to run the shutdown test + */ + private static final String SHUTDOWN_PROP = "com.mysql.jdbc.testsuite.MiniAdminTest.runShutdown"; + + /** + * Creates a new test case + * + * @param name + * the test to run + */ + public MiniAdminTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MiniAdminTest.class); + } + + /** + * Tests whether or not you can shutdown the server with MiniAdmin. + * + * Only runs if SHUTDOWN_PROP is defined. + * + * @throws Exception + * if an error occurs + */ + public void testShutdown() throws Exception { + if (runTestIfSysPropDefined(SHUTDOWN_PROP)) { + new MiniAdmin(this.conn).shutdown(); + } + } + + /** + * Tests whether or not you can construct a MiniAdmin with a JDBC URL. + * + * @throws Exception + * if an error occurs + */ + public void testUrlConstructor() throws Exception { + new MiniAdmin(dbUrl); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/MultiHostConnectionTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/MultiHostConnectionTest.java new file mode 100644 index 0000000..ad56c70 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/MultiHostConnectionTest.java @@ -0,0 +1,1272 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.NonRegisteringDriver; + +import testsuite.BaseTestCase; +import testsuite.UnreliableSocketFactory; + +public class MultiHostConnectionTest extends BaseTestCase { + private static final String HOST_1 = "host1"; + private static final String HOST_2 = "host2"; + private static final String HOST_3 = "host3"; + + private static final String HOST_1_OK = UnreliableSocketFactory.getHostConnectedStatus(HOST_1); + private static final String HOST_1_FAIL = UnreliableSocketFactory.getHostFailedStatus(HOST_1); + private static final String HOST_2_OK = UnreliableSocketFactory.getHostConnectedStatus(HOST_2); + private static final String HOST_2_FAIL = UnreliableSocketFactory.getHostFailedStatus(HOST_2); + private static final String HOST_3_OK = UnreliableSocketFactory.getHostConnectedStatus(HOST_3); + private static final String HOST_3_FAIL = UnreliableSocketFactory.getHostFailedStatus(HOST_3); + + private static final String STMT_CLOSED_ERR_PATTERN = "No operations allowed after statement closed."; + private static final String COMM_LINK_ERR_PATTERN = "(?s)Communications link failure.*"; + + /** + * Creates a new MultiHostConnectionTest. + * + * @param name + * the name of the test + */ + public MultiHostConnectionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(MultiHostConnectionTest.class); + } + + /** + * Asserts the execution and return for a simple single value query. + * + * @param testStmt + * The statement instance that runs the query. + * @param query + * The query. + * @param result + * The expected result. + */ + private static void assertSingleValueQuery(Statement testStmt, String query, Object result) throws Exception { + ResultSet testRs = testStmt.executeQuery(query); + assertTrue(testRs.next()); + assertEquals(result, testRs.getObject(1)); + assertFalse(testRs.next()); + testRs.close(); + } + + /** + * Asserts the SQLException thrown for connection commit() or rollback(); + * + * @param testConn + * The connection instance where to issue the command. + * @param command + * The command to issue. + * @param messageRegEx + * The expected message regular expression pattern. + */ + private static void assertSQLException(final Connection testConn, final String command, String messageRegEx) { + assertThrows(SQLException.class, messageRegEx, new Callable() { + public Void call() throws Exception { + if ("commit".equals(command)) { + testConn.commit(); + } else if ("rollback".equals(command)) { + testConn.rollback(); + } + return null; + } + }); + } + + /** + * Asserts the SQLException thrown for a query execution. + * + * @param testStmt + * The statement instance that runs the query. + * @param query + * The query. + * @param messageRegEx + * The expected message regular expression pattern. + */ + private static void assertSQLException(final Statement testStmt, final String query, String messageRegEx) { + assertThrows(SQLException.class, messageRegEx, new Callable() { + public Void call() throws Exception { + testStmt.execute(query); + return null; + } + }); + } + + /** + * Tests failover connection establishing with multiple up/down combinations of 3 hosts. + */ + public void testFailoverConnection() throws Exception { + Properties props = getPropertiesFromTestsuiteUrl(); + + String host = props.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY); + if (!NonRegisteringDriver.isHostPropertiesList(host)) { + String port = props.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306"); + host = host + ":" + port; + } + String noHost = "testfoconn-nohost:12345"; + + StringBuilder testURL = new StringBuilder("jdbc:mysql://"); + testURL.append(noHost).append(","); + testURL.append(noHost).append(","); + testURL.append(noHost).append("/"); + final String allDownURL = testURL.toString(); + + final Properties testConnProps = getHostFreePropertiesFromTestsuiteUrl(); + testConnProps.setProperty("retriesAllDown", "2"); + + // all hosts down + assertThrows(SQLException.class, COMM_LINK_ERR_PATTERN, new Callable() { + @SuppressWarnings("synthetic-access") + public Void call() throws Exception { + getConnectionWithProps(allDownURL, testConnProps); + return null; + } + }); + + // at least one host up + for (int i = 1; i < 8; i++) { + testURL = new StringBuilder("jdbc:mysql://"); + testURL.append((i & 1) == 0 ? noHost : host).append(","); + testURL.append((i & 2) == 0 ? noHost : host).append(","); + testURL.append((i & 4) == 0 ? noHost : host).append("/"); + + Connection testConn = getConnectionWithProps(testURL.toString(), testConnProps); + + final Statement testStmt = testConn.createStatement(); + + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + assertSQLException(testStmt, "SELECT * FROM missing_table", "Table '\\w*.missing_table' doesn't exist"); + assertSingleValueQuery(testStmt, "SELECT 2", 2L); + + testStmt.close(); + testConn.close(); + } + } + + /** + * Tests failover transitions in a default failover connection using three hosts. + */ + public void testFailoverTransitions() throws Exception { + Set downedHosts = new HashSet(); + + // from HOST_1 to HOST_2 + testFailoverTransition(HOST_1, HOST_2, null, null, HOST_1_OK, HOST_2_OK); + + // from HOST_1 to HOST_3 + downedHosts.clear(); + downedHosts.add(HOST_2); + testFailoverTransition(HOST_1, HOST_3, downedHosts, null, HOST_1_OK, HOST_2_FAIL, HOST_3_OK); + + // from HOST_2 to HOST_3 + downedHosts.clear(); + downedHosts.add(HOST_1); + testFailoverTransition(HOST_2, HOST_3, downedHosts, null, HOST_1_FAIL, HOST_2_OK, HOST_3_OK); + + // from HOST_2 to HOST_1 + downedHosts.clear(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_3); + testFailoverTransition(HOST_2, HOST_1, downedHosts, HOST_1, HOST_1_FAIL, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK); + + // from HOST_3 to HOST_1 + downedHosts.clear(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_2); + testFailoverTransition(HOST_3, HOST_1, downedHosts, HOST_1, HOST_1_FAIL, HOST_2_FAIL, HOST_3_OK, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK); + + // from HOST_3 to HOST_2 + downedHosts.clear(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_2); + testFailoverTransition(HOST_3, HOST_2, downedHosts, HOST_2, HOST_1_FAIL, HOST_2_FAIL, HOST_3_OK, HOST_2_OK); + } + + /** + * Tests a failover transition. + * + * @param fromHost + * The host where initially connected to. In order to connect to an host other than the primary all previous hosts must be downed (pinpoint them + * in the 'downedHosts' set). + * @param toHost + * The host where to failover. In order to correctly connect to this host, all hosts between (and eventually before) 'fromHost' and 'toHost' must + * be downed. + * @param downedHosts + * The set of hosts initially down. + * @param recoverHost + * The host that recovers after first connection. + * @param expectedConnectionsHistory + * The expected connection attempts sequence. + */ + private void testFailoverTransition(String fromHost, String toHost, Set downedHosts, String recoverHost, String... expectedConnectionsHistory) + throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + String fromHostOk = UnreliableSocketFactory.STATUS_CONNECTED + fromHost; + String toHostOk = UnreliableSocketFactory.STATUS_CONNECTED + toHost; + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, downedHosts); + Statement testStmt = null; + + try { + if (recoverHost != null) { + // 'recoverHost' up + UnreliableSocketFactory.dontDownHost(recoverHost); + } + + // connected to 'fromHost' + assertEquals(fromHostOk, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // 'fromHost' down + UnreliableSocketFactory.downHost(fromHost); + + // still connected to 'fromHost' + assertEquals(fromHostOk, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to 'toHost' on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(toHostOk, UnreliableSocketFactory.getHostFromLastConnection()); + + // statement closed + assertSQLException(testStmt, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // still connected to 'toHost' + assertEquals(toHostOk, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(expectedConnectionsHistory); + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + + /** + * Tests a default failover connection using three hosts and the following sequence of events: + * - [/HOST_1 : /HOST_2 : /HOST_3] --> HOST_1 + * - [\HOST_1 : /HOST_2 : /HOST_3] --> HOST_2 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [/HOST_1 : /HOST_2 : /HOST_3] + * - [/HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : \HOST_2 : \HOST_3] --> HOST_1 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverDefaultSettings() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props); + Statement testStmt1 = null, testStmt2 = null; + + try { + // connected to HOST_1 [/HOST_1 : /HOST_2 : /HOST_3] + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_1 down [\HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_2 on connection error + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // HOST_1 + HOST_2 up [/HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_2); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_3 down [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_2 on connection error (not time to come back to HOST_1 yet) + assertSQLException(testStmt2, "SELECT 2", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 down [/HOST_1 : \HOST_2 : \HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + assertConnectionsHistory(HOST_1_OK, HOST_2_OK, HOST_3_OK, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK); + + } finally { + if (testStmt1 != null) { + testStmt1.close(); + } + if (testStmt2 != null) { + testStmt2.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + + /** + * Repeatedly tests a failover connection using three hosts and the following sequence of events, combining distinct failover event triggering: + * - [/HOST_1 : /HOST_2 : /HOST_3] --> HOST_1 + * - [\HOST_1 : /HOST_2 : /HOST_3] --> HOST_2 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [/HOST_1 : /HOST_2 : /HOST_3] + * - [/HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : \HOST_2 : \HOST_3] --> HOST_1 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverCombinations() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + for (int run = 1; run <= 3; run++) { + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props); + Statement testStmt1 = null, testStmt2 = null; + + testConn.setAutoCommit(false); + + try { + // connected to HOST_1 [/HOST_1 : /HOST_2 : /HOST_3] + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_1 down [\HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_2 on connection error + if (run == 1) { + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + } else if (run == 2) { + assertSQLException(testConn, "commit", COMM_LINK_ERR_PATTERN); + } else { + assertSQLException(testConn, "rollback", COMM_LINK_ERR_PATTERN); + } + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + if (run == 1) { + assertSQLException(testConn, "commit", COMM_LINK_ERR_PATTERN); + } else if (run == 2) { + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + } else { + assertSQLException(testConn, "rollback", COMM_LINK_ERR_PATTERN); + } + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // HOST_1 + HOST_2 up [/HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_2); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_3 down [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_2 on connection error (not time to come back to HOST_1 yet) + if (run == 1) { + assertSQLException(testConn, "rollback", COMM_LINK_ERR_PATTERN); + } else if (run == 2) { + assertSQLException(testConn, "commit", COMM_LINK_ERR_PATTERN); + } else { + assertSQLException(testStmt2, "SELECT 2", COMM_LINK_ERR_PATTERN); + } + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 down [/HOST_1 : \HOST_2 : \HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + if (run == 1) { + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + } else if (run == 2) { + assertSQLException(testConn, "rollback", COMM_LINK_ERR_PATTERN); + } else { + assertSQLException(testConn, "commit", COMM_LINK_ERR_PATTERN); + } + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + assertConnectionsHistory(HOST_1_OK, HOST_2_OK, HOST_3_OK, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK); + + } finally { + if (testStmt1 != null) { + testStmt1.close(); + } + if (testStmt2 != null) { + testStmt2.close(); + } + testConn.close(); + } + } + } + + /** + * Tests the property 'failOverReadOnly' in a failover connection using three hosts and the following sequence of events: + * - [\HOST_1 : /HOST_2 : /HOST_3] --> HOST_2 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [\HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : \HOST_2 : \HOST_3] --> HOST_1 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverReadOnly() throws Exception { + Set downedHosts = new HashSet(); + downedHosts.add(HOST_1); + + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + for (boolean foReadOnly : new boolean[] { true, false }) { + props.setProperty("failOverReadOnly", Boolean.toString(foReadOnly)); + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, downedHosts); + Statement testStmt = null; + + try { + // connected ('failOverReadOnly') to HOST_2 [\HOST_1 : /HOST_2 : /HOST_3] + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + assertEquals(foReadOnly, testConn.isReadOnly()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_2 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects ('failOverReadOnly') to HOST_3 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + assertEquals(foReadOnly, testConn.isReadOnly()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_2 up & HOST_3 down [\HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_2); + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects ('failOverReadOnly') to HOST_2 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + assertEquals(foReadOnly, testConn.isReadOnly()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 up & HOST_2 down [/HOST_1 : \HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects (r+w) to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + assertFalse(testConn.isReadOnly()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 down & HOST_3 up [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_3); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects ('failOverReadOnly') to HOST_2 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + assertEquals(foReadOnly, testConn.isReadOnly()); + + assertConnectionsHistory(HOST_2_OK, HOST_3_OK, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK, HOST_2_FAIL, HOST_3_OK); + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests the property 'queriesBeforeRetryMaster' in a failover connection using three hosts and the following sequence of events: + * - [/HOST_1 : /HOST_2 : /HOST_3] --> HOST_1 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [/HOST_1 : /HOST_2 : \HOST_3] --> HOST_1 vs HOST_2 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverQueriesBeforeRetryMaster() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + for (boolean setQueriesBeforeRetryMaster : new boolean[] { true, false }) { + if (setQueriesBeforeRetryMaster) { + props.setProperty("queriesBeforeRetryMaster", "10"); + } else { + props.remove("queriesBeforeRetryMaster"); // default 50 + } + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props); + Statement testStmt = null; + + try { + testConn.setAutoCommit(false); // prevent automatic fall back + + // connected to HOST_1 [/HOST_1 : /HOST_2 : /HOST_3] + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 + HOST_2 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + for (int i = 0; i < 10; i++) { + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } + + // HOST_1 + HOST_2 up & HOST_3 down [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_2); + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + if (setQueriesBeforeRetryMaster) { + // connects to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(HOST_1_OK, HOST_2_FAIL, HOST_3_OK, HOST_1_OK); + + } else { + // connects to HOST_2 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(HOST_1_OK, HOST_2_FAIL, HOST_3_OK, HOST_2_OK); + } + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests the property 'secondsBeforeRetryMaster' in a failover connection using three hosts and the following sequence of events: + * - [/HOST_1 : /HOST_2 : /HOST_3] --> HOST_1 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [/HOST_1 : /HOST_2 : \HOST_3] --> HOST_1 vs HOST_2 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverSecondsBeforeRetryMaster() throws Exception { + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + for (boolean setSecondsBeforeRetryMaster : new boolean[] { true, false }) { + if (setSecondsBeforeRetryMaster) { + props.setProperty("secondsBeforeRetryMaster", "1"); + } else { + props.remove("secondsBeforeRetryMaster"); // default 50 + } + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props); + Statement testStmt = null; + + try { + testConn.setAutoCommit(false); // prevent automatic fall back + + // connected to HOST_1 [/HOST_1 : /HOST_2 : /HOST_3] + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 + HOST_2 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + long startTime = System.currentTimeMillis(); + do { + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + try { + Thread.sleep(200); + } catch (InterruptedException e) { + } + } while (System.currentTimeMillis() - startTime < 2000); + + // HOST_1 + HOST_2 up & HOST_3 down [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_2); + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + if (setSecondsBeforeRetryMaster) { + // connects to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(HOST_1_OK, HOST_2_FAIL, HOST_3_OK, HOST_1_OK); + + } else { + // connects to HOST_2 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(HOST_1_OK, HOST_2_FAIL, HOST_3_OK, HOST_2_OK); + } + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests the automatic fall back to primary host in a failover connection using three hosts and the following sequence of events: + * + 1.st part: + * - [\HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : /HOST_2 : /HOST_3] --> no_change vs HOST_1 (auto fall back) + * - [/HOST_1 : \HOST_2 : /HOST_3] --> HOST_1 vs no_change + * + 2.nd part: + * - [\HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : /HOST_2 : /HOST_3] --> no_change + * - [/HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [/HOST_1 : /HOST_2 : \HOST_3] --> HOST_1 + * - /HOST_2 & \HOST_3 + * + * The automatic fall back only happens at transaction boundaries and at least 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' is greater than 0. + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverAutoFallBack() throws Exception { + Set downedHosts = new HashSet(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_3); + + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + + // test fall back on ('queriesBeforeRetryMaster' > 0 || 'secondsBeforeRetryMaster' > 0) + props.setProperty("queriesBeforeRetryMaster", "10"); + props.setProperty("secondsBeforeRetryMaster", "1"); + + for (boolean autoCommit : new boolean[] { true, false }) { + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, downedHosts); + Statement testStmt = null; + + try { + testConn.setAutoCommit(autoCommit); + + // connected to HOST_2 [\HOST_1 : /HOST_2 : \HOST_3] + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 + HOST_3 up [/HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_3); + + // continue with same statement + long startTime = System.currentTimeMillis(); + boolean hostSwitched = false; + do { + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + if (autoCommit) { + if (!hostSwitched && UnreliableSocketFactory.getHostFromLastConnection().equals(HOST_1_OK)) { + hostSwitched = true; + } + if (hostSwitched) { + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } else { + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } + + } else { + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } while (System.currentTimeMillis() - startTime < 2000); + + // HOST_2 down [/HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + if (autoCommit) { + // already switched to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + } else { + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } + + assertConnectionsHistory(HOST_2_OK, HOST_1_OK); + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + + // test fall back off ('queriesBeforeRetryMaster' = 0 && 'secondsBeforeRetryMaster' = 0) + props.setProperty("queriesBeforeRetryMaster", "0"); + props.setProperty("secondsBeforeRetryMaster", "0"); + + for (boolean autoCommit : new boolean[] { true, false }) { + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, downedHosts); + Statement testStmt = null; + + try { + testConn.setAutoCommit(autoCommit); + + // connected to HOST_2 [\HOST_1 : /HOST_2 : \HOST_3] + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 + HOST_3 up [/HOST_1 : /HOST_2 : /HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_3); + + // continue with same statement + for (int i = 0; i < 55; i++) { // default queriesBeforeRetryMaster == 50 + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + } + + // HOST_2 down [/HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_2 up & HOST_3 down [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_2); + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + assertConnectionsHistory(HOST_2_OK, HOST_3_OK, HOST_1_OK); + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests the property 'autoReconnect' in a failover connection using three hosts and the following sequence of events: + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * - [\HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : /HOST_2 : \HOST_3] + * - [/HOST_1 : \HOST_2 : \HOST_3] --> HOST_1 + * - [/HOST_1 : \HOST_2 : /HOST_3] + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverAutoReconnect() throws Exception { + Set downedHosts = new HashSet(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_2); + + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + props.setProperty("maxReconnects", "2"); + props.setProperty("initialTimeout", "1"); + + for (boolean foAutoReconnect : new boolean[] { true, false }) { + props.setProperty("autoReconnect", Boolean.toString(foAutoReconnect)); + + Connection testConn = getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, downedHosts); + Statement testStmt1 = null, testStmt2 = null; + + try { + // connected to HOST_3 [\HOST_1 : \HOST_2 : /HOST_3] + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 up & HOST_3 down [\HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_2); + UnreliableSocketFactory.downHost(HOST_3); + + // still connected to HOST_3 + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_2 on connection error + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // HOST_1 up [/HOST_1 : /HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + + if (!foAutoReconnect) { + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + } // else statements reactivated + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_2 down [/HOST_1 : \HOST_2 : \HOST_3] + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + assertSQLException(testStmt2, "SELECT 2", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // HOST_3 up [/HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_3); + + if (!foAutoReconnect) { + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + }// else statements reactivated + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + // HOST_1 down [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error + assertSQLException(testStmt1, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + if (!foAutoReconnect) { + // statements closed + assertSQLException(testStmt1, "SELECT 1", STMT_CLOSED_ERR_PATTERN); + assertSQLException(testStmt2, "SELECT 2", STMT_CLOSED_ERR_PATTERN); + + // get new statements + testStmt1 = testConn.createStatement(); + testStmt2 = testConn.createStatement(); + } + assertSingleValueQuery(testStmt1, "SELECT 1", 1L); + assertSingleValueQuery(testStmt2, "SELECT 2", 2L); + + if (foAutoReconnect) { + // with 'autoReconnect=true' each fail counts twice ('maxReconnects=2') + assertConnectionsHistory(HOST_1_FAIL, HOST_1_FAIL, HOST_2_FAIL, HOST_2_FAIL, HOST_3_OK, HOST_2_OK, HOST_3_FAIL, HOST_3_FAIL, HOST_2_FAIL, + HOST_2_FAIL, HOST_3_FAIL, HOST_3_FAIL, HOST_1_OK, HOST_2_FAIL, HOST_2_FAIL, HOST_3_OK); + } else { + assertConnectionsHistory(HOST_1_FAIL, HOST_2_FAIL, HOST_3_OK, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK, HOST_2_FAIL, + HOST_3_OK); + } + + } finally { + if (testStmt1 != null) { + testStmt1.close(); + } + if (testStmt2 != null) { + testStmt2.close(); + } + if (testConn != null) { + testConn.close(); + } + } + } + } + + /** + * Tests connection properties synchronization in a failover connection using three hosts and the following sequence of events: + * - [\HOST_1 : /HOST_2 : \HOST_3] --> HOST_2 + * - [/HOST_1 : \HOST_2 : \HOST_3] --> HOST_1 + * - [\HOST_1 : \HOST_2 : /HOST_3] --> HOST_3 + * + * [Legend: "/HOST_n" --> HOST_n up; "\HOST_n" --> HOST_n down] + */ + public void testFailoverConnectionSynchronization() throws Exception { + Set downedHosts = new HashSet(); + downedHosts.add(HOST_1); + downedHosts.add(HOST_3); + + Properties props = new Properties(); + props.setProperty("retriesAllDown", "2"); + props.setProperty("failOverReadOnly", "false"); + + com.mysql.jdbc.Connection testConn = (com.mysql.jdbc.Connection) getUnreliableFailoverConnection(new String[] { HOST_1, HOST_2, HOST_3 }, props, + downedHosts); + Statement testStmt = null; + + int newTransactionIsolation = testConn.getTransactionIsolation(); + String newCatalog = "fotests"; + createDatabase(newCatalog); + + try { + // connected to HOST_2 [\HOST_1 : /HOST_2 : \HOST_3] + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // assert expected default session values + assertTrue(testConn.getAutoCommit()); + assertEquals(this.conn.getCatalog(), testConn.getCatalog()); + assertEquals(newTransactionIsolation, testConn.getTransactionIsolation()); + assertFalse(testConn.isReadOnly()); + assertEquals(-1, testConn.getSessionMaxRows()); + + // change session values + testConn.setAutoCommit(false); + testConn.setCatalog(newCatalog); + newTransactionIsolation = newTransactionIsolation * 2 == 16 ? 1 : newTransactionIsolation * 2; + testConn.setTransactionIsolation(newTransactionIsolation); + testConn.setReadOnly(true); + testConn.setSessionMaxRows(1); + + // assert expected session values after explicit change + assertFalse(testConn.getAutoCommit()); + assertEquals(newCatalog, testConn.getCatalog()); + assertEquals(newTransactionIsolation, testConn.getTransactionIsolation()); + assertTrue(testConn.isReadOnly()); + assertEquals(1, testConn.getSessionMaxRows()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // HOST_1 up & HOST_2 down [/HOST_1 : \HOST_2 : \HOST_3] + UnreliableSocketFactory.dontDownHost(HOST_1); + UnreliableSocketFactory.downHost(HOST_2); + + // still connected to HOST_2 + assertEquals(HOST_2_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_1 on connection error + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // assert expected session values after connection synchronization + assertFalse(testConn.getAutoCommit()); + assertEquals(newCatalog, testConn.getCatalog()); + assertEquals(newTransactionIsolation, testConn.getTransactionIsolation()); + assertTrue(testConn.isReadOnly()); + assertEquals(-1, testConn.getSessionMaxRows()); // this value is reset to default 'maxRows' when the new "internal" connection is created + + // change session values + testConn.setAutoCommit(true); + newTransactionIsolation = newTransactionIsolation * 2 == 16 ? 1 : newTransactionIsolation * 2; + testConn.setTransactionIsolation(newTransactionIsolation); + testConn.setReadOnly(false); + testConn.setSessionMaxRows(2); + + // assert expected session values after explicit change + assertTrue(testConn.getAutoCommit()); + assertEquals(newTransactionIsolation, testConn.getTransactionIsolation()); + assertFalse(testConn.isReadOnly()); + assertEquals(2, testConn.getSessionMaxRows()); + + // HOST_1 down & HOST_3 up [\HOST_1 : \HOST_2 : /HOST_3] + UnreliableSocketFactory.downHost(HOST_1); + UnreliableSocketFactory.dontDownHost(HOST_3); + + // still connected to HOST_1 + assertEquals(HOST_1_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // connects to HOST_3 on connection error (not time to come back to HOST_1 yet) + assertSQLException(testStmt, "SELECT 1", COMM_LINK_ERR_PATTERN); + assertEquals(HOST_3_OK, UnreliableSocketFactory.getHostFromLastConnection()); + + // get new statement + testStmt = testConn.createStatement(); + assertSingleValueQuery(testStmt, "SELECT 1", 1L); + + // assert expected session values after connection synchronization + assertTrue(testConn.getAutoCommit()); + assertEquals(newCatalog, testConn.getCatalog()); + assertEquals(newTransactionIsolation, testConn.getTransactionIsolation()); + assertFalse(testConn.isReadOnly()); + assertEquals(-1, testConn.getSessionMaxRows()); // this value is reset to default 'maxRows' when the new "internal" connection is created + + assertConnectionsHistory(HOST_1_FAIL, HOST_2_OK, HOST_3_FAIL, HOST_2_FAIL, HOST_3_FAIL, HOST_1_OK, HOST_2_FAIL, HOST_3_OK); + + } finally { + if (testStmt != null) { + testStmt.close(); + } + if (testConn != null) { + // testConn.close(); + } + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/NumbersTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/NumbersTest.java new file mode 100644 index 0000000..673dc61 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/NumbersTest.java @@ -0,0 +1,75 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.SQLException; + +import testsuite.BaseTestCase; + +public class NumbersTest extends BaseTestCase { + private static final long TEST_BIGINT_VALUE = 6147483647L; + + /** + * Creates a new NumbersTest object. + * + * @param name + */ + public NumbersTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(NumbersTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + createTestTable(); + } + + public void testNumbers() throws SQLException { + this.rs = this.stmt.executeQuery("SELECT * from number_test"); + + while (this.rs.next()) { + long minBigInt = this.rs.getLong(1); + long maxBigInt = this.rs.getLong(2); + long testBigInt = this.rs.getLong(3); + assertTrue("Minimum bigint not stored correctly", (minBigInt == Long.MIN_VALUE)); + assertTrue("Maximum bigint not stored correctly", (maxBigInt == Long.MAX_VALUE)); + assertTrue("Test bigint not stored correctly", (TEST_BIGINT_VALUE == testBigInt)); + } + } + + private void createTestTable() throws SQLException { + createTable("number_test", "(minBigInt bigint, maxBigInt bigint, testBigInt bigint)"); + this.stmt.executeUpdate( + "INSERT INTO number_test (minBigInt,maxBigInt,testBigInt) values (" + Long.MIN_VALUE + "," + Long.MAX_VALUE + "," + TEST_BIGINT_VALUE + ")"); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/ReadOnlyCallableStatementTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/ReadOnlyCallableStatementTest.java new file mode 100644 index 0000000..4d6f53b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/ReadOnlyCallableStatementTest.java @@ -0,0 +1,122 @@ +/* + Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +import testsuite.BaseTestCase; + +public class ReadOnlyCallableStatementTest extends BaseTestCase { + public ReadOnlyCallableStatementTest(String name) { + super(name); + } + + public void testReadOnlyWithProcBodyAccess() throws Exception { + if (versionMeetsMinimum(5, 0)) { + Connection replConn = null; + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("autoReconnect", "true"); + + try { + createProcedure("testProc1", "()\nREADS SQL DATA\nbegin\nSELECT NOW();\nend\n"); + + createProcedure("`testProc.1`", "()\nREADS SQL DATA\nbegin\nSELECT NOW();\nend\n"); + + replConn = getMasterSlaveReplicationConnection(); + replConn.setReadOnly(true); + + CallableStatement cstmt = replConn.prepareCall("CALL testProc1()"); + cstmt.execute(); + cstmt.execute(); + + cstmt = replConn.prepareCall("CALL `" + replConn.getCatalog() + "`.testProc1()"); + cstmt.execute(); + + cstmt = replConn.prepareCall("CALL `" + replConn.getCatalog() + "`.`testProc.1`()"); + cstmt.execute(); + + } finally { + + if (replConn != null) { + replConn.close(); + } + } + } + } + + public void testNotReadOnlyWithProcBodyAccess() throws Exception { + if (versionMeetsMinimum(5, 0)) { + + Connection replConn = null; + Properties props = getHostFreePropertiesFromTestsuiteUrl(); + props.setProperty("autoReconnect", "true"); + + try { + createProcedure("testProc2", "()\nMODIFIES SQL DATA\nbegin\nSELECT NOW();\nend\n"); + + createProcedure("`testProc.2`", "()\nMODIFIES SQL DATA\nbegin\nSELECT NOW();\nend\n"); + + replConn = getMasterSlaveReplicationConnection(); + replConn.setReadOnly(true); + + CallableStatement cstmt = replConn.prepareCall("CALL testProc2()"); + + try { + cstmt.execute(); + fail("Should not execute because procedure modifies data."); + } catch (SQLException e) { + assertEquals("Should error for read-only connection.", e.getSQLState(), "S1009"); + } + + cstmt = replConn.prepareCall("CALL `" + replConn.getCatalog() + "`.testProc2()"); + + try { + cstmt.execute(); + fail("Should not execute because procedure modifies data."); + } catch (SQLException e) { + assertEquals("Should error for read-only connection.", e.getSQLState(), "S1009"); + } + + cstmt = replConn.prepareCall("CALL `" + replConn.getCatalog() + "`.`testProc.2`()"); + + try { + cstmt.execute(); + fail("Should not execute because procedure modifies data."); + } catch (SQLException e) { + assertEquals("Should error for read-only connection.", e.getSQLState(), "S1009"); + } + + } finally { + + if (replConn != null) { + replConn.close(); + } + } + } + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/ResultSetTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/ResultSetTest.java new file mode 100644 index 0000000..6c73ca4 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/ResultSetTest.java @@ -0,0 +1,251 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.UnsupportedEncodingException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.ConnectionImpl; + +import testsuite.BaseTestCase; + +public class ResultSetTest extends BaseTestCase { + + public ResultSetTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ResultSetTest.class); + } + + public void testPadding() throws Exception { + if (!versionMeetsMinimum(4, 1, 0)) { + return; + } + + Connection paddedConn = null; + + int numChars = 32; + + // build map of charsets supported by server + Connection c = getConnectionWithProps("detectCustomCollations=true"); + Map charsetsMap = new HashMap(); + Iterator collationIndexes = ((ConnectionImpl) c).indexToMysqlCharset.keySet().iterator(); + while (collationIndexes.hasNext()) { + Integer index = collationIndexes.next(); + String charsetName = null; + if (((ConnectionImpl) c).indexToCustomMysqlCharset != null) { + charsetName = ((ConnectionImpl) c).indexToCustomMysqlCharset.get(index); + } + if (charsetName == null) { + charsetName = CharsetMapping.getMysqlCharsetNameForCollationIndex(index); + } + if (charsetName != null) { + charsetsMap.put(charsetName, index); + } + } + c.close(); + + Iterator charsetNames = charsetsMap.keySet().iterator(); + StringBuilder columns = new StringBuilder(); + StringBuilder emptyBuf = new StringBuilder(); + StringBuilder abcBuf = new StringBuilder(); + StringBuilder repeatBuf = new StringBuilder(); + StringBuilder selectBuf = new StringBuilder(); + + int counter = 0; + + while (charsetNames.hasNext()) { + String charsetName = charsetNames.next(); + + if (charsetName.equalsIgnoreCase("LATIN7") || charsetName.equalsIgnoreCase("BINARY")) { + continue; // no mapping in Java + } + + try { + "".getBytes(charsetName); + } catch (UnsupportedEncodingException uee) { + continue; // not supported on this platform + } + + if (counter != 0) { + columns.append(","); + emptyBuf.append(","); + abcBuf.append(","); + repeatBuf.append(","); + selectBuf.append(","); + } + + emptyBuf.append("''"); + abcBuf.append("'abc'"); + repeatBuf.append("REPEAT('b', " + numChars + ")"); + + columns.append("field_"); + columns.append(charsetName); + + columns.append(" CHAR("); + columns.append(numChars); + columns.append(") CHARACTER SET "); + columns.append(charsetName); + + selectBuf.append("field_"); + selectBuf.append(charsetName); + + counter++; + } + + createTable("testPadding", "(" + columns.toString() + ", ord INT)"); + + this.stmt.executeUpdate( + "INSERT INTO testPadding VALUES (" + emptyBuf.toString() + ", 1), (" + abcBuf.toString() + ", 2), (" + repeatBuf.toString() + ", 3)"); + + try { + Properties props = new Properties(); + props.setProperty("padCharsWithSpace", "true"); + + paddedConn = getConnectionWithProps(props); + + testPaddingForConnection(paddedConn, numChars, selectBuf); + + props.setProperty("useDynamicCharsetInfo", "true"); + + paddedConn = getConnectionWithProps(props); + + testPaddingForConnection(paddedConn, numChars, selectBuf); + } finally { + if (paddedConn != null) { + paddedConn.close(); + } + } + } + + private void testPaddingForConnection(Connection paddedConn, int numChars, StringBuilder selectBuf) throws SQLException { + + String query = "SELECT " + selectBuf.toString() + " FROM testPadding ORDER by ord"; + + this.rs = paddedConn.createStatement().executeQuery(query); + int numCols = this.rs.getMetaData().getColumnCount(); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + + this.rs = ((com.mysql.jdbc.Connection) paddedConn).clientPrepareStatement(query).executeQuery(); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + + if (versionMeetsMinimum(4, 1)) { + this.rs = ((com.mysql.jdbc.Connection) paddedConn).serverPrepareStatement(query).executeQuery(); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + } + + this.rs = this.stmt.executeQuery(query); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + if (this.rs.getRow() != 3) { + assertTrue( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars != this.rs.getString(i + 1).length()); + } else { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + } + + this.rs = ((com.mysql.jdbc.Connection) this.conn).clientPrepareStatement(query).executeQuery(); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + if (this.rs.getRow() != 3) { + assertTrue( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars != this.rs.getString(i + 1).length()); + } else { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + } + + if (versionMeetsMinimum(4, 1)) { + this.rs = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement(query).executeQuery(); + + while (this.rs.next()) { + for (int i = 0; i < numCols; i++) { + if (this.rs.getRow() != 3) { + assertTrue( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars != this.rs.getString(i + 1).length()); + } else { + assertEquals( + "For column '" + this.rs.getMetaData().getColumnName(i + 1) + "' of collation " + + ((com.mysql.jdbc.ResultSetMetaData) this.rs.getMetaData()).getColumnCharacterSet(i + 1), + numChars, this.rs.getString(i + 1).length()); + } + } + } + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/SSLTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/SSLTest.java new file mode 100644 index 0000000..8709a76 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/SSLTest.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import testsuite.BaseTestCase; + +/** + * Tests SSL functionality in the driver. + */ +public class SSLTest extends BaseTestCase { + /** + * Constructor for SSLTest. + * + * @param name + * the name of the test to run. + */ + public SSLTest(String name) { + super(name); + + System.setProperty("javax.net.debug", "all"); + + StringBuilder sslUrl = new StringBuilder(dbUrl); + + if (dbUrl.indexOf("?") == -1) { + sslUrl.append("?"); + } else { + sslUrl.append("&"); + } + + sslUrl.append("useSSL=true"); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(SSLTest.class); + } + + /** + * Tests SSL Connection + * + * @throws Exception + * if an error occurs + */ + public void testConnect() throws Exception { + System.out.println("<<<<<<<<<<< Look for SSL debug output >>>>>>>>>>>"); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/ServerControllerTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/ServerControllerTest.java new file mode 100644 index 0000000..70f2ad5 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/ServerControllerTest.java @@ -0,0 +1,80 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import com.mysql.jdbc.util.ServerController; + +import testsuite.BaseTestCase; + +/** + * Demonstrates usage of the ServerController class. + */ +public class ServerControllerTest extends BaseTestCase { + + private String baseDir; + + /** + * Creates a ServerControllerTest testcase. + * + * @param name + * the name of the test to run. + */ + public ServerControllerTest(String name) { + super(name); + + this.baseDir = System.getProperty("com.mysql.jdbc.test.ServerController.basedir"); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(ServerControllerTest.class); + } + + /** + * Demonstrates usage of the ServerController class. + * + * This test is only run if the property + * 'com.mysql.jdbc.test.ServerController.basedir' is set. + * + * @throws Exception + * if an error occurs. + */ + public void testServerController() throws Exception { + + if (this.baseDir != null) { + System.out.println("Starting server @ " + this.baseDir); + + ServerController controller = new ServerController(this.baseDir); + System.out.println(controller.start()); + System.out.println("Hit enter to stop server...."); + System.in.read(); + controller.stop(true); + + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/SimpleTransformer.java b/mysql-connector-java-5.1.40/src/testsuite/simple/SimpleTransformer.java new file mode 100644 index 0000000..3b552b1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/SimpleTransformer.java @@ -0,0 +1,48 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.jdbc.ConnectionPropertiesTransform; +import com.mysql.jdbc.NonRegisteringDriver; + +/** + * Used for testing the ConnectionPropertiesTransform functionality. + */ +public class SimpleTransformer implements ConnectionPropertiesTransform { + + /* + * (non-Javadoc) + * + * @see com.mysql.jdbc.ConnectionPropertiesTransform#transformProperties(java.util.Properties) + */ + public Properties transformProperties(Properties props) throws SQLException { + props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, "albequerque"); + + return props; + } + +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/SplitDBdotNameTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/SplitDBdotNameTest.java new file mode 100644 index 0000000..5a2e746 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/SplitDBdotNameTest.java @@ -0,0 +1,114 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.util.ArrayList; +import java.util.List; + +import com.mysql.jdbc.StringUtils; + +import testsuite.BaseTestCase; + +/** + * Tests new StringUtils functions in the driver: public static String sanitizeProcOrFuncName(String src) and public static List splitDBdotName(String src, + * String cat, String quotId, boolean isNoBslashEscSet) + * + * By the time sanitizeProcOrFuncName is called we should only have DB.SP as src, ie. SP/FUNC name is already sanitized during the process! + */ +public class SplitDBdotNameTest extends BaseTestCase { + /** + * Constructor for SplitDBdotNameTest. + * + * @param name + * the name of the test to run. + */ + public SplitDBdotNameTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(SplitDBdotNameTest.class); + } + + /** + * Tests sanitation and SplitDBdotName + * + * @throws Exception + * if an error occurs + */ + public void testSplit() throws Exception { + String src = null; + String resString = null; + List results = new ArrayList(); + + //Test 1.1, weird DB.SP name + src = "`MyDatabase 1.0.1.0`.`Proc 1.v1`"; + resString = StringUtils.sanitizeProcOrFuncName(src); + if ((resString != null)) { + results = StringUtils.splitDBdotName(resString, null, "`", true); + assertEquals(results.get(0), "MyDatabase 1.0.1.0"); + assertEquals(results.get(1), "Proc 1.v1"); + } else { + fail("Test 1.1 returned null resString"); + } + + //Test 1.2, toggle isNoBslashEscSet + src = "`MyDatabase 1.0.1.0`.`Proc 1.v1`"; + resString = StringUtils.sanitizeProcOrFuncName(src); + if ((resString != null)) { + results = StringUtils.splitDBdotName(resString, null, "`", false); + assertEquals(results.get(0), "MyDatabase 1.0.1.0"); + assertEquals(results.get(1), "Proc 1.v1"); + } else { + fail("Test 1.2 returned null resString"); + } + + //Test 2.1, weird SP name, no DB parameter + src = "`Proc 1.v1`"; + resString = StringUtils.sanitizeProcOrFuncName(src); + if ((resString != null)) { + results = StringUtils.splitDBdotName(resString, null, "`", true); + assertEquals(results.get(0), null); + assertEquals(results.get(1), "Proc 1.v1"); + } else { + fail("Test 2.1 returned null resString"); + } + + //Test 2.2, toggle isNoBslashEscSet + src = "`Proc 1.v1`"; + resString = StringUtils.sanitizeProcOrFuncName(src); + if ((resString != null)) { + results = StringUtils.splitDBdotName(resString, null, "`", false); + assertEquals(results.get(0), null); + assertEquals(results.get(1), "Proc 1.v1"); + } else { + fail("Test 2.2 returned null resString"); + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/StatementsTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/StatementsTest.java new file mode 100644 index 0000000..9fcfe91 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/StatementsTest.java @@ -0,0 +1,1778 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.sql.BatchUpdateException; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.Properties; + +import com.mysql.jdbc.CharsetMapping; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.NotImplemented; +import com.mysql.jdbc.ParameterBindings; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor; + +import testsuite.BaseTestCase; + +public class StatementsTest extends BaseTestCase { + private static final int MAX_COLUMN_LENGTH = 255; + + private static final int MAX_COLUMNS_TO_TEST = 40; + + private static final int STEP = 8; + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StatementsTest.class); + } + + /** + * Creates a new StatementsTest object. + * + * @param name + */ + public StatementsTest(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_test"); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_batch_test"); + + this.stmt.executeUpdate( + "CREATE TABLE statement_test (id int not null primary key auto_increment, strdata1 varchar(255) not null, strdata2 varchar(255))"); + + try { + this.stmt.executeUpdate("CREATE TABLE statement_batch_test (id int not null primary key auto_increment, " + + "strdata1 varchar(255) not null, strdata2 varchar(255), UNIQUE INDEX (strdata1))"); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("max key length") != -1) { + createTable("statement_batch_test", + "(id int not null primary key auto_increment, strdata1 varchar(175) not null, strdata2 varchar(175), " + "UNIQUE INDEX (strdata1))"); + } + } + + for (int i = 6; i < MAX_COLUMNS_TO_TEST; i += STEP) { + this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_col_test_" + i); + + StringBuilder insertBuf = new StringBuilder("INSERT INTO statement_col_test_"); + StringBuilder stmtBuf = new StringBuilder("CREATE TABLE IF NOT EXISTS statement_col_test_"); + stmtBuf.append(i); + insertBuf.append(i); + stmtBuf.append(" ("); + insertBuf.append(" VALUES ("); + + boolean firstTime = true; + + for (int j = 0; j < i; j++) { + if (!firstTime) { + stmtBuf.append(","); + insertBuf.append(","); + } else { + firstTime = false; + } + + stmtBuf.append("col_"); + stmtBuf.append(j); + stmtBuf.append(" VARCHAR("); + stmtBuf.append(MAX_COLUMN_LENGTH); + stmtBuf.append(")"); + insertBuf.append("'"); + + int numChars = 16; + + for (int k = 0; k < numChars; k++) { + insertBuf.append("A"); + } + + insertBuf.append("'"); + } + + stmtBuf.append(")"); + insertBuf.append(")"); + this.stmt.executeUpdate(stmtBuf.toString()); + this.stmt.executeUpdate(insertBuf.toString()); + } + + // explicitly set the catalog to exercise code in execute(), executeQuery() and executeUpdate() + // FIXME: Only works on Windows! + // this.conn.setCatalog(this.conn.getCatalog().toUpperCase()); + } + + @Override + public void tearDown() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE statement_test"); + + for (int i = 6; i < MAX_COLUMNS_TO_TEST; i += STEP) { + StringBuilder stmtBuf = new StringBuilder("DROP TABLE IF EXISTS statement_col_test_"); + stmtBuf.append(i); + this.stmt.executeUpdate(stmtBuf.toString()); + } + + try { + this.stmt.executeUpdate("DROP TABLE statement_batch_test"); + } catch (SQLException sqlEx) { + } + } finally { + super.tearDown(); + } + } + + public void testAccessorsAndMutators() throws SQLException { + assertTrue("Connection can not be null, and must be same connection", this.stmt.getConnection() == this.conn); + + // Set max rows, to exercise code in execute(), executeQuery() and executeUpdate() + Statement accessorStmt = null; + + try { + accessorStmt = this.conn.createStatement(); + accessorStmt.setMaxRows(1); + accessorStmt.setMaxRows(0); // FIXME, test that this actually affects rows returned + accessorStmt.setMaxFieldSize(255); + assertTrue("Max field size should match what was set", accessorStmt.getMaxFieldSize() == 255); + + try { + accessorStmt.setMaxFieldSize(Integer.MAX_VALUE); + fail("Should not be able to set max field size > max_packet_size"); + } catch (SQLException sqlEx) { + // ignore + } + + accessorStmt.setCursorName("undef"); + accessorStmt.setEscapeProcessing(true); + accessorStmt.setFetchDirection(java.sql.ResultSet.FETCH_FORWARD); + + int fetchDirection = accessorStmt.getFetchDirection(); + assertTrue("Set fetch direction != get fetch direction", fetchDirection == java.sql.ResultSet.FETCH_FORWARD); + + try { + accessorStmt.setFetchDirection(Integer.MAX_VALUE); + fail("Should not be able to set fetch direction to invalid value"); + } catch (SQLException sqlEx) { + // ignore + } + + try { + accessorStmt.setMaxRows(50000000 + 10); + fail("Should not be able to set max rows > 50000000"); + } catch (SQLException sqlEx) { + // ignore + } + + try { + accessorStmt.setMaxRows(Integer.MIN_VALUE); + fail("Should not be able to set max rows < 0"); + } catch (SQLException sqlEx) { + // ignore + } + + int fetchSize = this.stmt.getFetchSize(); + + try { + accessorStmt.setMaxRows(4); + accessorStmt.setFetchSize(Integer.MAX_VALUE); + fail("Should not be able to set FetchSize > max rows"); + } catch (SQLException sqlEx) { + // ignore + } + + try { + accessorStmt.setFetchSize(-2); + fail("Should not be able to set FetchSize < 0"); + } catch (SQLException sqlEx) { + // ignore + } + + assertTrue("Fetch size before invalid setFetchSize() calls should match fetch size now", fetchSize == this.stmt.getFetchSize()); + } finally { + if (accessorStmt != null) { + try { + accessorStmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + + accessorStmt = null; + } + } + } + + public void testAutoIncrement() throws SQLException { + try { + this.stmt.setFetchSize(Integer.MIN_VALUE); + + this.stmt.executeUpdate("INSERT INTO statement_test (strdata1) values ('blah')", Statement.RETURN_GENERATED_KEYS); + + int autoIncKeyFromApi = -1; + this.rs = this.stmt.getGeneratedKeys(); + + if (this.rs.next()) { + autoIncKeyFromApi = this.rs.getInt(1); + } else { + fail("Failed to retrieve AUTO_INCREMENT using Statement.getGeneratedKeys()"); + } + + this.rs.close(); + + int autoIncKeyFromFunc = -1; + this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID()"); + + if (this.rs.next()) { + autoIncKeyFromFunc = this.rs.getInt(1); + } else { + fail("Failed to retrieve AUTO_INCREMENT using LAST_INSERT_ID()"); + } + + if ((autoIncKeyFromApi != -1) && (autoIncKeyFromFunc != -1)) { + assertTrue("Key retrieved from API (" + autoIncKeyFromApi + ") does not match key retrieved from LAST_INSERT_ID() " + autoIncKeyFromFunc + + ") function", autoIncKeyFromApi == autoIncKeyFromFunc); + } else { + fail("AutoIncrement keys were '0'"); + } + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (Exception ex) { + // ignore + } + } + + this.rs = null; + } + } + + /** + * Tests all variants of numerical types (signed/unsigned) for correct + * operation when used as return values from a prepared statement. + * + * @throws Exception + */ + public void testBinaryResultSetNumericTypes() throws Exception { + /* + * TINYINT 1 -128 127 SMALLINT 2 -32768 32767 MEDIUMINT 3 -8388608 + * 8388607 INT 4 -2147483648 2147483647 BIGINT 8 -9223372036854775808 + * 9223372036854775807 + */ + + String unsignedMinimum = "0"; + + String tiMinimum = "-128"; + String tiMaximum = "127"; + String utiMaximum = "255"; + + String siMinimum = "-32768"; + String siMaximum = "32767"; + String usiMaximum = "65535"; + + String miMinimum = "-8388608"; + String miMaximum = "8388607"; + String umiMaximum = "16777215"; + + String iMinimum = "-2147483648"; + String iMaximum = "2147483647"; + String uiMaximum = "4294967295"; + + String biMinimum = "-9223372036854775808"; + String biMaximum = "9223372036854775807"; + String ubiMaximum = "18446744073709551615"; + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBinaryResultSetNumericTypes"); + this.stmt.executeUpdate("CREATE TABLE testBinaryResultSetNumericTypes(rowOrder TINYINT, ti TINYINT,uti TINYINT UNSIGNED, si SMALLINT," + + "usi SMALLINT UNSIGNED, mi MEDIUMINT,umi MEDIUMINT UNSIGNED, i INT, ui INT UNSIGNED,bi BIGINT, ubi BIGINT UNSIGNED)"); + PreparedStatement inserter = this.conn.prepareStatement("INSERT INTO testBinaryResultSetNumericTypes VALUES (?,?,?,?,?,?,?,?,?,?,?)"); + inserter.setInt(1, 0); + inserter.setString(2, tiMinimum); + inserter.setString(3, unsignedMinimum); + inserter.setString(4, siMinimum); + inserter.setString(5, unsignedMinimum); + inserter.setString(6, miMinimum); + inserter.setString(7, unsignedMinimum); + inserter.setString(8, iMinimum); + inserter.setString(9, unsignedMinimum); + inserter.setString(10, biMinimum); + inserter.setString(11, unsignedMinimum); + inserter.executeUpdate(); + + inserter.setInt(1, 1); + inserter.setString(2, tiMaximum); + inserter.setString(3, utiMaximum); + inserter.setString(4, siMaximum); + inserter.setString(5, usiMaximum); + inserter.setString(6, miMaximum); + inserter.setString(7, umiMaximum); + inserter.setString(8, iMaximum); + inserter.setString(9, uiMaximum); + inserter.setString(10, biMaximum); + inserter.setString(11, ubiMaximum); + inserter.executeUpdate(); + + PreparedStatement selector = this.conn.prepareStatement("SELECT * FROM testBinaryResultSetNumericTypes ORDER by rowOrder ASC"); + this.rs = selector.executeQuery(); + + assertTrue(this.rs.next()); + + assertTrue(this.rs.getString(2).equals(tiMinimum)); + assertTrue(this.rs.getString(3).equals(unsignedMinimum)); + assertTrue(this.rs.getString(4).equals(siMinimum)); + assertTrue(this.rs.getString(5).equals(unsignedMinimum)); + assertTrue(this.rs.getString(6).equals(miMinimum)); + assertTrue(this.rs.getString(7).equals(unsignedMinimum)); + assertTrue(this.rs.getString(8).equals(iMinimum)); + assertTrue(this.rs.getString(9).equals(unsignedMinimum)); + assertTrue(this.rs.getString(10).equals(biMinimum)); + assertTrue(this.rs.getString(11).equals(unsignedMinimum)); + + assertTrue(this.rs.next()); + + assertTrue(this.rs.getString(2) + " != " + tiMaximum, this.rs.getString(2).equals(tiMaximum)); + assertTrue(this.rs.getString(3) + " != " + utiMaximum, this.rs.getString(3).equals(utiMaximum)); + assertTrue(this.rs.getString(4) + " != " + siMaximum, this.rs.getString(4).equals(siMaximum)); + assertTrue(this.rs.getString(5) + " != " + usiMaximum, this.rs.getString(5).equals(usiMaximum)); + assertTrue(this.rs.getString(6) + " != " + miMaximum, this.rs.getString(6).equals(miMaximum)); + assertTrue(this.rs.getString(7) + " != " + umiMaximum, this.rs.getString(7).equals(umiMaximum)); + assertTrue(this.rs.getString(8) + " != " + iMaximum, this.rs.getString(8).equals(iMaximum)); + assertTrue(this.rs.getString(9) + " != " + uiMaximum, this.rs.getString(9).equals(uiMaximum)); + assertTrue(this.rs.getString(10) + " != " + biMaximum, this.rs.getString(10).equals(biMaximum)); + assertTrue(this.rs.getString(11) + " != " + ubiMaximum, this.rs.getString(11).equals(ubiMaximum)); + + assertTrue(!this.rs.next()); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testBinaryResultSetNumericTypes"); + } + } + + /** + * Tests stored procedure functionality + * + * @throws Exception + * if an error occurs. + */ + public void testCallableStatement() throws Exception { + if (versionMeetsMinimum(5, 0)) { + CallableStatement cStmt = null; + String stringVal = "abcdefg"; + int intVal = 42; + + try { + try { + this.stmt.executeUpdate("DROP PROCEDURE testCallStmt"); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("does not exist") == -1) { + throw sqlEx; + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS callStmtTbl"); + this.stmt.executeUpdate("CREATE TABLE callStmtTbl (x CHAR(16), y INT)"); + + this.stmt.executeUpdate("CREATE PROCEDURE testCallStmt(n INT, x CHAR(16), y INT) WHILE n DO SET n = n - 1;" + + " INSERT INTO callStmtTbl VALUES (x, y); END WHILE;"); + + int rowsToCheck = 15; + + cStmt = this.conn.prepareCall("{call testCallStmt(?,?,?)}"); + cStmt.setInt(1, rowsToCheck); + cStmt.setString(2, stringVal); + cStmt.setInt(3, intVal); + cStmt.execute(); + + this.rs = this.stmt.executeQuery("SELECT x,y FROM callStmtTbl"); + + int numRows = 0; + + while (this.rs.next()) { + assertTrue(this.rs.getString(1).equals(stringVal) && (this.rs.getInt(2) == intVal)); + + numRows++; + } + + this.rs.close(); + this.rs = null; + + cStmt.close(); + cStmt = null; + + System.out.println(rowsToCheck + " rows returned"); + + assertTrue(numRows == rowsToCheck); + } finally { + try { + this.stmt.executeUpdate("DROP PROCEDURE testCallStmt"); + } catch (SQLException sqlEx) { + if (sqlEx.getMessage().indexOf("does not exist") == -1) { + throw sqlEx; + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS callStmtTbl"); + + if (cStmt != null) { + cStmt.close(); + } + } + } + } + + public void testCancelStatement() throws Exception { + + if (versionMeetsMinimum(5, 0)) { + Connection cancelConn = null; + + try { + cancelConn = getConnectionWithProps((String) null); + final Statement cancelStmt = cancelConn.createStatement(); + + cancelStmt.setQueryTimeout(1); + + long begin = System.currentTimeMillis(); + + try { + cancelStmt.execute("SELECT SLEEP(30)"); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelStmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + cancelStmt.setQueryTimeout(0); + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + cancelStmt.setQueryTimeout(0); + + new Thread() { + + @Override + public void run() { + try { + try { + sleep(5000); + } catch (InterruptedException iEx) { + // ignore + } + + cancelStmt.cancel(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + }.start(); + + begin = System.currentTimeMillis(); + + try { + cancelStmt.execute("SELECT SLEEP(30)"); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelStmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + final PreparedStatement cancelPstmt = cancelConn.prepareStatement("SELECT SLEEP(30)"); + + cancelPstmt.setQueryTimeout(1); + + begin = System.currentTimeMillis(); + + try { + cancelPstmt.execute(); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelPstmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + cancelPstmt.setQueryTimeout(0); + + new Thread() { + + @Override + public void run() { + try { + try { + sleep(5000); + } catch (InterruptedException iEx) { + // ignore + } + + cancelPstmt.cancel(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + }.start(); + + begin = System.currentTimeMillis(); + + try { + cancelPstmt.execute(); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelPstmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + final PreparedStatement cancelClientPstmt = ((com.mysql.jdbc.Connection) cancelConn).clientPrepareStatement("SELECT SLEEP(30)"); + + cancelClientPstmt.setQueryTimeout(1); + + begin = System.currentTimeMillis(); + + try { + cancelClientPstmt.execute(); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelStmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + cancelClientPstmt.setQueryTimeout(0); + + new Thread() { + + @Override + public void run() { + try { + try { + sleep(5000); + } catch (InterruptedException iEx) { + // ignore + } + + cancelClientPstmt.cancel(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + }.start(); + + begin = System.currentTimeMillis(); + + try { + cancelClientPstmt.execute(); + } catch (SQLException sqlEx) { + assertTrue("Probably wasn't actually cancelled", System.currentTimeMillis() - begin < 30000); + } + + for (int i = 0; i < 1000; i++) { + try { + cancelClientPstmt.executeQuery("SELECT 1"); + } catch (SQLException timedOutEx) { + break; + } + } + + // Make sure we can still use the connection... + + this.rs = cancelStmt.executeQuery("SELECT 1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + Connection forceCancel = getConnectionWithProps("queryTimeoutKillsConnection=true"); + Statement forceStmt = forceCancel.createStatement(); + forceStmt.setQueryTimeout(1); + + try { + forceStmt.execute("SELECT SLEEP(30)"); + fail("Statement should have been cancelled"); + } catch (MySQLTimeoutException timeout) { + // expected + } + + int count = 1000; + + for (; count > 0; count--) { + if (forceCancel.isClosed()) { + break; + } + + Thread.sleep(100); + } + + if (count == 0) { + fail("Connection was never killed"); + } + + try { + forceCancel.setAutoCommit(true); // should fail too + } catch (SQLException sqlEx) { + assertTrue(sqlEx.getCause() instanceof MySQLStatementCancelledException); + } + + } finally { + if (this.rs != null) { + ResultSet toClose = this.rs; + this.rs = null; + toClose.close(); + } + + if (cancelConn != null) { + cancelConn.close(); + } + } + } + } + + public void testClose() throws SQLException { + Statement closeStmt = null; + boolean exceptionAfterClosed = false; + + try { + closeStmt = this.conn.createStatement(); + closeStmt.close(); + + try { + closeStmt.executeQuery("SELECT 1"); + } catch (SQLException sqlEx) { + exceptionAfterClosed = true; + } + } finally { + if (closeStmt != null) { + try { + closeStmt.close(); + } catch (SQLException sqlEx) { + /* ignore */ + } + } + + closeStmt = null; + } + + assertTrue("Operations not allowed on Statement after .close() is called!", exceptionAfterClosed); + } + + public void testEnableStreamingResults() throws Exception { + Statement streamStmt = this.conn.createStatement(); + ((com.mysql.jdbc.Statement) streamStmt).enableStreamingResults(); + assertEquals(streamStmt.getFetchSize(), Integer.MIN_VALUE); + assertEquals(streamStmt.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY); + } + + public void testHoldingResultSetsOverClose() throws Exception { + Properties props = new Properties(); + props.setProperty("holdResultsOpenOverStatementClose", "true"); + + Connection conn2 = getConnectionWithProps(props); + + Statement stmt2 = null; + PreparedStatement pstmt2 = null; + + ResultSet rs2 = null; + + try { + stmt2 = conn2.createStatement(); + + this.rs = stmt2.executeQuery("SELECT 1"); + this.rs.next(); + this.rs.getInt(1); + stmt2.close(); + this.rs.getInt(1); + + stmt2 = conn2.createStatement(); + stmt2.execute("SELECT 1"); + this.rs = stmt2.getResultSet(); + this.rs.next(); + this.rs.getInt(1); + stmt2.execute("SELECT 2"); + this.rs.getInt(1); + + pstmt2 = conn2.prepareStatement("SELECT 1"); + this.rs = pstmt2.executeQuery(); + this.rs.next(); + this.rs.getInt(1); + pstmt2.close(); + this.rs.getInt(1); + + pstmt2 = conn2.prepareStatement("SELECT 1"); + this.rs = pstmt2.executeQuery(); + this.rs.next(); + this.rs.getInt(1); + rs2 = pstmt2.executeQuery(); + this.rs.getInt(1); + pstmt2.execute(); + this.rs.getInt(1); + rs2.close(); + + pstmt2 = ((com.mysql.jdbc.Connection) conn2).clientPrepareStatement("SELECT 1"); + this.rs = pstmt2.executeQuery(); + this.rs.next(); + this.rs.getInt(1); + pstmt2.close(); + this.rs.getInt(1); + + pstmt2 = ((com.mysql.jdbc.Connection) conn2).clientPrepareStatement("SELECT 1"); + this.rs = pstmt2.executeQuery(); + this.rs.next(); + this.rs.getInt(1); + rs2 = pstmt2.executeQuery(); + this.rs.getInt(1); + pstmt2.execute(); + this.rs.getInt(1); + rs2.close(); + + stmt2 = conn2.createStatement(); + this.rs = stmt2.executeQuery("SELECT 1"); + this.rs.next(); + this.rs.getInt(1); + rs2 = stmt2.executeQuery("SELECT 2"); + this.rs.getInt(1); + this.rs = stmt2.executeQuery("SELECT 1"); + this.rs.next(); + this.rs.getInt(1); + stmt2.executeUpdate("SET @var=1"); + this.rs.getInt(1); + stmt2.execute("SET @var=2"); + this.rs.getInt(1); + rs2.close(); + } finally { + if (stmt2 != null) { + stmt2.close(); + } + } + } + + public void testInsert() throws SQLException { + try { + boolean autoCommit = this.conn.getAutoCommit(); + + // Test running a query for an update. It should fail. + try { + this.conn.setAutoCommit(false); + this.stmt.executeUpdate("SELECT * FROM statement_test"); + } catch (SQLException sqlEx) { + assertTrue("Exception thrown for unknown reason", sqlEx.getSQLState().equalsIgnoreCase("01S03")); + } finally { + this.conn.setAutoCommit(autoCommit); + } + + // Test running a update for an query. It should fail. + try { + this.conn.setAutoCommit(false); + this.rs = this.stmt.executeQuery("UPDATE statement_test SET strdata1='blah' WHERE 1=0"); + } catch (SQLException sqlEx) { + assertTrue("Exception thrown for unknown reason", sqlEx.getSQLState().equalsIgnoreCase(SQLError.SQL_STATE_ILLEGAL_ARGUMENT)); + } finally { + this.conn.setAutoCommit(autoCommit); + } + + for (int i = 0; i < 10; i++) { + int updateCount = this.stmt.executeUpdate("INSERT INTO statement_test (strdata1,strdata2) values ('abcdefg', 'poi')"); + assertTrue("Update count must be '1', was '" + updateCount + "'", (updateCount == 1)); + } + + int insertIdFromGeneratedKeys = Integer.MIN_VALUE; + + this.stmt.executeUpdate("INSERT INTO statement_test (strdata1, strdata2) values ('a', 'a'), ('b', 'b'), ('c', 'c')", + Statement.RETURN_GENERATED_KEYS); + this.rs = this.stmt.getGeneratedKeys(); + + if (this.rs.next()) { + insertIdFromGeneratedKeys = this.rs.getInt(1); + } + + this.rs.close(); + this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID()"); + + int insertIdFromServer = Integer.MIN_VALUE; + + if (this.rs.next()) { + insertIdFromServer = this.rs.getInt(1); + } + + assertEquals(insertIdFromGeneratedKeys, insertIdFromServer); + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (Exception ex) { + // ignore + } + } + + this.rs = null; + } + } + + /** + * Tests multiple statement support + * + * @throws Exception + */ + public void testMultiStatements() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Connection multiStmtConn = null; + Statement multiStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("allowMultiQueries", "true"); + + multiStmtConn = getConnectionWithProps(props); + + multiStmt = multiStmtConn.createStatement(); + + multiStmt.executeUpdate("DROP TABLE IF EXISTS testMultiStatements"); + multiStmt.executeUpdate("CREATE TABLE testMultiStatements (field1 VARCHAR(255), field2 INT, field3 DOUBLE)"); + multiStmt.executeUpdate("INSERT INTO testMultiStatements VALUES ('abcd', 1, 2)"); + + multiStmt.execute("SELECT field1 FROM testMultiStatements WHERE field1='abcd';UPDATE testMultiStatements SET field3=3;" + + "SELECT field3 FROM testMultiStatements WHERE field3=3"); + + this.rs = multiStmt.getResultSet(); + + assertTrue(this.rs.next()); + + assertTrue("abcd".equals(this.rs.getString(1))); + this.rs.close(); + + // Next should be an update count... + assertTrue(!multiStmt.getMoreResults()); + + assertTrue("Update count was " + multiStmt.getUpdateCount() + ", expected 1", multiStmt.getUpdateCount() == 1); + + assertTrue(multiStmt.getMoreResults()); + + this.rs = multiStmt.getResultSet(); + + assertTrue(this.rs.next()); + + assertTrue(this.rs.getDouble(1) == 3); + + // End of multi results + assertTrue(!multiStmt.getMoreResults()); + assertTrue(multiStmt.getUpdateCount() == -1); + } finally { + if (multiStmt != null) { + multiStmt.executeUpdate("DROP TABLE IF EXISTS testMultiStatements"); + + multiStmt.close(); + } + + if (multiStmtConn != null) { + multiStmtConn.close(); + } + } + } + } + + /** + * Tests that NULLs and '' work correctly. + * + * @throws SQLException + * if an error occurs + */ + public void testNulls() throws SQLException { + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS nullTest"); + this.stmt.executeUpdate("CREATE TABLE IF NOT EXISTS nullTest (field_1 CHAR(20), rowOrder INT)"); + this.stmt.executeUpdate("INSERT INTO nullTest VALUES (null, 1), ('', 2)"); + + this.rs = this.stmt.executeQuery("SELECT field_1 FROM nullTest ORDER BY rowOrder"); + + this.rs.next(); + + assertTrue("NULL field not returned as NULL", (this.rs.getString("field_1") == null) && this.rs.wasNull()); + + this.rs.next(); + + assertTrue("Empty field not returned as \"\"", this.rs.getString("field_1").equals("") && !this.rs.wasNull()); + + this.rs.close(); + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (Exception ex) { + // ignore + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS nullTest"); + } + } + + public void testParsedConversionWarning() throws Exception { + if (versionMeetsMinimum(4, 1)) { + try { + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + Connection warnConn = getConnectionWithProps(props); + + this.stmt.executeUpdate("DROP TABLE IF EXISTS testParsedConversionWarning"); + this.stmt.executeUpdate("CREATE TABLE testParsedConversionWarning(field1 VARCHAR(255))"); + this.stmt.executeUpdate("INSERT INTO testParsedConversionWarning VALUES ('1.0')"); + + PreparedStatement badStmt = warnConn.prepareStatement("SELECT field1 FROM testParsedConversionWarning"); + + this.rs = badStmt.executeQuery(); + assertTrue(this.rs.next()); + this.rs.getFloat(1); + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testParsedConversionWarning"); + } + } + } + + public void testPreparedStatement() throws SQLException { + this.stmt.executeUpdate("INSERT INTO statement_test (id, strdata1,strdata2) values (999,'abcdefg', 'poi')"); + this.pstmt = this.conn.prepareStatement("UPDATE statement_test SET strdata1=?, strdata2=? where id=999"); + this.pstmt.setString(1, "iop"); + this.pstmt.setString(2, "higjklmn"); + + int updateCount = this.pstmt.executeUpdate(); + assertTrue("Update count must be '1', was '" + updateCount + "'", (updateCount == 1)); + + this.pstmt.clearParameters(); + + this.pstmt.close(); + + this.rs = this.stmt.executeQuery("SELECT id, strdata1, strdata2 FROM statement_test"); + + assertTrue(this.rs.next()); + assertTrue(this.rs.getInt(1) == 999); + assertTrue("Expected 'iop', received '" + this.rs.getString(2) + "'", "iop".equals(this.rs.getString(2))); + assertTrue("Expected 'higjklmn', received '" + this.rs.getString(3) + "'", "higjklmn".equals(this.rs.getString(3))); + } + + public void testPreparedStatementBatch() throws SQLException { + this.pstmt = this.conn.prepareStatement("INSERT INTO statement_batch_test (strdata1, strdata2) VALUES (?,?)"); + + for (int i = 0; i < 1000; i++) { + this.pstmt.setString(1, "batch_" + i); + this.pstmt.setString(2, "batch_" + i); + this.pstmt.addBatch(); + } + + int[] updateCounts = this.pstmt.executeBatch(); + + for (int i = 0; i < updateCounts.length; i++) { + assertTrue("Update count must be '1', was '" + updateCounts[i] + "'", (updateCounts[i] == 1)); + } + } + + public void testRowFetch() throws Exception { + if (versionMeetsMinimum(5, 0, 5)) { + createTable("testRowFetch", "(field1 int)"); + + this.stmt.executeUpdate("INSERT INTO testRowFetch VALUES (1)"); + + Connection fetchConn = null; + + Properties props = new Properties(); + props.setProperty("useCursorFetch", "true"); + + try { + fetchConn = getConnectionWithProps(props); + + PreparedStatement fetchStmt = fetchConn.prepareStatement("SELECT field1 FROM testRowFetch WHERE field1=1"); + fetchStmt.setFetchSize(10); + this.rs = fetchStmt.executeQuery(); + assertTrue(this.rs.next()); + + this.stmt.executeUpdate("INSERT INTO testRowFetch VALUES (2), (3)"); + + fetchStmt = fetchConn.prepareStatement("SELECT field1 FROM testRowFetch ORDER BY field1"); + fetchStmt.setFetchSize(1); + this.rs = fetchStmt.executeQuery(); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + assertTrue(this.rs.next()); + assertEquals(3, this.rs.getInt(1)); + assertEquals(false, this.rs.next()); + + this.rs = fetchStmt.executeQuery(); + } finally { + if (fetchConn != null) { + fetchConn.close(); + } + } + + } + } + + public void testSelectColumns() throws SQLException { + for (int i = 6; i < MAX_COLUMNS_TO_TEST; i += STEP) { + long start = System.currentTimeMillis(); + this.rs = this.stmt.executeQuery("SELECT * from statement_col_test_" + i); + + if (this.rs.next()) { + } + + long end = System.currentTimeMillis(); + System.out.println(i + " columns = " + (end - start) + " ms"); + } + } + + /** + * Tests for PreparedStatement.setObject() + * + * @throws Exception + */ + public void testSetObject() throws Exception { + Properties props = new Properties(); + props.put("noDatetimeStringSync", "true"); // value=true for #5 + Connection conn1 = getConnectionWithProps(props); + Statement stmt1 = conn1.createStatement(); + createTable("t1", + " (c1 DECIMAL," // instance of String + + "c2 VARCHAR(255)," // instance of String + + "c3 BLOB," // instance of byte[] + + "c4 DATE," // instance of java.util.Date + + "c5 TIMESTAMP," // instance of String + + "c6 TIME," // instance of String + + "c7 TIME)"); // instance of java.sql.Timestamp + + this.pstmt = conn1.prepareStatement("INSERT INTO t1 VALUES (?, ?, ?, ?, ?, ?, ?)"); + + long currentTime = System.currentTimeMillis(); + + this.pstmt.setObject(1, "1000", Types.DECIMAL); + this.pstmt.setObject(2, "2000", Types.VARCHAR); + this.pstmt.setObject(3, new byte[] { 0 }, Types.BLOB); + this.pstmt.setObject(4, new java.util.Date(currentTime), Types.DATE); + this.pstmt.setObject(5, "2000-01-01 23-59-59", Types.TIMESTAMP); + this.pstmt.setObject(6, "11:22:33", Types.TIME); + this.pstmt.setObject(7, new java.sql.Timestamp(currentTime), Types.TIME); + this.pstmt.execute(); + this.rs = stmt1.executeQuery("SELECT * FROM t1"); + this.rs.next(); + + assertEquals("1000", this.rs.getString(1)); + assertEquals("2000", this.rs.getString(2)); + assertEquals(1, ((byte[]) this.rs.getObject(3)).length); + assertEquals(0, ((byte[]) this.rs.getObject(3))[0]); + assertEquals(new java.sql.Date(currentTime).toString(), this.rs.getDate(4).toString()); + + if (versionMeetsMinimum(4, 1)) { + assertEquals("2000-01-01 23:59:59", this.rs.getString(5)); + } else { + assertEquals("20000101235959", this.rs.getString(5)); + } + + assertEquals("11:22:33", this.rs.getString(6)); + assertEquals(new java.sql.Time(currentTime).toString(), this.rs.getString(7)); + } + + public void testStatementRewriteBatch() throws Exception { + for (int j = 0; j < 2; j++) { + Properties props = new Properties(); + + if (j == 0) { + props.setProperty("useServerPrepStmts", "true"); + } + + props.setProperty("rewriteBatchedStatements", "true"); + Connection multiConn = getConnectionWithProps(props); + createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + Statement multiStmt = multiConn.createStatement(); + multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (1)"); + multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (2)"); + multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (3)"); + multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (4)"); + multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=5 WHERE field1=1"); + multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=6 WHERE field1=2 OR field1=3"); + + int[] counts = multiStmt.executeBatch(); + ResultSet genKeys = multiStmt.getGeneratedKeys(); + + for (int i = 1; i < 5; i++) { + genKeys.next(); + assertEquals(i, genKeys.getInt(1)); + } + + assertEquals(counts.length, 6); + assertEquals(counts[0], 1); + assertEquals(counts[1], 1); + assertEquals(counts[2], 1); + assertEquals(counts[3], 1); + assertEquals(counts[4], 1); + assertEquals(counts[5], 2); + + this.rs = multiStmt.executeQuery("SELECT field1 FROM testStatementRewriteBatch ORDER BY field1"); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 4); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 5); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 6); + assertTrue(this.rs.next()); + assertEquals(this.rs.getInt(1), 6); + + createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + props.clear(); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("maxAllowedPacket", "1024"); + multiConn = getConnectionWithProps(props); + multiStmt = multiConn.createStatement(); + + for (int i = 0; i < 1000; i++) { + multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (" + i + ")"); + } + + multiStmt.executeBatch(); + genKeys = multiStmt.getGeneratedKeys(); + + for (int i = 1; i < 1000; i++) { + genKeys.next(); + assertEquals(i, genKeys.getInt(1)); + } + + createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + + props.clear(); + props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false"); + props.setProperty("rewriteBatchedStatements", "true"); + multiConn = getConnectionWithProps(props); + + PreparedStatement pStmt = null; + pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + + for (int i = 0; i < 1000; i++) { + pStmt.setInt(1, i); + pStmt.addBatch(); + } + + pStmt.executeBatch(); + genKeys = pStmt.getGeneratedKeys(); + + for (int i = 1; i < 1000; i++) { + genKeys.next(); + assertEquals(i, genKeys.getInt(1)); + } + + createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)"); + props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false"); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("maxAllowedPacket", j == 0 ? "10240" : "1024"); + multiConn = getConnectionWithProps(props); + + pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + + for (int i = 0; i < 1000; i++) { + pStmt.setInt(1, i); + pStmt.addBatch(); + } + + pStmt.executeBatch(); + genKeys = pStmt.getGeneratedKeys(); + + for (int i = 1; i < 1000; i++) { + genKeys.next(); + assertEquals(i, genKeys.getInt(1)); + } + + Object[][] differentTypes = new Object[1000][14]; + + createTable("rewriteBatchTypes", + "(internalOrder int, f1 tinyint null, " + "f2 smallint null, f3 int null, f4 bigint null, " + + "f5 decimal(8, 2) null, f6 float null, f7 double null, " + "f8 varchar(255) null, f9 text null, f10 blob null, f11 blob null, " + + (versionMeetsMinimum(5, 6, 4) ? "f12 datetime(3) null, f13 time(3) null, f14 date null)" + : "f12 datetime null, f13 time null, f14 date null)")); + + for (int i = 0; i < 1000; i++) { + differentTypes[i][0] = Math.random() < .5 ? null : new Byte((byte) (Math.random() * 127)); + differentTypes[i][1] = Math.random() < .5 ? null : new Short((short) (Math.random() * Short.MAX_VALUE)); + differentTypes[i][2] = Math.random() < .5 ? null : new Integer((int) (Math.random() * Integer.MAX_VALUE)); + differentTypes[i][3] = Math.random() < .5 ? null : new Long((long) (Math.random() * Long.MAX_VALUE)); + differentTypes[i][4] = Math.random() < .5 ? null : new BigDecimal("19.95"); + differentTypes[i][5] = Math.random() < .5 ? null : new Float(3 + ((float) (Math.random()))); + differentTypes[i][6] = Math.random() < .5 ? null : new Double(3 + (Math.random())); + differentTypes[i][7] = Math.random() < .5 ? null : randomString(); + differentTypes[i][8] = Math.random() < .5 ? null : randomString(); + differentTypes[i][9] = Math.random() < .5 ? null : randomString().getBytes(); + differentTypes[i][10] = Math.random() < .5 ? null : randomString().getBytes(); + differentTypes[i][11] = Math.random() < .5 ? null : new Timestamp(System.currentTimeMillis()); + differentTypes[i][12] = Math.random() < .5 ? null : new Time(System.currentTimeMillis()); + differentTypes[i][13] = Math.random() < .5 ? null : new Date(System.currentTimeMillis()); + } + + props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false"); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("maxAllowedPacket", j == 0 ? "10240" : "1024"); + multiConn = getConnectionWithProps(props); + pStmt = multiConn.prepareStatement( + "INSERT INTO rewriteBatchTypes(internalOrder,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14) VALUES " + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); + + for (int i = 0; i < 1000; i++) { + pStmt.setInt(1, i); + for (int k = 0; k < 14; k++) { + if (k == 8) { + String asString = (String) differentTypes[i][k]; + + if (asString == null) { + pStmt.setObject(k + 2, null); + } else { + pStmt.setCharacterStream(k + 2, new StringReader(asString), asString.length()); + } + } else if (k == 9) { + byte[] asBytes = (byte[]) differentTypes[i][k]; + + if (asBytes == null) { + pStmt.setObject(k + 2, null); + } else { + pStmt.setBinaryStream(k + 2, new ByteArrayInputStream(asBytes), asBytes.length); + } + } else { + pStmt.setObject(k + 2, differentTypes[i][k]); + } + } + pStmt.addBatch(); + } + + pStmt.executeBatch(); + + this.rs = this.stmt + .executeQuery("SELECT f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14 FROM rewriteBatchTypes ORDER BY internalOrder"); + + int idx = 0; + + // We need to format this ourselves, since we have to strip the nanos off of TIMESTAMPs, so .equals() doesn't really work... + + SimpleDateFormat sdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US); + + while (this.rs.next()) { + for (int k = 0; k < 14; k++) { + if (differentTypes[idx][k] == null) { + assertTrue("On row " + idx + " expected NULL, found " + this.rs.getObject(k + 1) + " in column " + (k + 1), + this.rs.getObject(k + 1) == null); + } else { + String className = differentTypes[idx][k].getClass().getName(); + + if (className.equals("java.io.StringReader")) { + StringReader reader = (StringReader) differentTypes[idx][k]; + StringBuilder buf = new StringBuilder(); + + int c = 0; + + while ((c = reader.read()) != -1) { + buf.append((char) c); + } + + String asString = this.rs.getString(k + 1); + + assertEquals("On row " + idx + ", column " + (k + 1), buf.toString(), asString); + + } else if (differentTypes[idx][k] instanceof java.io.InputStream) { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + int bytesRead = 0; + + byte[] buf = new byte[128]; + InputStream in = (InputStream) differentTypes[idx][k]; + + while ((bytesRead = in.read(buf)) != -1) { + bOut.write(buf, 0, bytesRead); + } + + byte[] expected = bOut.toByteArray(); + byte[] actual = this.rs.getBytes(k + 1); + + assertEquals("On row " + idx + ", column " + (k + 1), StringUtils.dumpAsHex(expected, expected.length), + StringUtils.dumpAsHex(actual, actual.length)); + } else if (differentTypes[idx][k] instanceof byte[]) { + byte[] expected = (byte[]) differentTypes[idx][k]; + byte[] actual = this.rs.getBytes(k + 1); + assertEquals("On row " + idx + ", column " + (k + 1), StringUtils.dumpAsHex(expected, expected.length), + StringUtils.dumpAsHex(actual, actual.length)); + } else if (differentTypes[idx][k] instanceof Timestamp) { + assertEquals("On row " + idx + ", column " + (k + 1), sdf.format(differentTypes[idx][k]), sdf.format(this.rs.getObject(k + 1))); + } else if (differentTypes[idx][k] instanceof Double) { + assertEquals("On row " + idx + ", column " + (k + 1), ((Double) differentTypes[idx][k]).doubleValue(), this.rs.getDouble(k + 1), + .1); + } else if (differentTypes[idx][k] instanceof Float) { + assertEquals("On row " + idx + ", column " + (k + 1), ((Float) differentTypes[idx][k]).floatValue(), this.rs.getFloat(k + 1), .1); + } else if (className.equals("java.lang.Byte")) { + // special mapping in JDBC for ResultSet.getObject() + assertEquals("On row " + idx + ", column " + (k + 1), new Integer(((Byte) differentTypes[idx][k]).byteValue()), + this.rs.getObject(k + 1)); + } else if (className.equals("java.lang.Short")) { + // special mapping in JDBC for ResultSet.getObject() + assertEquals("On row " + idx + ", column " + (k + 1), new Integer(((Short) differentTypes[idx][k]).shortValue()), + this.rs.getObject(k + 1)); + } else { + assertEquals("On row " + idx + ", column " + (k + 1) + " (" + differentTypes[idx][k].getClass() + "/" + + this.rs.getObject(k + 1).getClass(), differentTypes[idx][k].toString(), this.rs.getObject(k + 1).toString()); + } + } + } + + idx++; + } + } + } + + public void testBatchRewriteErrors() throws Exception { + createTable("rewriteErrors", "(field1 int not null primary key) ENGINE=MyISAM"); + + Properties props = new Properties(); + Connection multiConn = null; + + for (int j = 0; j < 2; j++) { + props.setProperty("useServerPrepStmts", "false"); + + if (j == 1) { + props.setProperty("continueBatchOnError", "false"); + } else { + props.setProperty("continueBatchOnError", "true"); + } + + props.setProperty("maxAllowedPacket", "4096"); + props.setProperty("rewriteBatchedStatements", "true"); + multiConn = getConnectionWithProps(props); + this.pstmt = multiConn.prepareStatement("INSERT INTO rewriteErrors VALUES (?)"); + Statement multiStmt = multiConn.createStatement(); + + for (int i = 0; i < 4096; i++) { + multiStmt.addBatch("INSERT INTO rewriteErrors VALUES (" + i + ")"); + this.pstmt.setInt(1, i); + this.pstmt.addBatch(); + } + + multiStmt.addBatch("INSERT INTO rewriteErrors VALUES (2048)"); + + this.pstmt.setInt(1, 2048); + this.pstmt.addBatch(); + + try { + this.pstmt.executeBatch(); + } catch (BatchUpdateException bUpE) { + int[] counts = bUpE.getUpdateCounts(); + + for (int i = 4059; i < counts.length; i++) { + assertEquals(counts[i], Statement.EXECUTE_FAILED); + } + + // this depends on max_allowed_packet, only a sanity check + assertTrue(getRowCount("rewriteErrors") >= 4000); + } + + this.stmt.execute("TRUNCATE TABLE rewriteErrors"); + + try { + multiStmt.executeBatch(); + } catch (BatchUpdateException bUpE) { + int[] counts = bUpE.getUpdateCounts(); + + for (int i = 4094; i < counts.length; i++) { + assertEquals(counts[i], Statement.EXECUTE_FAILED); + } + + // this depends on max_allowed_packet, only a sanity check + assertTrue(getRowCount("rewriteErrors") >= 4000); + } + + if (versionMeetsMinimum(5, 0)) { + this.stmt.execute("TRUNCATE TABLE rewriteErrors"); + + createProcedure("sp_rewriteErrors", "(param1 INT)\nBEGIN\nINSERT INTO rewriteErrors VALUES (param1);\nEND"); + + CallableStatement cStmt = multiConn.prepareCall("{ CALL sp_rewriteErrors(?)}"); + + for (int i = 0; i < 4096; i++) { + cStmt.setInt(1, i); + cStmt.addBatch(); + } + + cStmt.setInt(1, 2048); + cStmt.addBatch(); + + try { + cStmt.executeBatch(); + } catch (BatchUpdateException bUpE) { + int[] counts = bUpE.getUpdateCounts(); + + for (int i = 4093; i < counts.length; i++) { + assertEquals(counts[i], Statement.EXECUTE_FAILED); + } + + // this depends on max_allowed_packet, only a sanity check + assertTrue(getRowCount("rewriteErrors") >= 4000); + } + } + } + } + + public void testStreamChange() throws Exception { + createTable("testStreamChange", "(field1 varchar(32), field2 int, field3 TEXT, field4 BLOB)"); + this.pstmt = this.conn.prepareStatement("INSERT INTO testStreamChange VALUES (?, ?, ?, ?)"); + + try { + this.pstmt.setString(1, "A"); + this.pstmt.setInt(2, 1); + + char[] cArray = { 'A', 'B', 'C' }; + Reader r = new CharArrayReader(cArray); + this.pstmt.setCharacterStream(3, r, cArray.length); + + byte[] bArray = { 'D', 'E', 'F' }; + ByteArrayInputStream bais = new ByteArrayInputStream(bArray); + this.pstmt.setBinaryStream(4, bais, bArray.length); + + assertEquals(1, this.pstmt.executeUpdate()); + + this.rs = this.stmt.executeQuery("SELECT field3, field4 from testStreamChange where field1='A'"); + this.rs.next(); + assertEquals("ABC", this.rs.getString(1)); + assertEquals("DEF", this.rs.getString(2)); + + char[] ucArray = { 'C', 'E', 'S', 'U' }; + this.pstmt.setString(1, "CESU"); + this.pstmt.setInt(2, 3); + Reader ucReader = new CharArrayReader(ucArray); + this.pstmt.setCharacterStream(3, ucReader, ucArray.length); + this.pstmt.setBinaryStream(4, null, 0); + assertEquals(1, this.pstmt.executeUpdate()); + + this.rs = this.stmt.executeQuery("SELECT field3, field4 from testStreamChange where field1='CESU'"); + this.rs.next(); + assertEquals("CESU", this.rs.getString(1)); + assertEquals(null, this.rs.getString(2)); + } finally { + if (this.rs != null) { + this.rs.close(); + this.rs = null; + } + + if (this.pstmt != null) { + this.pstmt.close(); + this.pstmt = null; + } + } + } + + public void testStubbed() throws SQLException { + try { + this.stmt.getResultSetHoldability(); + } catch (NotImplemented notImplEx) { + } + } + + public void testTruncationOnRead() throws Exception { + this.rs = this.stmt.executeQuery("SELECT '" + Long.MAX_VALUE + "'"); + this.rs.next(); + + try { + this.rs.getByte(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getShort(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getInt(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + this.rs = this.stmt.executeQuery("SELECT '" + Double.MAX_VALUE + "'"); + + this.rs.next(); + + try { + this.rs.getByte(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getShort(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getInt(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getLong(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getLong(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + PreparedStatement pStmt = null; + + System.out.println("Testing prepared statements with binary result sets now"); + + try { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTruncationOnRead"); + this.stmt.executeUpdate("CREATE TABLE testTruncationOnRead(intField INTEGER, bigintField BIGINT, doubleField DOUBLE)"); + this.stmt.executeUpdate("INSERT INTO testTruncationOnRead VALUES (" + Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ", " + Double.MAX_VALUE + ")"); + this.stmt.executeUpdate("INSERT INTO testTruncationOnRead VALUES (" + Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ", " + Double.MIN_VALUE + ")"); + + pStmt = this.conn.prepareStatement("SELECT intField, bigintField, doubleField FROM testTruncationOnRead ORDER BY intField DESC"); + this.rs = pStmt.executeQuery(); + + this.rs.next(); + + try { + this.rs.getByte(1); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getInt(2); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + + try { + this.rs.getLong(3); + fail("Should've thrown an out-of-range exception"); + } catch (SQLException sqlEx) { + assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE.equals(sqlEx.getSQLState())); + } + } finally { + this.stmt.executeUpdate("DROP TABLE IF EXISTS testTruncationOnRead"); + } + + } + + public void testStatementInterceptors() throws Exception { + Connection interceptedConn = null; + + /* + * try { + * Properties props = new Properties(); + * props.setProperty("statementInterceptors", "com.mysql.jdbc.interceptors.ResultSetScannerInterceptor"); + * props.setProperty("resultSetScannerRegex", ".*"); + * interceptedConn = getConnectionWithProps(props); + * this.rs = interceptedConn.createStatement().executeQuery("SELECT 'abc'"); + * this.rs.next(); + * this.rs.getString(1); + * } finally { + * closeMemberJDBCResources(); + * + * if (interceptedConn != null) { + * interceptedConn.close(); + * } + * } + */ + + try { + Properties props = new Properties(); + props.setProperty("statementInterceptors", ServerStatusDiffInterceptor.class.getName()); + + interceptedConn = getConnectionWithProps(props); + this.rs = interceptedConn.createStatement().executeQuery("SELECT 'abc'"); + } finally { + if (interceptedConn != null) { + interceptedConn.close(); + } + } + } + + public void testParameterBindings() throws Exception { + // Need to check character set stuff, so need a new connection + Connection utfConn = getConnectionWithProps("characterEncoding=utf-8,treatUtilDateAsTimestamp=false,autoDeserialize=true"); + + java.util.Date now = new java.util.Date(); + + Object[] valuesToTest = new Object[] { new Byte(Byte.MIN_VALUE), new Short(Short.MIN_VALUE), new Integer(Integer.MIN_VALUE), new Long(Long.MIN_VALUE), + new Double(Double.MIN_VALUE), "\u4E2D\u6587", new BigDecimal(Math.PI), null, // to test isNull + now // to test serialization + }; + + StringBuilder statementText = new StringBuilder("SELECT ?"); + + for (int i = 1; i < valuesToTest.length; i++) { + statementText.append(",?"); + } + + this.pstmt = utfConn.prepareStatement(statementText.toString()); + + for (int i = 0; i < valuesToTest.length; i++) { + this.pstmt.setObject(i + 1, valuesToTest[i]); + } + + ParameterBindings bindings = ((com.mysql.jdbc.PreparedStatement) this.pstmt).getParameterBindings(); + + for (int i = 0; i < valuesToTest.length; i++) { + Object boundObject = bindings.getObject(i + 1); + + if (boundObject == null || valuesToTest[i] == null) { + continue; + } + + Class boundObjectClass = boundObject.getClass(); + Class testObjectClass = valuesToTest[i].getClass(); + + if (boundObject instanceof Number) { + assertEquals("For binding #" + (i + 1) + " of class " + boundObjectClass + " compared to " + testObjectClass, boundObject.toString(), + valuesToTest[i].toString()); + } else if (boundObject instanceof Date) { + + } else { + assertEquals("For binding #" + (i + 1) + " of class " + boundObjectClass + " compared to " + testObjectClass, boundObject, valuesToTest[i]); + } + } + } + + public void testLocalInfileHooked() throws Exception { + createTable("localInfileHooked", "(field1 int, field2 varchar(255))"); + String streamData = "1\tabcd\n2\tefgh\n3\tijkl"; + InputStream stream = new ByteArrayInputStream(streamData.getBytes()); + try { + ((com.mysql.jdbc.Statement) this.stmt).setLocalInfileInputStream(stream); + this.stmt.execute("LOAD DATA LOCAL INFILE 'bogusFileName' INTO TABLE localInfileHooked CHARACTER SET " + + CharsetMapping.getMysqlCharsetForJavaEncoding(((MySQLConnection) this.conn).getEncoding(), (com.mysql.jdbc.Connection) this.conn)); + assertEquals(-1, stream.read()); + this.rs = this.stmt.executeQuery("SELECT field2 FROM localInfileHooked ORDER BY field1 ASC"); + this.rs.next(); + assertEquals("abcd", this.rs.getString(1)); + this.rs.next(); + assertEquals("efgh", this.rs.getString(1)); + this.rs.next(); + assertEquals("ijkl", this.rs.getString(1)); + } finally { + ((com.mysql.jdbc.Statement) this.stmt).setLocalInfileInputStream(null); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/StringUtilsTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/StringUtilsTest.java new file mode 100644 index 0000000..6929769 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/StringUtilsTest.java @@ -0,0 +1,820 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.nio.charset.Charset; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.StringUtils.SearchMode; + +import testsuite.BaseTestCase; + +public class StringUtilsTest extends BaseTestCase { + /** + * Creates a new StringUtilsTest. + * + * @param name + * the name of the test + */ + public StringUtilsTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(StringUtilsTest.class); + } + + /** + * Tests StringUtil.indexOfIgnoreCase() methods + * + * @throws Exception + */ + public void testIndexOfIgnoreCase() throws Exception { + final String markerStart = "\"'`("; + final String markerEnd = "\"'`)"; + + String searchIn; + String[] searchInMulti; + String searchFor; + String[] searchForMulti; + int[] expectedIdx; + + int pos; + Set searchMode; + + /* + * A. test indexOfIgnoreCase(String searchIn, String searchFor) + */ + // basic test set + assertEquals(-1, StringUtils.indexOfIgnoreCase(null, null)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(null, "abc")); + assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", null)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", "")); + assertEquals(-1, StringUtils.indexOfIgnoreCase("abc", "bcd")); + assertEquals(0, StringUtils.indexOfIgnoreCase("abc", "abc")); + assertEquals(3, StringUtils.indexOfIgnoreCase("abc d efg", " d ")); + + // exhaustive test set + searchIn = "A strange STRONG SsStRiNg to be searched in"; + searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" }; + expectedIdx = new int[] { 2, 18, -1, 0, 40, 29 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test A." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(searchIn, searchForMulti[i])); + } + + /* + * B. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor) + */ + // basic test set + assertEquals(-1, StringUtils.indexOfIgnoreCase(3, null, null)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(3, null, "abc")); + assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", null)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", "")); + assertEquals(-1, StringUtils.indexOfIgnoreCase(3, "abc", "bcd")); + assertEquals(3, StringUtils.indexOfIgnoreCase(3, "abcabc", "abc")); + assertEquals(3, StringUtils.indexOfIgnoreCase("abc d efg", " d ")); + + // exhaustive test set + searchIn = "A strange STRONG SsStRiNg to be searched in"; + searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" }; + expectedIdx = new int[] { 10, 18, -1, -1, 40, 29 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test B." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(3, searchIn, searchForMulti[i])); + } + + /* + * C. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set + * searchMode) using search modes SEARCH_MODE__BSESC_MRK_WS or SEARCH_MODE__MRK_WS + */ + // basic test set + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, (String) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "bcd", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(6, StringUtils.indexOfIgnoreCase(0, "ab -- abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(5, StringUtils.indexOfIgnoreCase(0, "ab # abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/*/* /c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/**/c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(5, StringUtils.indexOfIgnoreCase(0, "ab/* abc */c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d efg", " d ", markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + + // exhaustive test set + searchInMulti = new String[] { "A \"strange \"STRONG SsStRiNg to be searched in", "A 'strange 'STRONG SsStRiNg to be searched in", + "A `strange `STRONG SsStRiNg to be searched in", "A (strange )STRONG SsStRiNg to be searched in" }; + searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" }; + expectedIdx = new int[] { 12, 20, -1, -1, 42, 31 }; + for (int i = 0; i < searchForMulti.length; i++) { + for (int j = 0; j < searchInMulti.length; j++) { + // multiple markers + assertEquals("Test C." + j + "." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart, markerEnd, StringUtils.SEARCH_MODE__MRK_WS)); + assertEquals("Test C." + j + "." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + + // single marker + assertEquals("Test C." + j + "." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], + markerStart.substring(j, j + 1), markerEnd.substring(j, j + 1), StringUtils.SEARCH_MODE__MRK_WS)); + assertEquals("Test C." + j + "." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchInMulti[j], searchForMulti[i], + markerStart.substring(j, j + 1), markerEnd.substring(j, j + 1), StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + } + } + + searchIn = "A (`'\"strange \"'`)STRONG SsStRiNg to be searched in"; + searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" }; + expectedIdx = new int[] { 18, 26, -1, -1, 48, 37 }; + for (int i = 0; i < searchForMulti.length; i++) { + // multiple markers + assertEquals("Test C.4." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + // single marker + assertEquals("Test C.5." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], "'", "'", StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + } + + searchIn = "A 'strange \\''STRONG \\`SsSTRING\\\" to be searched in"; + searchForMulti = new String[] { "STR", "sstr", "Z", "a str", " in", "b" }; + expectedIdx = new int[] { 14, 24, -1, -1, 48, 37 }; + for (int i = 0; i < searchForMulti.length; i++) { + // multiple markers + assertEquals("Test C.6." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + // single marker + assertEquals("Test C.7." + i, expectedIdx[i], + StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], "'", "'", StringUtils.SEARCH_MODE__BSESC_MRK_WS)); + } + + /* + * D. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set + * searchMode) using combined and single search modes + */ + // basic test set + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, (String) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", "bcd", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab -- abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab # abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/*/* /c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/**/c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "ab/* abc */c", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", "abc", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d efg", " d ", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + + // exhaustive test set + searchIn = "/* MySQL01 *//* MySQL02 */ \"MySQL03\" /* MySQL04 */-- MySQL05\n/* MySQL06 *//* MySQL07 */ 'MySQL08' /* MySQL09 */-- # MySQL10\r\n" + + "/* MySQL11 *//* MySQL12 */ `MySQL13` /* MySQL14 */# MySQL15\r\n/* MySQL16 *//* MySQL17 */ (MySQL18) /* MySQL19 */# -- MySQL20 \n" + + "/* MySQL21 *//* MySQL22 */ \\MySQL23--;/*! MySQL24 */ MySQL25 --"; + searchFor = "mYSql"; + + // 1. different markers in method arguments + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, null, null, StringUtils.SEARCH_MODE__BSESC_COM_WS); + assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "", "", StringUtils.SEARCH_MODE__ALL); + assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "'`(", "'`)", StringUtils.SEARCH_MODE__ALL); + assertEquals(3, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "\"`(", "\"`)", StringUtils.SEARCH_MODE__ALL); + assertEquals(8, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "\"'(", "\"')", StringUtils.SEARCH_MODE__ALL); + assertEquals(13, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, "\"'`", "\"'`", StringUtils.SEARCH_MODE__ALL); + assertEquals(18, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + + // 2a. search mode: all but skip markers + searchMode = StringUtils.SEARCH_MODE__BSESC_COM_WS; + pos = 0; + expectedIdx = new int[] { 3, 8, 13, 18, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.2a." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + // 2b. search mode: only skip markers + searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS); + pos = 0; + expectedIdx = new int[] { 1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.2b." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + + // 3a. search mode: all but skip line comments + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS, + SearchMode.SKIP_WHITE_SPACE); + pos = 0; + expectedIdx = new int[] { 5, 10, 15, 20, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.3a." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + // 3b. search mode: only skip line comments + searchMode = EnumSet.of(SearchMode.SKIP_LINE_COMMENTS); + pos = 0; + expectedIdx = new int[] { 1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.3b." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + + // 4a. search mode: all but skip block comments + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE); + pos = 0; + expectedIdx = new int[] { 1, 2, 4, 6, 7, 9, 11, 12, 14, 16, 17, 19, 21, 22, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.4a." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + // 4b. search mode: only skip block comments + searchMode = EnumSet.of(SearchMode.SKIP_BLOCK_COMMENTS); + pos = 0; + expectedIdx = new int[] { 3, 5, 8, 10, 13, 15, 18, 20, 23, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.4b." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + + // 5a. search mode: all but allow backslash escape + pos = StringUtils.indexOfIgnoreCase(0, searchIn, searchFor, markerStart, markerEnd, StringUtils.SEARCH_MODE__MRK_COM_WS); + assertEquals(23, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + // 5b. search mode: only allow backslash escape + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE); + pos = 0; + expectedIdx = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, searchMode); + assertEquals("Test D.5b." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + + // 6. all together + pos = 0; + expectedIdx = new int[] { 24, 25, -1 }; + for (int i = 0; i < expectedIdx.length; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL); + assertEquals("Test D.6." + i, expectedIdx[i], testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + pos = StringUtils.indexOfIgnoreCase(0, searchIn, "YourSQL", markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL); + assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + + // 7. none + pos = 0; + for (int i = 1; i <= 25; i++, pos++) { + pos = StringUtils.indexOfIgnoreCase(pos, searchIn, searchFor, markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE); + assertEquals("Test D.7." + i, i, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + } + pos = StringUtils.indexOfIgnoreCase(pos + 1, searchIn, searchFor, markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE); + assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + pos = StringUtils.indexOfIgnoreCase(0, searchIn, "YourSQL", markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE); + assertEquals(-1, testIndexOfIgnoreCaseMySQLIndexMarker(searchIn, pos)); + + /* + * E. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set + * searchMode) illegal markers arguments + */ + assertThrows(IllegalArgumentException.class, + "Illegal argument value null for openingMarkers and/or - for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", "abc", null, "-", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value - for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value null for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", "abc", null, null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value - for openingMarkers and/or -! for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", "abc", "-", "-!", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + + /* + * F. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor, String openingMarkers, String closingMarkers, Set + * searchMode) special cases + */ + // unclosed, unopened or nested block comments + searchMode = EnumSet.of(SearchMode.SKIP_BLOCK_COMMENTS); + searchIn = "one * /* two /* * / three /*/*/ four * /"; + searchForMulti = new String[] { "one", "two", "three", "four" }; + expectedIdx = new int[] { 0, -1, -1, 32 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.1." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE); + expectedIdx = new int[] { 0, 9, 20, 32 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.2." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + + // double quoted escapes, including some "noise" chars + searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS); + searchIn = "one 'two\" ''three''' four\""; + searchForMulti = new String[] { "one", "two", "three", "four" }; + expectedIdx = new int[] { 0, -1, -1, 21 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.3." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + searchMode = StringUtils.SEARCH_MODE__BSESC_COM_WS; + expectedIdx = new int[] { 0, 5, 12, 21 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.4." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + + // nested different opening/closing marker, including some "noise" chars + searchMode = EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS); + searchIn = "one (two\"( (three''')) )four)"; + searchForMulti = new String[] { "one", "two", "three", "four" }; + expectedIdx = new int[] { 0, -1, -1, 24 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.5." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + searchMode = StringUtils.SEARCH_MODE__BSESC_COM_WS; + expectedIdx = new int[] { 0, 5, 12, 24 }; + for (int i = 0; i < searchForMulti.length; i++) { + assertEquals("Test F.6." + i, expectedIdx[i], StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti[i], markerStart, markerEnd, searchMode)); + } + + /* + * G. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor[], String openingMarkers, String closingMarkers, Set + * searchMode) using all combined search modes + */ + // basic test set + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, (String[]) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, null, new String[] { "abc" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", (String[]) null, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", new String[] {}, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "", "" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "abc -- d", new String[] { "c", "d" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(0, StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, + StringUtils.indexOfIgnoreCase(0, "abc d efg h", new String[] { " d ", " efg" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(3, StringUtils.indexOfIgnoreCase(0, "abc d efg h", new String[] { " d ", "efg" }, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + + // exhaustive test set + searchForMulti = new String[] { "ONE", "two", "ThrEE" }; + + // 1. simple strings + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "onetwothee", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(0, "one one one one two", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(11, StringUtils.indexOfIgnoreCase(0, "onetwothee one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(20, + StringUtils.indexOfIgnoreCase(0, "/* one two three */ one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(38, StringUtils.indexOfIgnoreCase(0, "/* one two three *//* one two three */one two three", searchForMulti, markerStart, markerEnd, + StringUtils.SEARCH_MODE__ALL)); + assertEquals(7, + StringUtils.indexOfIgnoreCase(0, "/*one*/one/*two*/two/*three*/three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(0, + StringUtils.indexOfIgnoreCase(0, "one/*one*/two/*two*/three/*three*/", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(16, + StringUtils.indexOfIgnoreCase(0, "# one two three\none two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(17, + StringUtils.indexOfIgnoreCase(0, "-- one two three\none two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(22, + StringUtils.indexOfIgnoreCase(0, "/* one two three */--;one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(4, + StringUtils.indexOfIgnoreCase(0, "/*! one two three */--;one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(9, StringUtils.indexOfIgnoreCase(0, "/*!50616 one two three */--;one two three", searchForMulti, markerStart, markerEnd, + StringUtils.SEARCH_MODE__ALL)); + assertEquals(16, + StringUtils.indexOfIgnoreCase(0, "\"one two three\" one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(16, + StringUtils.indexOfIgnoreCase(0, "'one two three' one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(16, + StringUtils.indexOfIgnoreCase(0, "`one two three` one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + assertEquals(16, + StringUtils.indexOfIgnoreCase(0, "(one two three) one two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + + assertEquals(3, StringUtils.indexOfIgnoreCase(0, "/* one two three */ one two three", searchForMulti, markerStart, markerEnd, + StringUtils.SEARCH_MODE__NONE)); + assertEquals(2, + StringUtils.indexOfIgnoreCase(0, "# one two three\none two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE)); + assertEquals(3, + StringUtils.indexOfIgnoreCase(0, "-- one two three\none two three", searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE)); + + // 2. complex string + searchIn = "/* one two three *//* one two three */ one 'two' three -- \"one/* one */two three\" one owt three\n" + + "onetwothree # 'one/* one */two three' one owt three\noneone /* two *//* three */ -- `one/* one */two three` one two three\n" + + "two -- three\n\n\n/* three */threethree"; + + printRuler(searchIn); + // 2.1. skip all + assertEquals(159, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__ALL)); + // 2.2. search within block comments + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_LINE_COMMENTS, SearchMode.SKIP_WHITE_SPACE); + assertEquals(3, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, searchMode)); + assertEquals(3, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, StringUtils.SEARCH_MODE__NONE)); + // 2.3. search within line comments and unidentified markers + searchMode = EnumSet.of(SearchMode.ALLOW_BACKSLASH_ESCAPE, SearchMode.SKIP_BETWEEN_MARKERS, SearchMode.SKIP_BLOCK_COMMENTS, + SearchMode.SKIP_WHITE_SPACE); + assertEquals(61, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "'`(", "'`)", searchMode)); + assertEquals(116, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "\"`(", "\"`)", searchMode)); + assertEquals(188, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, "\"'(", "\"')", searchMode)); + assertEquals(212, StringUtils.indexOfIgnoreCase(0, searchIn, searchForMulti, markerStart, markerEnd, searchMode)); + + /* + * H. test indexOfIgnoreCase(int startingPosition, String searchIn, String searchFor[], String openingMarkers, String closingMarkers, Set + * searchMode) illegal markers arguments + */ + assertThrows(IllegalArgumentException.class, + "Illegal argument value null for openingMarkers and/or - for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, null, "-", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value - for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, "-", null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value null for openingMarkers and/or null for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, null, null, EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + assertThrows(IllegalArgumentException.class, + "Illegal argument value - for openingMarkers and/or -! for closingMarkers. These cannot be null and must have the same length.", + new Callable() { + public Void call() throws Exception { + StringUtils.indexOfIgnoreCase(0, "abc", new String[] { "abc" }, "-", "-!", EnumSet.of(SearchMode.SKIP_BETWEEN_MARKERS)); + return null; + } + }); + } + + private int testIndexOfIgnoreCaseMySQLIndexMarker(String source, int pos) { + return pos == -1 ? -1 : Integer.parseInt(source.substring(pos + 5, pos + 7)); + } + + private static void printRuler(String txt) { + System.out.printf(" 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100%n"); + System.out.printf(" |----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|%n"); + + int count = 0; + for (String line : txt.split("\n")) { + System.out.printf("%4d+ %s%n", count, line); + count += line.length() + 1; + } + } + + /** + * Tests StringUtil.indexOfQuoteDoubleAware() method + * + * @throws Exception + */ + public void testIndexOfQuoteDoubleAware() throws Exception { + final String[] searchInDoubledQt = new String[] { "A 'strange' \"STRONG\" `SsStRiNg` to be searched in", + "A ''strange'' \"\"STRONG\"\" ``SsStRiNg`` to be searched in" }; + + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(null, null, 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(null, "'", 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware("abc", null, 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware("abc", "", 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware("abc", "bcd", 0)); + assertEquals(0, StringUtils.indexOfQuoteDoubleAware("abc", "abc", 0)); + + int qtPos = 0; + assertEquals(2, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "'", 0)); + assertEquals(10, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "'", qtPos + 1)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "'", qtPos + 1)); + assertEquals(12, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "\"", 0)); + assertEquals(19, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "\"", qtPos + 1)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "\"", qtPos + 1)); + assertEquals(21, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "`", 0)); + assertEquals(30, qtPos = StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "`", qtPos + 1)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[0], "`", qtPos + 1)); + + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], "'", 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], "\"", 0)); + assertEquals(-1, StringUtils.indexOfQuoteDoubleAware(searchInDoubledQt[1], "`", 0)); + } + + /** + * Tests StringUtil.appendAsHex() methods. + * + * @throws Exception + */ + public void testAppendAsHex() throws Exception { + final byte[] testBytes = new byte[256]; + final int[] testInts = new int[] { Integer.MIN_VALUE, -1023, 0, 511, 512, 0x100FF, 0x10000FF, Integer.MAX_VALUE }; + StringBuilder builder; + + for (int i = 0; i < 256; i++) { + testBytes[i] = (byte) i; + } + + // test StringUtils.appendAsHex(StringBuilder, byte[]) + builder = new StringBuilder(1024); + builder.append("0x"); + for (byte b : testBytes) { + builder.append(String.format("%02x", b)); + } + String expected = builder.toString(); + + builder = new StringBuilder(1024); + StringUtils.appendAsHex(builder, testBytes); + + assertEquals("Wrong byte[] to HEX convertion", expected, builder.toString()); + + // test StringUtils.appendAsHex(StringBuilder, int) + for (int i : testInts) { + builder = new StringBuilder(1024); + StringUtils.appendAsHex(builder, i); + assertEquals("Wrong int to HEX convertion", "0x" + Integer.toHexString(i), builder.toString()); + } + } + + /** + * Tests StringUtil.getBytes() methods. + * + * @throws Exception + */ + public void testGetBytes() throws Exception { + final int offset = 8; + final int length = 13; + final String text = "MySQL ≠𝄞 for my ears"; + final String textPart = text.substring(offset, offset + length); + final String textWrapped = "`MySQL ≠𝄞 for my ears`"; + final char[] textAsCharArray = text.toCharArray(); + + byte[] asBytesFromString; + byte[] asBytesFromStringUtils; + + asBytesFromString = text.getBytes(); + asBytesFromStringUtils = StringUtils.getBytes(text); + assertByteArrayEquals("Default Charset: " + Charset.defaultCharset().name(), asBytesFromString, asBytesFromStringUtils); + + asBytesFromString = textPart.getBytes(); + asBytesFromStringUtils = StringUtils.getBytes(text, offset, length); + assertByteArrayEquals("Default Charset: " + Charset.defaultCharset().name(), asBytesFromString, asBytesFromStringUtils); + + Map charsetMap = Charset.availableCharsets(); + for (Charset cs : charsetMap.values()) { + if (cs.canEncode()) { + asBytesFromString = text.getBytes(cs.name()); + + asBytesFromStringUtils = StringUtils.getBytes(text, cs.name()); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, cs.name()); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + + asBytesFromStringUtils = StringUtils.getBytes(text, null, cs.name(), null, true, null); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, null, cs.name(), null, true, null); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + + asBytesFromString = textPart.getBytes(cs.name()); + + asBytesFromStringUtils = StringUtils.getBytes(text, offset, length, cs.name()); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, offset, length, cs.name()); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + + asBytesFromStringUtils = StringUtils.getBytes(text, null, cs.name(), null, offset, length, true, null); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + asBytesFromStringUtils = StringUtils.getBytes(textAsCharArray, null, cs.name(), null, offset, length, true, null); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + + asBytesFromString = textWrapped.getBytes(cs.name()); + + asBytesFromStringUtils = StringUtils.getBytesWrapped(text, '`', '`', null, cs.name(), null, true, null); + assertByteArrayEquals("Custom Charset: " + cs.name(), asBytesFromString, asBytesFromStringUtils); + } + } + } + + /** + * Tests StringUtil.quoteIdentifier() and StringUtil.unQuoteIdentifier() methods using back quote marks. + * + * @throws Exception + */ + public void testQuoteUnQuoteIdentifierWithBackQuote() throws Exception { + // Base set of identifiers + String[] identifiers = new String[] { "abcxyz", "abc`xyz", "abc``xyz", "abc```xyz", // 1..4 + "`abcxyz`", "`abc`xyz`", "`abc``xyz`", "`abc```xyz`", // 5..8 + "``abcxyz``", "``abc`xyz``", "``abc``xyz``", "``abc```xyz``", // 9..12 + "```abcxyz```", "```abc`xyz```", "```abc``xyz```", "```abc```xyz```", // 13..16 + "`abcxyz", "``abcxyz", "```abcxyz", "abcxyz`", "abcxyz``", "abcxyz```", // 17..22 + "``abcxyz`", "``abc`xyz`", "``abc``xyz`", "``abc```xyz`", // 23..26 + "```abcxyz`", "```abc`xyz`", "```abc``xyz`", "```abc```xyz`", // 27..30 + "`abcxyz``", "`abc`xyz``", "`abc``xyz``", "`abc```xyz``", // 31..34 + "`abcxyz```", "`abc`xyz```", "`abc``xyz```", "`abc```xyz```" // 35..38 + }; + + // Identifiers unquoted + String[] identifiersUnQuoted = new String[] { "abcxyz", "abc`xyz", "abc``xyz", "abc```xyz", // 1..4 + "abcxyz", "`abc`xyz`", "abc`xyz", "`abc```xyz`", // 5..8 + "``abcxyz``", "``abc`xyz``", "``abc``xyz``", "``abc```xyz``", // 9..12 + "`abcxyz`", "```abc`xyz```", "`abc`xyz`", "```abc```xyz```", // 13..16 + "`abcxyz", "``abcxyz", "```abcxyz", "abcxyz`", "abcxyz``", "abcxyz```", // 17..22 + "``abcxyz`", "``abc`xyz`", "``abc``xyz`", "``abc```xyz`", // 23..26 + "`abcxyz", "```abc`xyz`", "`abc`xyz", "```abc```xyz`", // 27..30 + "`abcxyz``", "`abc`xyz``", "`abc``xyz``", "`abc```xyz``", // 31..34 + "abcxyz`", "`abc`xyz```", "abc`xyz`", "`abc```xyz```" // 35..38 + }; + + // Identifiers quoted in non-pedantic mode + String[] identifiersQuotedNonPedantic = new String[] { "`abcxyz`", "`abc``xyz`", "`abc````xyz`", "`abc``````xyz`", // 1..4 + "`abcxyz`", "```abc``xyz```", "`abc``xyz`", "```abc``````xyz```", // 5..8 + "`````abcxyz`````", "`````abc``xyz`````", "`````abc````xyz`````", "`````abc``````xyz`````", // 9..12 + "```abcxyz```", "```````abc``xyz```````", "```abc``xyz```", "```````abc``````xyz```````", // 13..16 + "```abcxyz`", "`````abcxyz`", "```````abcxyz`", "`abcxyz```", "`abcxyz`````", "`abcxyz```````", // 17..22 + "`````abcxyz```", "`````abc``xyz```", "`````abc````xyz```", "`````abc``````xyz```", // 23..26 + "```abcxyz`", "```````abc``xyz```", "```abc``xyz`", "```````abc``````xyz```", // 27..30 + "```abcxyz`````", "```abc``xyz`````", "```abc````xyz`````", "```abc``````xyz`````", // 31..34 + "`abcxyz```", "```abc``xyz```````", "`abc``xyz```", "```abc``````xyz```````" // 35..38 + }; + + // Identifiers quoted in pedantic mode + String[] identifiersQuotedPedantic = new String[] { "`abcxyz`", "`abc``xyz`", "`abc````xyz`", "`abc``````xyz`", // 1..4 + "```abcxyz```", "```abc``xyz```", "```abc````xyz```", "```abc``````xyz```", // 5..8 + "`````abcxyz`````", "`````abc``xyz`````", "`````abc````xyz`````", "`````abc``````xyz`````", // 9..12 + "```````abcxyz```````", "```````abc``xyz```````", "```````abc````xyz```````", "```````abc``````xyz```````", // 13..16 + "```abcxyz`", "`````abcxyz`", "```````abcxyz`", "`abcxyz```", "`abcxyz`````", "`abcxyz```````", // 17..22 + "`````abcxyz```", "`````abc``xyz```", "`````abc````xyz```", "`````abc``````xyz```", // 23..26 + "```````abcxyz```", "```````abc``xyz```", "```````abc````xyz```", "```````abc``````xyz```", // 27..30 + "```abcxyz`````", "```abc``xyz`````", "```abc````xyz`````", "```abc``````xyz`````", // 31..34 + "```abcxyz```````", "```abc``xyz```````", "```abc````xyz```````", "```abc``````xyz```````" // 35..38 + }; + + // Quoting rules (non-pedantic mode): + // * identifiers[n] --> identifiersQuotedNonPedantic[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". non-pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiers[i], "`", false)); + assertEquals(i + 1 + ". " + identifiers[i] + ". non-pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiers[i], false)); + } + + // Quoting rules (pedantic mode): + // * identifiers[n] --> identifiersQuotedPedantic[n] + // * identifiersUnQuoted[n] --> identifiersQuotedNonPedantic[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". pedantic quoting", identifiersQuotedPedantic[i], + StringUtils.quoteIdentifier(identifiers[i], "`", true)); + assertEquals(i + 1 + ". " + identifiers[i] + ". pedantic quoting", identifiersQuotedPedantic[i], StringUtils.quoteIdentifier(identifiers[i], true)); + + assertEquals(i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiersUnQuoted[i], "`", true)); + assertEquals(i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiersUnQuoted[i], true)); + } + + // Unquoting rules: + // * identifiers[n] --> identifiersUnQuoted[n] + // * identifiersQuotedNonPedantic[n] --> identifiersUnQuoted[n] + // * identifiersQuotedPedantic[n] --> identifiers[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". unquoting", identifiersUnQuoted[i], StringUtils.unQuoteIdentifier(identifiers[i], "`")); + assertEquals(i + 1 + ". " + identifiersQuotedNonPedantic[i] + ". non-pedantic unquoting", identifiersUnQuoted[i], + StringUtils.unQuoteIdentifier(identifiersQuotedNonPedantic[i], "`")); + assertEquals(i + 1 + ". " + identifiersQuotedPedantic[i] + ". pedantic unquoting", identifiers[i], + StringUtils.unQuoteIdentifier(identifiersQuotedPedantic[i], "`")); + } + } + + /** + * Tests StringUtil.quoteIdentifier() and StringUtil.unQuoteIdentifier() methods using double quote marks. + * + * @throws Exception + */ + public void testQuoteUnQuoteIdentifierWithDoubleQuote() throws Exception { + // Base set of identifiers + String[] identifiers = new String[] { "abcxyz", "abc\"xyz", "abc\"\"xyz", "abc\"\"\"xyz", // 1..4 + "\"abcxyz\"", "\"abc\"xyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"xyz\"", // 5..8 + "\"\"abcxyz\"\"", "\"\"abc\"xyz\"\"", "\"\"abc\"\"xyz\"\"", "\"\"abc\"\"\"xyz\"\"", // 9..12 + "\"\"\"abcxyz\"\"\"", "\"\"\"abc\"xyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"xyz\"\"\"", // 13..16 + "\"abcxyz", "\"\"abcxyz", "\"\"\"abcxyz", "abcxyz\"", "abcxyz\"\"", "abcxyz\"\"\"", // 17..22 + "\"\"abcxyz\"", "\"\"abc\"xyz\"", "\"\"abc\"\"xyz\"", "\"\"abc\"\"\"xyz\"", // 23..26 + "\"\"\"abcxyz\"", "\"\"\"abc\"xyz\"", "\"\"\"abc\"\"xyz\"", "\"\"\"abc\"\"\"xyz\"", // 27..30 + "\"abcxyz\"\"", "\"abc\"xyz\"\"", "\"abc\"\"xyz\"\"", "\"abc\"\"\"xyz\"\"", // 31..34 + "\"abcxyz\"\"\"", "\"abc\"xyz\"\"\"", "\"abc\"\"xyz\"\"\"", "\"abc\"\"\"xyz\"\"\"" // 35..38 + }; + + // Identifiers unquoted + String[] identifiersUnQuoted = new String[] { "abcxyz", "abc\"xyz", "abc\"\"xyz", "abc\"\"\"xyz", // 1..4 + "abcxyz", "\"abc\"xyz\"", "abc\"xyz", "\"abc\"\"\"xyz\"", // 5..8 + "\"\"abcxyz\"\"", "\"\"abc\"xyz\"\"", "\"\"abc\"\"xyz\"\"", "\"\"abc\"\"\"xyz\"\"", // 9..12 + "\"abcxyz\"", "\"\"\"abc\"xyz\"\"\"", "\"abc\"xyz\"", "\"\"\"abc\"\"\"xyz\"\"\"", // 13..16 + "\"abcxyz", "\"\"abcxyz", "\"\"\"abcxyz", "abcxyz\"", "abcxyz\"\"", "abcxyz\"\"\"", // 17..22 + "\"\"abcxyz\"", "\"\"abc\"xyz\"", "\"\"abc\"\"xyz\"", "\"\"abc\"\"\"xyz\"", // 23..26 + "\"abcxyz", "\"\"\"abc\"xyz\"", "\"abc\"xyz", "\"\"\"abc\"\"\"xyz\"", // 27..30 + "\"abcxyz\"\"", "\"abc\"xyz\"\"", "\"abc\"\"xyz\"\"", "\"abc\"\"\"xyz\"\"", // 31..34 + "abcxyz\"", "\"abc\"xyz\"\"\"", "abc\"xyz\"", "\"abc\"\"\"xyz\"\"\"" // 35..38 + }; + + // Identifiers quoted in non-pedantic mode + String[] identifiersQuotedNonPedantic = new String[] { "\"abcxyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"\"xyz\"", "\"abc\"\"\"\"\"\"xyz\"", // 1..4 + "\"abcxyz\"", "\"\"\"abc\"\"xyz\"\"\"", "\"abc\"\"xyz\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // 5..8 + "\"\"\"\"\"abcxyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"\"\"", // 9.. + "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"", // ..12 + "\"\"\"abcxyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"\"\"\"\"", // 13.. + "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"", // ..16 + "\"\"\"abcxyz\"", "\"\"\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abcxyz\"", "\"abcxyz\"\"\"", "\"abcxyz\"\"\"\"\"", "\"abcxyz\"\"\"\"\"\"\"", // 17..22 + "\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // 23..26 + "\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"xyz\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // 27..30 + "\"\"\"abcxyz\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"", // 31..34 + "\"abcxyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"\"\"", "\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"" // 35..38 + }; + + // Identifiers quoted in pedantic mode + String[] identifiersQuotedPedantic = new String[] { "\"abcxyz\"", "\"abc\"\"xyz\"", "\"abc\"\"\"\"xyz\"", "\"abc\"\"\"\"\"\"xyz\"", // 1..4 + "\"\"\"abcxyz\"\"\"", "\"\"\"abc\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // 5..8 + "\"\"\"\"\"abcxyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"\"\"", // 9.. + "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"", // ..12 + "\"\"\"\"\"\"\"abcxyz\"\"\"\"\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"\"\"\"\"", // 13.. + "\"\"\"\"\"\"\"abc\"\"\"\"xyz\"\"\"\"\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"", // ..16 + "\"\"\"abcxyz\"", "\"\"\"\"\"abcxyz\"", "\"\"\"\"\"\"\"abcxyz\"", "\"abcxyz\"\"\"", "\"abcxyz\"\"\"\"\"", "\"abcxyz\"\"\"\"\"\"\"", // 17..22 + "\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"abc\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // 23..26 + "\"\"\"\"\"\"\"abcxyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"xyz\"\"\"", // 27.. + "\"\"\"\"\"\"\"abc\"\"\"\"xyz\"\"\"", "\"\"\"\"\"\"\"abc\"\"\"\"\"\"xyz\"\"\"", // ..30 + "\"\"\"abcxyz\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"", // 31..34 + "\"\"\"abcxyz\"\"\"\"\"\"\"", "\"\"\"abc\"\"xyz\"\"\"\"\"\"\"", // 35.. + "\"\"\"abc\"\"\"\"xyz\"\"\"\"\"\"\"", "\"\"\"abc\"\"\"\"\"\"xyz\"\"\"\"\"\"\"" // ..38 + }; + + // Quoting rules (non-pedantic mode): + // * identifiers[n] --> identifiersQuotedNonPedantic[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". non-pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiers[i], "\"", false)); + } + + // Quoting rules (pedantic mode): + // * identifiers[n] --> identifiersQuotedPedantic[n] + // * identifiersUnQuoted[n] --> identifiersQuotedNonPedantic[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". pedantic quoting", identifiersQuotedPedantic[i], + StringUtils.quoteIdentifier(identifiers[i], "\"", true)); + + assertEquals(i + 1 + ". " + identifiersUnQuoted[i] + ". pedantic quoting", identifiersQuotedNonPedantic[i], + StringUtils.quoteIdentifier(identifiersUnQuoted[i], "\"", true)); + } + + // Unquoting rules: + // * identifiers[n] --> identifiersUnQuoted[n] + // * identifiersQuotedNonPedantic[n] --> identifiersUnQuoted[n] + // * identifiersQuotedPedantic[n] --> identifiers[n] + for (int i = 0; i < identifiers.length; i++) { + assertEquals(i + 1 + ". " + identifiers[i] + ". unquoting", identifiersUnQuoted[i], StringUtils.unQuoteIdentifier(identifiers[i], "\"")); + assertEquals(i + 1 + ". " + identifiersQuotedNonPedantic[i] + ". non-pedantic unquoting", identifiersUnQuoted[i], + StringUtils.unQuoteIdentifier(identifiersQuotedNonPedantic[i], "\"")); + assertEquals(i + 1 + ". " + identifiersQuotedPedantic[i] + ". pedantic unquoting", identifiers[i], + StringUtils.unQuoteIdentifier(identifiersQuotedPedantic[i], "\"")); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/TestBug57662Logger.java b/mysql-connector-java-5.1.40/src/testsuite/simple/TestBug57662Logger.java new file mode 100644 index 0000000..f02e73d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/TestBug57662Logger.java @@ -0,0 +1,44 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import com.mysql.jdbc.log.StandardLogger; +import com.mysql.jdbc.profiler.ProfilerEvent; + +public class TestBug57662Logger extends StandardLogger { + + public boolean hasNegativeDurations = false; + + public TestBug57662Logger(String name) { + super(name, false); + } + + @Override + protected void logInternal(int level, Object msg, Throwable exception) { + if (!this.hasNegativeDurations && msg instanceof ProfilerEvent) { + this.hasNegativeDurations = ((ProfilerEvent) msg).getEventDuration() < 0; + } + super.logInternal(level, msg, exception); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/TestLifecycleInterceptor.java b/mysql-connector-java-5.1.40/src/testsuite/simple/TestLifecycleInterceptor.java new file mode 100644 index 0000000..6c06ef8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/TestLifecycleInterceptor.java @@ -0,0 +1,75 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.SQLException; +import java.sql.Savepoint; +import java.util.Properties; + +import com.mysql.jdbc.ConnectionLifecycleInterceptor; + +public class TestLifecycleInterceptor implements ConnectionLifecycleInterceptor { + static int transactionsBegun = 0; + static int transactionsCompleted = 0; + + public void close() throws SQLException { + } + + public boolean commit() throws SQLException { + return true; + } + + public boolean rollback() throws SQLException { + return true; + } + + public boolean rollback(Savepoint s) throws SQLException { + return true; + } + + public boolean setAutoCommit(boolean flag) throws SQLException { + return true; + } + + public boolean setCatalog(String catalog) throws SQLException { + return true; + } + + public boolean transactionBegun() throws SQLException { + transactionsBegun++; + return true; + } + + public boolean transactionCompleted() throws SQLException { + transactionsCompleted++; + return true; + } + + public void destroy() { + } + + public void init(com.mysql.jdbc.Connection conn, Properties props) throws SQLException { + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/TransactionTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/TransactionTest.java new file mode 100644 index 0000000..4254351 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/TransactionTest.java @@ -0,0 +1,81 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.SQLException; + +import testsuite.BaseTestCase; + +public class TransactionTest extends BaseTestCase { + private static final double DOUBLE_CONST = 25.4312; + + private static final double EPSILON = .0000001; + + /** + * Creates a new TransactionTest object. + * + * @param name + */ + public TransactionTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(TransactionTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + } + + public void testTransaction() throws SQLException { + try { + createTable("trans_test", "(id INT NOT NULL PRIMARY KEY, decdata DOUBLE)", "InnoDB"); + this.conn.setAutoCommit(false); + this.stmt.executeUpdate("INSERT INTO trans_test (id, decdata) VALUES (1, 1.0)"); + this.conn.rollback(); + this.rs = this.stmt.executeQuery("SELECT * from trans_test"); + + boolean hasResults = this.rs.next(); + assertTrue("Results returned, rollback to empty table failed", (hasResults != true)); + this.stmt.executeUpdate("INSERT INTO trans_test (id, decdata) VALUES (2, " + DOUBLE_CONST + ")"); + this.conn.commit(); + this.rs = this.stmt.executeQuery("SELECT * from trans_test where id=2"); + hasResults = this.rs.next(); + assertTrue("No rows in table after INSERT", hasResults); + + double doubleVal = this.rs.getDouble(2); + double delta = Math.abs(DOUBLE_CONST - doubleVal); + assertTrue("Double value returned != " + DOUBLE_CONST, (delta < EPSILON)); + } finally { + this.conn.setAutoCommit(true); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/TraversalTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/TraversalTest.java new file mode 100644 index 0000000..01d13b8 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/TraversalTest.java @@ -0,0 +1,199 @@ +/* + Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import testsuite.BaseTestCase; + +/** + * Tests result set traversal methods. + */ +public class TraversalTest extends BaseTestCase { + + /** + * Creates a new TraversalTest object. + * + * @param name + */ + public TraversalTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(TraversalTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + createTestTable(); + } + + @Override + public void tearDown() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE TRAVERSAL"); + } catch (SQLException SQLE) { + } + super.tearDown(); + } + + public void testTraversal() throws SQLException { + + Statement scrollableStmt = null; + + try { + scrollableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); + this.rs = scrollableStmt.executeQuery("SELECT * FROM TRAVERSAL ORDER BY pos"); + + // Test isFirst() + if (this.rs.first()) { + assertTrue("ResultSet.isFirst() failed", this.rs.isFirst()); + this.rs.relative(-1); + assertTrue("ResultSet.isBeforeFirst() failed", this.rs.isBeforeFirst()); + } + + // Test isLast() + if (this.rs.last()) { + assertTrue("ResultSet.isLast() failed", this.rs.isLast()); + this.rs.relative(1); + assertTrue("ResultSet.isAfterLast() failed", this.rs.isAfterLast()); + } + + int count = 0; + this.rs.beforeFirst(); + + boolean forwardOk = true; + + while (this.rs.next()) { + + int pos = this.rs.getInt("POS"); + + // test case-sensitive column names + pos = this.rs.getInt("pos"); + pos = this.rs.getInt("Pos"); + pos = this.rs.getInt("POs"); + pos = this.rs.getInt("PoS"); + pos = this.rs.getInt("pOS"); + pos = this.rs.getInt("pOs"); + pos = this.rs.getInt("poS"); + + if (pos != count) { + forwardOk = false; + } + + assertTrue("ResultSet.getRow() failed.", pos == (this.rs.getRow() - 1)); + + count++; + + } + + assertTrue("Only traversed " + count + " / 100 rows", forwardOk); + + boolean isAfterLast = this.rs.isAfterLast(); + assertTrue("ResultSet.isAfterLast() failed", isAfterLast); + this.rs.afterLast(); + + // Scroll backwards + count = 99; + + boolean reverseOk = true; + + while (this.rs.previous()) { + + int pos = this.rs.getInt("pos"); + + if (pos != count) { + reverseOk = false; + } + + count--; + } + + assertTrue("ResultSet.previous() failed", reverseOk); + + boolean isBeforeFirst = this.rs.isBeforeFirst(); + assertTrue("ResultSet.isBeforeFirst() failed", isBeforeFirst); + + this.rs.next(); + boolean isFirst = this.rs.isFirst(); + assertTrue("ResultSet.isFirst() failed", isFirst); + + // Test absolute positioning + this.rs.absolute(50); + int pos = this.rs.getInt("pos"); + assertTrue("ResultSet.absolute() failed", pos == 49); + + // Test relative positioning + this.rs.relative(-1); + pos = this.rs.getInt("pos"); + assertTrue("ResultSet.relative(-1) failed", pos == 48); + + // Test bogus absolute index + boolean onResultSet = this.rs.absolute(200); + assertTrue("ResultSet.absolute() to point off result set failed", onResultSet == false); + onResultSet = this.rs.absolute(100); + assertTrue("ResultSet.absolute() from off this.rs to on this.rs failed", onResultSet); + + onResultSet = this.rs.absolute(-99); + assertTrue("ResultSet.absolute(-99) failed", onResultSet); + assertTrue("ResultSet absolute(-99) failed", this.rs.getInt(1) == 1); + } finally { + + if (scrollableStmt != null) { + + try { + scrollableStmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + } + } + } + + private void createTestTable() throws SQLException { + // + // Catch the error, the table might exist + // + try { + this.stmt.executeUpdate("DROP TABLE TRAVERSAL"); + } catch (SQLException SQLE) { + // ignore + } + + this.stmt.executeUpdate("CREATE TABLE TRAVERSAL (pos int PRIMARY KEY, stringdata CHAR(32))"); + + for (int i = 0; i < 100; i++) { + this.stmt.executeUpdate("INSERT INTO TRAVERSAL VALUES (" + i + ", 'StringData')"); + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/UpdatabilityTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/UpdatabilityTest.java new file mode 100644 index 0000000..3077a6d --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/UpdatabilityTest.java @@ -0,0 +1,324 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mysql.jdbc.NotUpdatable; + +import testsuite.BaseTestCase; + +/** + * Tests for updatable result sets + */ +public class UpdatabilityTest extends BaseTestCase { + /** + * Creates a new UpdatabilityTest object. + * + * @param name + */ + public UpdatabilityTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(UpdatabilityTest.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + createTestTable(); + } + + @Override + public void tearDown() throws Exception { + try { + this.stmt.executeUpdate("DROP TABLE UPDATABLE"); + } catch (SQLException SQLE) { + } + super.tearDown(); + } + + /** + * If using MySQL-4.1, tests if aliased tables work as updatable result + * sets. + * + * @throws Exception + * if an error occurs + */ + public void testAliasedTables() throws Exception { + if (versionMeetsMinimum(4, 1)) { + Statement scrollableStmt = null; + + try { + scrollableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = scrollableStmt.executeQuery("SELECT pos1 AS p1, pos2 AS P2, char_field AS cf FROM UPDATABLE AS UPD LIMIT 1"); + this.rs.next(); + this.rs.close(); + this.rs = null; + + scrollableStmt.close(); + scrollableStmt = null; + } finally { + if (this.rs != null) { + try { + this.rs.close(); + } catch (SQLException sqlEx) { + // ignore + } + + this.rs = null; + } + + if (scrollableStmt != null) { + try { + scrollableStmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + + scrollableStmt = null; + } + } + } + } + + /** + * Tests that the driver does not let you update result sets that come from + * tables that don't have primary keys + * + * @throws SQLException + * if an error occurs + */ + public void testBogusTable() throws SQLException { + this.stmt.executeUpdate("DROP TABLE IF EXISTS BOGUS_UPDATABLE"); + this.stmt.executeUpdate("CREATE TABLE BOGUS_UPDATABLE (field1 int)"); + + Statement scrollableStmt = null; + + try { + scrollableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = scrollableStmt.executeQuery("SELECT * FROM BOGUS_UPDATABLE"); + + try { + this.rs.moveToInsertRow(); + fail("ResultSet.moveToInsertRow() should not succeed on non-updatable table"); + } catch (NotUpdatable noUpdate) { + // ignore + } + } finally { + if (scrollableStmt != null) { + try { + scrollableStmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS BOGUS_UPDATABLE"); + } + } + + /** + * Tests that the driver does not let you update result sets that come from + * queries that haven't selected all primary keys + * + * @throws SQLException + * if an error occurs + */ + public void testMultiKeyTable() throws SQLException { + this.stmt.executeUpdate("DROP TABLE IF EXISTS MULTI_UPDATABLE"); + this.stmt.executeUpdate("CREATE TABLE MULTI_UPDATABLE (field1 int NOT NULL, field2 int NOT NULL, PRIMARY KEY (field1, field2))"); + + Statement scrollableStmt = null; + + try { + scrollableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = scrollableStmt.executeQuery("SELECT field1 FROM MULTI_UPDATABLE"); + + try { + this.rs.moveToInsertRow(); + fail("ResultSet.moveToInsertRow() should not succeed on query that does not select all primary keys"); + } catch (NotUpdatable noUpdate) { + // ignore + } + } finally { + if (scrollableStmt != null) { + try { + scrollableStmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + } + + this.stmt.executeUpdate("DROP TABLE IF EXISTS MULTI_UPDATABLE"); + } + } + + public void testUpdatability() throws SQLException { + Statement scrollableStmt = null; + + try { + scrollableStmt = this.conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + this.rs = scrollableStmt.executeQuery("SELECT * FROM UPDATABLE ORDER BY pos1"); + + this.rs.getMetaData().getColumnCount(); + + while (this.rs.next()) { + int rowPos = this.rs.getInt(1); + this.rs.updateString(3, "New Data" + (100 - rowPos)); + this.rs.updateRow(); + } + + // + // Insert a new row + // + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 400); + this.rs.updateInt(2, 400); + this.rs.updateString(3, "New Data" + (100 - 400)); + this.rs.insertRow(); + + // Test moveToCurrentRow + int rememberedPosition = this.rs.getRow(); + this.rs.moveToInsertRow(); + this.rs.moveToCurrentRow(); + assertTrue("ResultSet.moveToCurrentRow() failed", this.rs.getRow() == rememberedPosition); + this.rs.close(); + this.rs = scrollableStmt.executeQuery("SELECT * FROM UPDATABLE ORDER BY pos1"); + + boolean dataGood = true; + + while (this.rs.next()) { + int rowPos = this.rs.getInt(1); + + if (!this.rs.getString(3).equals("New Data" + (100 - rowPos))) { + dataGood = false; + } + } + + assertTrue("Updates failed", dataGood); + + // move back, and change the primary key + // This should work + int newPrimaryKeyId = 99999; + this.rs.absolute(1); + this.rs.updateInt(1, newPrimaryKeyId); + this.rs.updateRow(); + + int savedPrimaryKeyId = this.rs.getInt(1); + assertTrue("Updated primary key does not match", (newPrimaryKeyId == savedPrimaryKeyId)); + + // Check cancelRowUpdates() + this.rs.absolute(1); + + int primaryKey = this.rs.getInt(1); + int originalValue = this.rs.getInt(2); + this.rs.updateInt(2, -3); + this.rs.cancelRowUpdates(); + + int newValue = this.rs.getInt(2); + assertTrue("ResultSet.cancelRowUpdates() failed", newValue == originalValue); + + // Now check refreshRow() + // Check cancelRowUpdates() + this.rs.absolute(1); + primaryKey = this.rs.getInt(1); + this.stmt.executeUpdate("UPDATE UPDATABLE SET char_field='foo' WHERE pos1=" + primaryKey); + this.rs.refreshRow(); + assertTrue("ResultSet.refreshRow failed", this.rs.getString("char_field").equals("foo")); + + // Now check deleteRow() + this.rs.last(); + + int oldLastRow = this.rs.getRow(); + this.rs.deleteRow(); + this.rs.last(); + assertTrue("ResultSet.deleteRow() failed", this.rs.getRow() == (oldLastRow - 1)); + this.rs.close(); + + /* + * FIXME: Move to regression + * + * scrollableStmt.executeUpdate("DROP TABLE IF EXISTS test"); + * scrollableStmt.executeUpdate("CREATE TABLE test (ident INTEGER + * PRIMARY KEY, name TINYTEXT, expiry DATETIME default null)"); + * scrollableStmt.executeUpdate("INSERT INTO test SET ident=1, + * name='original'"); + * + * //Select to get a resultset to work on ResultSet this.rs = + * this.stmt.executeQuery("SELECT ident, name, expiry FROM test"); + * + * //Check that the expiry field was null before we did our update + * this.rs.first(); + * + * java.sql.Date before = this.rs.getDate("expiry"); + * + * if (this.rs.wasNull()) { System.out.println("Expiry was correctly + * SQL null before update"); } + * + * //Update a different field this.rs.updateString("name", + * "Updated"); this.rs.updateRow(); + * + * //Test to see if field has been altered java.sql.Date after = + * this.rs.getDate(3); + * + * if (this.rs.wasNull()) System.out.println("Bug disproved - expiry + * SQL null after update"); else System.out.println("Bug proved - + * expiry corrupted to '" + after + "'"); + */ + } finally { + if (scrollableStmt != null) { + try { + scrollableStmt.close(); + } catch (SQLException sqlEx) { + } + } + } + } + + private void createTestTable() throws SQLException { + // + // Catch the error, the table might exist + // + try { + this.stmt.executeUpdate("DROP TABLE UPDATABLE"); + } catch (SQLException SQLE) { + } + + this.stmt.executeUpdate("CREATE TABLE UPDATABLE (pos1 int not null, pos2 int not null, char_field VARCHAR(32), PRIMARY KEY (pos1, pos2))"); + + for (int i = 0; i < 100; i++) { + this.stmt.executeUpdate("INSERT INTO UPDATABLE VALUES (" + i + ", " + i + ",'StringData" + i + "')"); + } + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/UtilsTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/UtilsTest.java new file mode 100644 index 0000000..f0eaccb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/UtilsTest.java @@ -0,0 +1,135 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.List; + +import com.mysql.jdbc.ConnectionImpl; +import com.mysql.jdbc.ConnectionProperties; +import com.mysql.jdbc.MySQLConnection; +import com.mysql.jdbc.PreparedStatement; +import com.mysql.jdbc.ResultSetImpl; +import com.mysql.jdbc.Statement; +import com.mysql.jdbc.StatementImpl; +import com.mysql.jdbc.Util; +import com.mysql.jdbc.Wrapper; + +import testsuite.BaseTestCase; + +public class UtilsTest extends BaseTestCase { + /** + * Creates a new UtilsTest. + * + * @param name + * the name of the test + */ + public UtilsTest(String name) { + super(name); + } + + /** + * Runs all test cases in this test suite + * + * @param args + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(UtilsTest.class); + } + + /** + * Tests Util.isJdbcInterface() + * + * @throws Exception + */ + public void testIsJdbcInterface() throws Exception { + // Classes directly or indirectly implementing JDBC interfaces. + assertTrue(Util.isJdbcInterface(PreparedStatement.class)); + assertTrue(Util.isJdbcInterface(StatementImpl.class)); + assertTrue(Util.isJdbcInterface(Statement.class)); + assertTrue(Util.isJdbcInterface(ResultSetImpl.class)); + Statement s = (Statement) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { Statement.class }, new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return null; + } + }); + assertTrue(Util.isJdbcInterface(s.getClass())); + + // Classes not implementing JDBC interfaces. + assertFalse(Util.isJdbcInterface(Util.class)); + assertFalse(Util.isJdbcInterface(UtilsTest.class)); + + } + + /** + * Tests Util.isJdbcPackage() + * + * @throws Exception + */ + public void testIsJdbcPackage() throws Exception { + // JDBC packages. + assertTrue(Util.isJdbcPackage("java.sql")); + assertTrue(Util.isJdbcPackage("javax.sql")); + assertTrue(Util.isJdbcPackage("javax.sql.rowset")); + assertTrue(Util.isJdbcPackage("com.mysql.jdbc")); + assertTrue(Util.isJdbcPackage("com.mysql.jdbc")); + assertTrue(Util.isJdbcPackage("com.mysql.jdbc.jdbc2.optional")); + + // Non-JDBC packages. + assertFalse(Util.isJdbcPackage("java")); + assertFalse(Util.isJdbcPackage("java.lang")); + assertFalse(Util.isJdbcPackage("com")); + assertFalse(Util.isJdbcPackage("com.mysql")); + } + + /** + * Tests Util.isJdbcPackage() + * + * @throws Exception + */ + public void testGetImplementedInterfaces() throws Exception { + Class[] ifaces; + ifaces = Util.getImplementedInterfaces(Statement.class); + assertEquals(2, ifaces.length); + List> ifacesList = Arrays.asList(ifaces); + for (Class clazz : new Class[] { java.sql.Statement.class, Wrapper.class }) { + assertTrue(ifacesList.contains(clazz)); + } + + ifaces = Util.getImplementedInterfaces(StatementImpl.class); + assertEquals(1, ifaces.length); + assertEquals(ifaces[0], Statement.class); + + ifaces = Util.getImplementedInterfaces(ConnectionImpl.class); + assertEquals(3, ifaces.length); + ifacesList = Arrays.asList(ifaces); + for (Class clazz : new Class[] { MySQLConnection.class, Serializable.class, ConnectionProperties.class }) { + assertTrue(ifacesList.contains(clazz)); + } + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/XATest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/XATest.java new file mode 100644 index 0000000..95b1b65 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/XATest.java @@ -0,0 +1,477 @@ +/* + Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.rmi.server.UID; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Savepoint; + +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; +import com.mysql.jdbc.jdbc2.optional.MysqlXid; + +import testsuite.BaseTestCase; + +/** + * Unit tests for our XA implementation. + */ +public class XATest extends BaseTestCase { + MysqlXADataSource xaDs; + + public XATest(String name) { + super(name); + + this.xaDs = new MysqlXADataSource(); + this.xaDs.setUrl(BaseTestCase.dbUrl); + this.xaDs.setRollbackOnPooledClose(true); + } + + /** + * Tests that simple distributed transaction processing works as expected. + * + * @throws Exception + * if the test fails. + */ + public void testCoordination() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testCoordination", "(field1 int) ENGINE=InnoDB"); + + Connection conn1 = null; + Connection conn2 = null; + XAConnection xaConn1 = null; + XAConnection xaConn2 = null; + + try { + xaConn1 = getXAConnection(); + XAResource xaRes1 = xaConn1.getXAResource(); + conn1 = xaConn1.getConnection(); + + xaConn2 = getXAConnection(); + XAResource xaRes2 = xaConn2.getXAResource(); + conn2 = xaConn2.getConnection(); + + Xid xid1 = createXid(); + Xid xid2 = createXid(xid1); + + xaRes1.start(xid1, XAResource.TMNOFLAGS); + xaRes2.start(xid2, XAResource.TMNOFLAGS); + conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)"); + conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)"); + xaRes1.end(xid1, XAResource.TMSUCCESS); + xaRes2.end(xid2, XAResource.TMSUCCESS); + + xaRes1.prepare(xid1); + xaRes2.prepare(xid2); + + xaRes1.commit(xid1, false); + xaRes2.commit(xid2, false); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1"); + + assertTrue(this.rs.next()); + assertEquals(1, this.rs.getInt(1)); + + assertTrue(this.rs.next()); + assertEquals(2, this.rs.getInt(1)); + + this.stmt.executeUpdate("TRUNCATE TABLE testCoordination"); + + // + // Now test rollback + // + + xid1 = createXid(); + xid2 = createXid(xid1); + + xaRes1.start(xid1, XAResource.TMNOFLAGS); + xaRes2.start(xid2, XAResource.TMNOFLAGS); + conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)"); + + // ensure visibility + assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testCoordination WHERE field1=1").toString()); + + conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)"); + + // ensure visibility + assertEquals("2", getSingleIndexedValueWithQuery(conn2, 1, "SELECT field1 FROM testCoordination WHERE field1=2").toString()); + + xaRes1.end(xid1, XAResource.TMSUCCESS); + xaRes2.end(xid2, XAResource.TMSUCCESS); + + xaRes1.prepare(xid1); + xaRes2.prepare(xid2); + + xaRes1.rollback(xid1); + xaRes2.rollback(xid2); + + this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1"); + + assertTrue(!this.rs.next()); + } finally { + if (conn1 != null) { + conn1.close(); + } + + if (conn2 != null) { + conn2.close(); + } + + if (xaConn1 != null) { + xaConn1.close(); + } + + if (xaConn2 != null) { + xaConn2.close(); + } + } + } + + protected XAConnection getXAConnection() throws Exception { + return this.xaDs.getXAConnection(); + } + + /** + * Tests that XA RECOVER works as expected. + * + * @throws Exception + * if test fails + */ + public void testRecover() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + if (versionMeetsMinimum(5, 7) && !versionMeetsMinimum(5, 7, 5)) { + // Test is broken in 5.7.0 - 5.7.4 after server bug#14670465 fix which changed the XA RECOVER output format. + // Fixed in 5.7.5 server version + return; + } + + // BUG#14670465 fix broke this functionality in 5.7.1 - 5.7.2 + if (versionMeetsMinimum(5, 7, 1) && !versionMeetsMinimum(5, 7, 3)) { + return; + } + + XAConnection xaConn = null, recoverConn = null; + + try { + xaConn = getXAConnection(); + + Connection c = xaConn.getConnection(); + Xid xid = createXid(); + + XAResource xaRes = xaConn.getXAResource(); + xaRes.start(xid, XAResource.TMNOFLAGS); + c.createStatement().execute("SELECT 1"); + xaRes.end(xid, XAResource.TMSUCCESS); + xaRes.prepare(xid); + + // Now try and recover + recoverConn = getXAConnection(); + + XAResource recoverRes = recoverConn.getXAResource(); + + Xid[] recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); + + assertTrue(recoveredXids != null); + assertTrue(recoveredXids.length > 0); + + boolean xidFound = false; + + for (int i = 0; i < recoveredXids.length; i++) { + if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) { + xidFound = true; + + break; + } + } + + assertTrue(xidFound); + + recoverRes = recoverConn.getXAResource(); + + recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN); + + assertTrue(recoveredXids != null); + assertTrue(recoveredXids.length > 0); + + xidFound = false; + + for (int i = 0; i < recoveredXids.length; i++) { + if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) { + xidFound = true; + + break; + } + } + + assertTrue(xidFound); + + // Test flags + recoverRes.recover(XAResource.TMSTARTRSCAN); + recoverRes.recover(XAResource.TMENDRSCAN); + recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); + + // This should fail + try { + recoverRes.recover(XAResource.TMSUCCESS); + fail("XAException should have been thrown"); + } catch (XAException xaEx) { + assertEquals(XAException.XAER_INVAL, xaEx.errorCode); + } + } finally { + if (xaConn != null) { + xaConn.close(); + } + + if (recoverConn != null) { + recoverConn.close(); + } + } + } + + /** + * Tests operation of local transactions on XAConnections when global + * transactions are in or not in progress (follows from BUG#17401). + * + * @throws Exception + * if the testcase fails + */ + public void testLocalTransaction() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testLocalTransaction", "(field1 int) ENGINE=InnoDB"); + + Connection conn1 = null; + + XAConnection xaConn1 = null; + + try { + xaConn1 = getXAConnection(); + XAResource xaRes1 = xaConn1.getXAResource(); + conn1 = xaConn1.getConnection(); + assertEquals(true, conn1.getAutoCommit()); + conn1.setAutoCommit(true); + conn1.createStatement().executeUpdate("INSERT INTO testLocalTransaction VALUES (1)"); + assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testLocalTransaction").toString()); + + conn1.createStatement().executeUpdate("TRUNCATE TABLE testLocalTransaction"); + conn1.setAutoCommit(false); + conn1.createStatement().executeUpdate("INSERT INTO testLocalTransaction VALUES (2)"); + assertEquals("2", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testLocalTransaction").toString()); + conn1.rollback(); + assertEquals(0, getRowCount("testLocalTransaction")); + + conn1.createStatement().executeUpdate("INSERT INTO testLocalTransaction VALUES (3)"); + assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testLocalTransaction").toString()); + conn1.commit(); + assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testLocalTransaction").toString()); + conn1.commit(); + + Savepoint sp = conn1.setSavepoint(); + conn1.rollback(sp); + sp = conn1.setSavepoint("abcd"); + conn1.rollback(sp); + Savepoint spSaved = sp; + + Xid xid = createXid(); + xaRes1.start(xid, XAResource.TMNOFLAGS); + + try { + try { + conn1.setAutoCommit(true); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + conn1.commit(); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + conn1.rollback(); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + sp = conn1.setSavepoint(); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + conn1.rollback(spSaved); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + sp = conn1.setSavepoint("abcd"); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + + try { + conn1.rollback(spSaved); + } catch (SQLException sqlEx) { + // we expect an exception here + assertEquals("2D000", sqlEx.getSQLState()); + } + } finally { + xaRes1.forget(xid); + } + } finally { + if (xaConn1 != null) { + try { + xaConn1.close(); + } catch (SQLException sqlEx) { + // this is just busted in the server right now + } + } + } + } + + public void testSuspendableTx() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + Connection conn1 = null; + + MysqlXADataSource suspXaDs = new MysqlXADataSource(); + suspXaDs.setUrl(BaseTestCase.dbUrl); + suspXaDs.setPinGlobalTxToPhysicalConnection(true); + suspXaDs.setRollbackOnPooledClose(true); + + XAConnection xaConn1 = null; + + Xid xid = createXid(); + + try { + /* + * -- works using RESUME + * xa start 0x123,0x456; + * select * from foo; + * xa end 0x123,0x456; + * xa start 0x123,0x456 resume; + * select * from foo; + * xa end 0x123,0x456; + * xa commit 0x123,0x456 one phase; + */ + + xaConn1 = suspXaDs.getXAConnection(); + XAResource xaRes1 = xaConn1.getXAResource(); + conn1 = xaConn1.getConnection(); + xaRes1.start(xid, XAResource.TMNOFLAGS); + conn1.createStatement().execute("SELECT 1"); + xaRes1.end(xid, XAResource.TMSUCCESS); + xaRes1.start(xid, XAResource.TMRESUME); + conn1.createStatement().execute("SELECT 1"); + xaRes1.end(xid, XAResource.TMSUCCESS); + xaRes1.commit(xid, true); + + xaConn1.close(); + + /* + * + * -- fails using JOIN + * xa start 0x123,0x456; + * select * from foo; + * xa end 0x123,0x456; + * xa start 0x123,0x456 join; + * select * from foo; + * xa end 0x123,0x456; + * xa commit 0x123,0x456 one phase; + */ + + xaConn1 = suspXaDs.getXAConnection(); + xaRes1 = xaConn1.getXAResource(); + conn1 = xaConn1.getConnection(); + xaRes1.start(xid, XAResource.TMNOFLAGS); + conn1.createStatement().execute("SELECT 1"); + xaRes1.end(xid, XAResource.TMSUCCESS); + xaRes1.start(xid, XAResource.TMJOIN); + conn1.createStatement().execute("SELECT 1"); + xaRes1.end(xid, XAResource.TMSUCCESS); + xaRes1.commit(xid, true); + } finally { + if (xaConn1 != null) { + xaConn1.close(); + } + } + } + + private Xid createXid() throws IOException { + ByteArrayOutputStream gtridOut = new ByteArrayOutputStream(); + DataOutputStream dataOut = new DataOutputStream(gtridOut); + new UID().write(dataOut); + + final byte[] gtrid = gtridOut.toByteArray(); + + ByteArrayOutputStream bqualOut = new ByteArrayOutputStream(); + dataOut = new DataOutputStream(bqualOut); + + new UID().write(dataOut); + + final byte[] bqual = bqualOut.toByteArray(); + + Xid xid = new MysqlXid(gtrid, bqual, 3306); + return xid; + } + + private Xid createXid(Xid xidToBranch) throws IOException { + ByteArrayOutputStream bqualOut = new ByteArrayOutputStream(); + DataOutputStream dataOut = new DataOutputStream(bqualOut); + + new UID().write(dataOut); + + final byte[] bqual = bqualOut.toByteArray(); + + Xid xid = new MysqlXid(xidToBranch.getGlobalTransactionId(), bqual, 3306); + + return xid; + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc4/StatementsTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc4/StatementsTest.java new file mode 100644 index 0000000..745dcbb --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc4/StatementsTest.java @@ -0,0 +1,612 @@ +/* + Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple.jdbc4; + +import java.io.Reader; +import java.io.StringReader; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import com.mysql.jdbc.NonRegisteringDriver; + +import testsuite.BaseTestCase; +import testsuite.regression.ConnectionRegressionTest.CountingReBalanceStrategy; + +public class StatementsTest extends BaseTestCase { + + public StatementsTest(String name) { + super(name); + + } + + /** + * Tests for ResultSet.getNCharacterStream() + * + * @throws Exception + */ + public void testGetNCharacterSteram() throws Exception { + createTable("testGetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))"); + this.stmt.executeUpdate("INSERT INTO testGetNCharacterStream (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')"); + this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNCharacterStream"); + this.rs.next(); + char[] c1 = new char[3]; + this.rs.getNCharacterStream(1).read(c1); + assertEquals("aaa", new String(c1)); + char[] c2 = new char[3]; + this.rs.getNCharacterStream("c2").read(c2); + assertEquals("bbb", new String(c2)); + this.rs.close(); + } + + /** + * Tests for ResultSet.getNClob() + * + * @throws Exception + */ + public void testGetNClob() throws Exception { + createTable("testGetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))"); + this.stmt.executeUpdate("INSERT INTO testGetNClob (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')"); + this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNClob"); + this.rs.next(); + char[] c1 = new char[3]; + this.rs.getNClob(1).getCharacterStream().read(c1); + assertEquals("aaa", new String(c1)); + char[] c2 = new char[3]; + this.rs.getNClob("c2").getCharacterStream().read(c2); + assertEquals("bbb", new String(c2)); + this.rs.close(); + + // for isBinaryEncoded = true, using PreparedStatement + createTable("testGetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))"); + this.stmt.executeUpdate("INSERT INTO testGetNClob (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')"); + this.pstmt = this.conn.prepareStatement("SELECT c1, c2 FROM testGetNClob"); + this.rs = this.pstmt.executeQuery(); + this.rs.next(); + c1 = new char[3]; + this.rs.getNClob(1).getCharacterStream().read(c1); + assertEquals("aaa", new String(c1)); + c2 = new char[3]; + this.rs.getNClob("c2").getCharacterStream().read(c2); + assertEquals("bbb", new String(c2)); + this.rs.close(); + } + + /** + * Tests for ResultSet.getNString() + * + * @throws Exception + */ + public void testGetNString() throws Exception { + createTable("testGetNString", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))"); + this.stmt.executeUpdate("INSERT INTO testGetNString (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')"); + this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNString"); + this.rs.next(); + assertEquals("aaa", this.rs.getNString(1)); + assertEquals("bbb", this.rs.getNString("c2")); + this.rs.close(); + } + + /** + * Tests for PreparedStatement.setNCharacterSteam() + * + * @throws Exception + */ + public void testSetNCharacterStream() throws Exception { + // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES" + + createTable("testSetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "false"); // use client-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + com.mysql.jdbc.PreparedStatement pstmt1 = (com.mysql.jdbc.PreparedStatement) conn1 + .prepareStatement("INSERT INTO testSetNCharacterStream (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt1.setNCharacterStream(1, null, 0); + pstmt1.setNCharacterStream(2, new StringReader("aaa"), 3); + pstmt1.setNCharacterStream(3, new StringReader("\'aaa\'"), 5); + pstmt1.execute(); + ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNCharacterStream"); + rs1.next(); + assertEquals(null, rs1.getString(1)); + assertEquals("aaa", rs1.getString(2)); + assertEquals("\'aaa\'", rs1.getString(3)); + rs1.close(); + pstmt1.close(); + conn1.close(); + + createTable("testSetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "false"); // use client-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + com.mysql.jdbc.PreparedStatement pstmt2 = (com.mysql.jdbc.PreparedStatement) conn2 + .prepareStatement("INSERT INTO testSetNCharacterStream (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt2.setNCharacterStream(1, null, 0); + pstmt2.setNCharacterStream(2, new StringReader("aaa"), 3); + pstmt2.setNCharacterStream(3, new StringReader("\'aaa\'"), 5); + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNCharacterStream"); + rs2.next(); + assertEquals(null, rs2.getString(1)); + assertEquals("aaa", rs2.getString(2)); + assertEquals("\'aaa\'", rs2.getString(3)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for ServerPreparedStatement.setNCharacterSteam() + * + * @throws Exception + */ + public void testSetNCharacterStreamServer() throws Exception { + createTable("testSetNCharacterStreamServer", "(c1 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testSetNCharacterStreamServer (c1) VALUES (?)"); + try { + pstmt1.setNCharacterStream(1, new StringReader("aaa"), 3); + fail(); + } catch (SQLException e) { + // ok + assertEquals("Can not call setNCharacterStream() when connection character set isn't UTF-8", e.getMessage()); + } + pstmt1.close(); + conn1.close(); + + createTable("testSetNCharacterStreamServer", "(c1 LONGTEXT charset utf8) ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testSetNCharacterStreamServer (c1) VALUES (?)"); + pstmt2.setNCharacterStream(1, new StringReader(new String(new char[81921])), 81921); // 10 Full Long Data Packet's chars + 1 char + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1 FROM testSetNCharacterStreamServer"); + rs2.next(); + assertEquals(new String(new char[81921]), rs2.getString(1)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for PreparedStatement.setNClob() + * + * @throws Exception + */ + public void testSetNClob() throws Exception { + // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES" + + createTable("testSetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "false"); // use client-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testSetNClob (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt1.setNClob(1, (NClob) null); + NClob nclob2 = conn1.createNClob(); + nclob2.setString(1, "aaa"); + pstmt1.setNClob(2, nclob2); // for setNClob(int, NClob) + Reader reader3 = new StringReader("\'aaa\'"); + pstmt1.setNClob(3, reader3, 5); // for setNClob(int, Reader, long) + pstmt1.execute(); + ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNClob"); + rs1.next(); + assertEquals(null, rs1.getString(1)); + assertEquals("aaa", rs1.getString(2)); + assertEquals("\'aaa\'", rs1.getString(3)); + rs1.close(); + pstmt1.close(); + conn1.close(); + + createTable("testSetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "false"); // use client-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testSetNClob (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt2.setNClob(1, (NClob) null); + nclob2 = conn2.createNClob(); + nclob2.setString(1, "aaa"); + pstmt2.setNClob(2, nclob2); // for setNClob(int, NClob) + reader3 = new StringReader("\'aaa\'"); + pstmt2.setNClob(3, reader3, 5); // for setNClob(int, Reader, long) + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNClob"); + rs2.next(); + assertEquals(null, rs2.getString(1)); + assertEquals("aaa", rs2.getString(2)); + assertEquals("\'aaa\'", rs2.getString(3)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for ServerPreparedStatement.setNClob() + * + * @throws Exception + */ + public void testSetNClobServer() throws Exception { + createTable("testSetNClobServer", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testSetNClobServer (c1, c2) VALUES (?, ?)"); + NClob nclob1 = conn1.createNClob(); + nclob1.setString(1, "aaa"); + Reader reader2 = new StringReader("aaa"); + try { + pstmt1.setNClob(1, nclob1); + fail(); + } catch (SQLException e) { + // ok + assertEquals("Can not call setNClob() when connection character set isn't UTF-8", e.getMessage()); + } + try { + pstmt1.setNClob(2, reader2, 3); + fail(); + } catch (SQLException e) { + // ok + assertEquals("Can not call setNClob() when connection character set isn't UTF-8", e.getMessage()); + } + pstmt1.close(); + conn1.close(); + + createTable("testSetNClobServer", "(c1 NATIONAL CHARACTER(10), c2 LONGTEXT charset utf8) ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testSetNClobServer (c1, c2) VALUES (?, ?)"); + nclob1 = conn2.createNClob(); + nclob1.setString(1, "aaa"); + pstmt2.setNClob(1, nclob1); + pstmt2.setNClob(2, new StringReader(new String(new char[81921])), 81921); // 10 Full Long Data Packet's chars + 1 char + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2 FROM testSetNClobServer"); + rs2.next(); + assertEquals("aaa", rs2.getString(1)); + assertEquals(new String(new char[81921]), rs2.getString(2)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for PreparedStatement.setNString() + * + * @throws Exception + */ + public void testSetNString() throws Exception { + // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES" + + createTable("testSetNString", + "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) DEFAULT CHARACTER SET cp932 ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "false"); // use client-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "MS932"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testSetNString (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt1.setNString(1, null); + pstmt1.setNString(2, "aaa"); + pstmt1.setNString(3, "\'aaa\'"); + pstmt1.execute(); + ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNString"); + rs1.next(); + assertEquals(null, rs1.getString(1)); + assertEquals("aaa", rs1.getString(2)); + assertEquals("\'aaa\'", rs1.getString(3)); + rs1.close(); + pstmt1.close(); + conn1.close(); + + createTable("testSetNString", + "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), c3 NATIONAL CHARACTER(10)) DEFAULT CHARACTER SET cp932 ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "false"); // use client-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testSetNString (c1, c2, c3) VALUES (?, ?, ?)"); + pstmt2.setNString(1, null); + pstmt2.setNString(2, "aaa"); + pstmt2.setNString(3, "\'aaa\'"); + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNString"); + rs2.next(); + assertEquals(null, rs2.getString(1)); + assertEquals("aaa", rs2.getString(2)); + assertEquals("\'aaa\'", rs2.getString(3)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for ServerPreparedStatement.setNString() + * + * @throws Exception + */ + public void testSetNStringServer() throws Exception { + createTable("testSetNStringServer", "(c1 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("useUnicode", "true"); + props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testSetNStringServer (c1) VALUES (?)"); + try { + pstmt1.setNString(1, "aaa"); + fail(); + } catch (SQLException e) { + // ok + assertEquals("Can not call setNString() when connection character set isn't UTF-8", e.getMessage()); + } + pstmt1.close(); + conn1.close(); + + createTable("testSetNStringServer", "(c1 NATIONAL CHARACTER(10)) ENGINE=InnoDB"); + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("useUnicode", "true"); + props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testSetNStringServer (c1) VALUES (?)"); + pstmt2.setNString(1, "\'aaa\'"); + pstmt2.execute(); + ResultSet rs2 = this.stmt.executeQuery("SELECT c1 FROM testSetNStringServer"); + rs2.next(); + assertEquals("\'aaa\'", rs2.getString(1)); + rs2.close(); + pstmt2.close(); + conn2.close(); + } + + /** + * Tests for ResultSet.updateNCharacterStream() + * + * @throws Exception + */ + public void testUpdateNCharacterStream() throws Exception { + createTable("testUpdateNCharacterStream", "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("characterEncoding", "UTF-8"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testUpdateNCharacterStream (c1, c2) VALUES (?, ?)"); + pstmt1.setString(1, "1"); + pstmt1.setNCharacterStream(2, new StringReader("aaa"), 3); + pstmt1.execute(); + Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream"); + rs1.next(); + rs1.updateNCharacterStream("c2", new StringReader("bbb"), 3); + rs1.updateRow(); + rs1.moveToInsertRow(); + rs1.updateString("c1", "2"); + rs1.updateNCharacterStream("c2", new StringReader("ccc"), 3); + rs1.insertRow(); + ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream"); + rs2.next(); + assertEquals("1", rs2.getString("c1")); + assertEquals("bbb", rs2.getNString("c2")); + rs2.next(); + assertEquals("2", rs2.getString("c1")); + assertEquals("ccc", rs2.getNString("c2")); + pstmt1.close(); + stmt1.close(); + conn1.close(); + + createTable("testUpdateNCharacterStream", "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testUpdateNCharacterStream (c1, c2) VALUES (?, ?)"); + pstmt2.setString(1, "1"); + pstmt2.setString(2, "aaa"); + pstmt2.execute(); + Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream"); + rs3.next(); + try { + rs3.updateNCharacterStream("c2", new StringReader("bbb"), 3); // field's charset isn't utf8 + fail(); + } catch (SQLException ex) { + assertEquals("Can not call updateNCharacterStream() when field's character set isn't UTF-8", ex.getMessage()); + } + rs3.close(); + pstmt2.close(); + stmt2.close(); + conn2.close(); + } + + /** + * Tests for ResultSet.updateNClob() + * + * @throws Exception + */ + public void testUpdateNClob() throws Exception { + createTable("testUpdateNChlob", "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("characterEncoding", "UTF-8"); // ensure charset isn't utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testUpdateNChlob (c1, c2) VALUES (?, ?)"); + pstmt1.setString(1, "1"); + NClob nClob1 = conn1.createNClob(); + nClob1.setString(1, "aaa"); + pstmt1.setNClob(2, nClob1); + pstmt1.execute(); + Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNChlob"); + rs1.next(); + NClob nClob2 = conn1.createNClob(); + nClob2.setString(1, "bbb"); + rs1.updateNClob("c2", nClob2); + rs1.updateRow(); + rs1.moveToInsertRow(); + rs1.updateString("c1", "2"); + NClob nClob3 = conn1.createNClob(); + nClob3.setString(1, "ccc"); + rs1.updateNClob("c2", nClob3); + rs1.insertRow(); + ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNChlob"); + rs2.next(); + assertEquals("1", rs2.getString("c1")); + assertEquals("bbb", rs2.getNString("c2")); + rs2.next(); + assertEquals("2", rs2.getString("c1")); + assertEquals("ccc", rs2.getNString("c2")); + pstmt1.close(); + stmt1.close(); + conn1.close(); + + createTable("testUpdateNChlob", "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testUpdateNChlob (c1, c2) VALUES (?, ?)"); + pstmt2.setString(1, "1"); + pstmt2.setString(2, "aaa"); + pstmt2.execute(); + Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNChlob"); + rs3.next(); + NClob nClob4 = conn2.createNClob(); + nClob4.setString(1, "bbb"); + try { + rs3.updateNClob("c2", nClob4); // field's charset isn't utf8 + fail(); + } catch (SQLException ex) { + assertEquals("Can not call updateNClob() when field's character set isn't UTF-8", ex.getMessage()); + } + rs3.close(); + pstmt2.close(); + stmt2.close(); + conn2.close(); + } + + /** + * Tests for ResultSet.updateNString() + * + * @throws Exception + */ + public void testUpdateNString() throws Exception { + createTable("testUpdateNString", "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis"); + Properties props1 = new Properties(); + props1.put("useServerPrepStmts", "true"); // use server-side prepared statement + props1.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here + Connection conn1 = getConnectionWithProps(props1); + PreparedStatement pstmt1 = conn1.prepareStatement("INSERT INTO testUpdateNString (c1, c2) VALUES (?, ?)"); + pstmt1.setString(1, "1"); + pstmt1.setNString(2, "aaa"); + pstmt1.execute(); + Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNString"); + rs1.next(); + rs1.updateNString("c2", "bbb"); + rs1.updateRow(); + rs1.moveToInsertRow(); + rs1.updateString("c1", "2"); + rs1.updateNString("c2", "ccc"); + rs1.insertRow(); + ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNString"); + rs2.next(); + assertEquals("1", rs2.getString("c1")); + assertEquals("bbb", rs2.getNString("c2")); + rs2.next(); + assertEquals("2", rs2.getString("c1")); + assertEquals("ccc", rs2.getNString("c2")); + pstmt1.close(); + stmt1.close(); + conn1.close(); + + createTable("testUpdateNString", "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field + Properties props2 = new Properties(); + props2.put("useServerPrepStmts", "true"); // use server-side prepared statement + props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here + Connection conn2 = getConnectionWithProps(props2); + PreparedStatement pstmt2 = conn2.prepareStatement("INSERT INTO testUpdateNString (c1, c2) VALUES (?, ?)"); + pstmt2.setString(1, "1"); + pstmt2.setString(2, "aaa"); + pstmt2.execute(); + Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNString"); + rs3.next(); + try { + rs3.updateNString("c2", "bbb"); // field's charset isn't utf8 + fail(); + } catch (SQLException ex) { + assertEquals("Can not call updateNString() when field's character set isn't UTF-8", ex.getMessage()); + } + rs3.close(); + pstmt2.close(); + stmt2.close(); + conn2.close(); + } + + public void testJdbc4LoadBalancing() throws Exception { + Properties props = new Properties(); + props.setProperty("loadBalanceStrategy", CountingReBalanceStrategy.class.getName()); + props.setProperty("loadBalanceAutoCommitStatementThreshold", "3"); + + String portNumber = new NonRegisteringDriver().parseURL(dbUrl, null).getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY); + + if (portNumber == null) { + portNumber = "3306"; + } + + Connection conn2 = this.getUnreliableLoadBalancedConnection(new String[] { "first", "second" }, props); + try { + conn2.createNClob(); + } catch (SQLException e) { + fail("Unable to call Connection.createNClob() in load-balanced connection"); + } + + } + +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ConnectionTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ConnectionTest.java new file mode 100644 index 0000000..9f77be1 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ConnectionTest.java @@ -0,0 +1,134 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple.jdbc42; + +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.Driver; +import com.mysql.jdbc.MySQLConnection; + +import testsuite.BaseTestCase; + +public class ConnectionTest extends BaseTestCase { + public ConnectionTest(String name) { + super(name); + } + + /** + * Test for Driver.acceptsURL() behavior clarification: + * - acceptsURL() throws SQLException if URL is null. + */ + public void testDriverAcceptsURLNullArgument() { + assertThrows(SQLException.class, "The url cannot be null", new Callable() { + public Void call() throws Exception { + Driver mysqlDriver = new Driver(); + mysqlDriver.acceptsURL(null); + return null; + } + }); + } + + /** + * Test for Driver.connect() behavior clarifications: + * - connect() throws SQLException if URL is null. + */ + public void testDriverConnectNullArgument() throws Exception { + assertThrows(SQLException.class, "The url cannot be null", new Callable() { + public Void call() throws Exception { + Driver mysqlDriver = new Driver(); + mysqlDriver.connect(null, null); + return null; + } + }); + + assertThrows(SQLException.class, "The url cannot be null", new Callable() { + public Void call() throws Exception { + DriverManager.getConnection(null); + return null; + } + }); + } + + /** + * Test for Driver.connect() behavior clarifications: + * - connect() properties precedence is implementation-defined. + */ + public void testDriverConnectPropertiesPrecedence() throws Exception { + assertThrows(SQLException.class, "Access denied for user 'dummy'@'localhost' \\(using password: YES\\)", new Callable() { + public Void call() throws Exception { + DriverManager.getConnection(BaseTestCase.dbUrl, "dummy", "dummy"); + return null; + } + }); + + // make sure the connection string doesn't contain 'maxRows' + String testUrl = BaseTestCase.dbUrl; + int b = testUrl.indexOf("maxRows"); + if (b != -1) { + int e = testUrl.indexOf('&', b); + if (e == -1) { + e = testUrl.length(); + b--; + } else { + e++; + } + testUrl = testUrl.substring(0, b) + testUrl.substring(e, testUrl.length()); + } + + Properties props = new Properties(); + props.setProperty("maxRows", "123"); + + // Default property value. + MySQLConnection testConn = (MySQLConnection) DriverManager.getConnection(testUrl); + assertEquals(-1, testConn.getMaxRows()); + testConn = (MySQLConnection) DriverManager.getConnection(testUrl, new Properties()); + assertEquals(-1, testConn.getMaxRows()); + + // Property in properties only. + testConn = (MySQLConnection) DriverManager.getConnection(testUrl, props); + assertEquals(123, testConn.getMaxRows()); + + testUrl += (testUrl.indexOf('?') == -1 ? "?" : "&") + "maxRows=321"; + + // Property in URL only. + testConn = (MySQLConnection) DriverManager.getConnection(testUrl); + assertEquals(321, testConn.getMaxRows()); + testConn = (MySQLConnection) DriverManager.getConnection(testUrl, new Properties()); + assertEquals(321, testConn.getMaxRows()); + + // Property in both. + testConn = (MySQLConnection) DriverManager.getConnection(testUrl, props); + assertEquals(123, testConn.getMaxRows()); + } + + /** + * Test for REF_CURSOR support checking. + */ + public void testSupportsRefCursors() throws Exception { + assertFalse(this.conn.getMetaData().supportsRefCursors()); + } +} diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ResultSetTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ResultSetTest.java new file mode 100644 index 0000000..60a5727 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/ResultSetTest.java @@ -0,0 +1,439 @@ +/* + Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple.jdbc42; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.util.concurrent.Callable; + +import com.mysql.jdbc.NotUpdatable; + +import testsuite.BaseTestCase; + +public class ResultSetTest extends BaseTestCase { + + public ResultSetTest(String name) { + super(name); + } + + /** + * Test for ResultSet.updateObject(), non-updatable ResultSet behaviour. + */ + public void testNonUpdResultSetUpdateObject() throws Exception { + this.rs = this.stmt.executeQuery("SELECT 'testResultSetUpdateObject' AS test"); + + final ResultSet rsTmp = this.rs; + assertThrows(NotUpdatable.class, "Result Set not updatable.*", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(1, rsTmp.toString(), JDBCType.VARCHAR); + return null; + } + }); + assertThrows(NotUpdatable.class, "Result Set not updatable.*", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(1, rsTmp.toString(), JDBCType.VARCHAR, 10); + return null; + } + }); + assertThrows(NotUpdatable.class, "Result Set not updatable.*", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("test", rsTmp.toString(), JDBCType.VARCHAR); + return null; + } + }); + assertThrows(NotUpdatable.class, "Result Set not updatable.*", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("test", rsTmp.toString(), JDBCType.VARCHAR, 10); + return null; + } + }); + } + + /** + * Test for (Updatable)ResultSet.[update|get]Object(). + * Note: ResultSet.getObject() is covered in methods TestJDBC42Statemet.validateTestData[Local|Offset]DTTypes. + */ + public void testUpdResultSetUpdateObjectAndNewSupportedTypes() throws Exception { + /* + * Objects java.time.Local[Date][Time] are supported via conversion to/from java.sql.[Date|Time|Timestamp]. + */ + createTable("testUpdateObject1", "(id INT PRIMARY KEY, d DATE, t TIME, dt DATETIME, ts TIMESTAMP)"); + + Statement testStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + + /* + * Test insert new rows. + */ + String testDateString = "2015-01-01"; + String testTimeString = "00:00:01"; + String testDateTimeString = testDateString + " " + testTimeString + ".0"; + String testISODateTimeString = testDateString + "T" + testTimeString + ".0"; + + Date testSqlDate = Date.valueOf(testDateString); + Time testSqlTime = Time.valueOf(testTimeString); + Timestamp testSqlTimeStamp = Timestamp.valueOf(testDateTimeString); + + LocalDate testLocalDate = LocalDate.parse(testDateString); + LocalTime testLocalTime = LocalTime.parse(testTimeString); + LocalDateTime testLocalDateTime = LocalDateTime.parse(testISODateTimeString); + + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject1"); + + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 1); + this.rs.updateObject(2, testLocalDate); + this.rs.updateObject(3, testLocalTime); + this.rs.updateObject(4, testLocalDateTime); + this.rs.updateObject(5, testLocalDateTime); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 2); + this.rs.updateObject(2, testLocalDate, 10); + this.rs.updateObject(3, testLocalTime, 8); + this.rs.updateObject(4, testLocalDateTime, 20); + this.rs.updateObject(5, testLocalDateTime, 20); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 3); + this.rs.updateObject("d", testLocalDate); + this.rs.updateObject("t", testLocalTime); + this.rs.updateObject("dt", testLocalDateTime); + this.rs.updateObject("ts", testLocalDateTime); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 4); + this.rs.updateObject("d", testLocalDate, 10); + this.rs.updateObject("t", testLocalTime, 8); + this.rs.updateObject("dt", testLocalDateTime, 20); + this.rs.updateObject("ts", testLocalDateTime, 20); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 5); + this.rs.updateObject(2, testLocalDate, JDBCType.DATE); + this.rs.updateObject(3, testLocalTime, JDBCType.TIME); + this.rs.updateObject(4, testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateObject(5, testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 6); + this.rs.updateObject(2, testLocalDate, JDBCType.DATE, 10); + this.rs.updateObject(3, testLocalTime, JDBCType.TIME, 8); + this.rs.updateObject(4, testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateObject(5, testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 7); + this.rs.updateObject("d", testLocalDate, JDBCType.DATE); + this.rs.updateObject("t", testLocalTime, JDBCType.TIME); + this.rs.updateObject("dt", testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateObject("ts", testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.insertRow(); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 8); + this.rs.updateObject("d", testLocalDate, JDBCType.DATE, 10); + this.rs.updateObject("t", testLocalTime, JDBCType.TIME, 8); + this.rs.updateObject("dt", testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateObject("ts", testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.insertRow(); + + // check final results. + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject1"); + for (int i = 1; i <= 8; i++) { + assertTrue(this.rs.next()); + assertEquals(i, this.rs.getInt(1)); + assertEquals(testSqlDate, this.rs.getDate(2)); + assertEquals(testSqlTime, this.rs.getTime(3)); + assertEquals(testSqlTimeStamp, this.rs.getTimestamp(4)); + assertEquals(testSqlTimeStamp, this.rs.getTimestamp(5)); + } + assertFalse(this.rs.next()); + + /* + * Test update rows. + */ + testDateString = "2015-12-31"; + testTimeString = "23:59:59"; + testDateTimeString = testDateString + " " + testTimeString + ".0"; + testISODateTimeString = testDateString + "T" + testTimeString + ".0"; + + testSqlDate = Date.valueOf(testDateString); + testSqlTime = Time.valueOf(testTimeString); + testSqlTimeStamp = Timestamp.valueOf(testDateTimeString); + + testLocalDate = LocalDate.parse(testDateString); + testLocalTime = LocalTime.parse(testTimeString); + testLocalDateTime = LocalDateTime.parse(testISODateTimeString); + + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject1"); + + assertTrue(this.rs.next()); + this.rs.updateObject(2, testLocalDate); + this.rs.updateObject(3, testLocalTime); + this.rs.updateObject(4, testLocalDateTime); + this.rs.updateObject(5, testLocalDateTime); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject(2, testLocalDate, 10); + this.rs.updateObject(3, testLocalTime, 8); + this.rs.updateObject(4, testLocalDateTime, 20); + this.rs.updateObject(5, testLocalDateTime, 20); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject("d", testLocalDate); + this.rs.updateObject("t", testLocalTime); + this.rs.updateObject("dt", testLocalDateTime); + this.rs.updateObject("ts", testLocalDateTime); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject("d", testLocalDate, 10); + this.rs.updateObject("t", testLocalTime, 8); + this.rs.updateObject("dt", testLocalDateTime, 20); + this.rs.updateObject("ts", testLocalDateTime, 20); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject(2, testLocalDate, JDBCType.DATE); + this.rs.updateObject(3, testLocalTime, JDBCType.TIME); + this.rs.updateObject(4, testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateObject(5, testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject(2, testLocalDate, JDBCType.DATE, 10); + this.rs.updateObject(3, testLocalTime, JDBCType.TIME, 8); + this.rs.updateObject(4, testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateObject(5, testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject("d", testLocalDate, JDBCType.DATE); + this.rs.updateObject("t", testLocalTime, JDBCType.TIME); + this.rs.updateObject("dt", testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateObject("ts", testLocalDateTime, JDBCType.TIMESTAMP); + this.rs.updateRow(); + assertTrue(this.rs.next()); + this.rs.updateObject("d", testLocalDate, JDBCType.DATE, 10); + this.rs.updateObject("t", testLocalTime, JDBCType.TIME, 8); + this.rs.updateObject("dt", testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateObject("ts", testLocalDateTime, JDBCType.TIMESTAMP, 20); + this.rs.updateRow(); + + // check final results. + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject1"); + int rowCount = 0; + while (rs.next()) { + String row = "Row " + this.rs.getInt(1); + assertEquals(row, ++rowCount, this.rs.getInt(1)); + + assertEquals(row, testSqlDate, this.rs.getDate(2)); + assertEquals(row, testSqlTime, this.rs.getTime(3)); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp(4)); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp(5)); + + assertEquals(row, testLocalDate, this.rs.getObject(2, LocalDate.class)); + assertEquals(row, testLocalTime, this.rs.getObject(3, LocalTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject(4, LocalDateTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject(5, LocalDateTime.class)); + + assertEquals(row, rowCount, this.rs.getInt("id")); + + assertEquals(row, testSqlDate, this.rs.getDate("d")); + assertEquals(row, testSqlTime, this.rs.getTime("t")); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp("dt")); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp("ts")); + + assertEquals(row, testLocalDate, this.rs.getObject("d", LocalDate.class)); + assertEquals(row, testLocalTime, this.rs.getObject("t", LocalTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject("dt", LocalDateTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject("ts", LocalDateTime.class)); + } + assertEquals(8, rowCount); + + /* + * Objects java.time.Offset[Date]Time are supported via conversion to *CHAR or serialization. + */ + OffsetDateTime testOffsetDateTime = OffsetDateTime.of(2015, 8, 04, 12, 34, 56, 7890, ZoneOffset.UTC); + OffsetTime testOffsetTime = OffsetTime.of(12, 34, 56, 7890, ZoneOffset.UTC); + + createTable("testUpdateObject2", "(id INT PRIMARY KEY, ot1 VARCHAR(100), ot2 BLOB, odt1 VARCHAR(100), odt2 BLOB)"); + + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject2"); + this.rs.moveToInsertRow(); + this.rs.updateInt(1, 1); + this.rs.updateObject(2, testOffsetTime, JDBCType.VARCHAR); + this.rs.updateObject(3, testOffsetTime); + this.rs.updateObject(4, testOffsetDateTime, JDBCType.VARCHAR); + this.rs.updateObject(5, testOffsetDateTime); + this.rs.insertRow(); + + this.rs.updateInt("id", 2); + this.rs.updateObject("ot1", testOffsetTime, JDBCType.VARCHAR); + this.rs.updateObject("ot2", testOffsetTime); + this.rs.updateObject("odt1", testOffsetDateTime, JDBCType.VARCHAR); + this.rs.updateObject("odt2", testOffsetDateTime); + this.rs.insertRow(); + + Connection testConn = getConnectionWithProps("autoDeserialize=true"); + testStmt = testConn.createStatement(); + + this.rs = testStmt.executeQuery("SELECT * FROM testUpdateObject2"); + rowCount = 0; + while (rs.next()) { + String row = "Row " + this.rs.getInt(1); + assertEquals(row, ++rowCount, this.rs.getInt(1)); + + assertEquals(row, testOffsetTime, this.rs.getObject(2, OffsetTime.class)); + assertEquals(row, testOffsetTime, this.rs.getObject(3, OffsetTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject(4, OffsetDateTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject(5, OffsetDateTime.class)); + + assertEquals(row, rowCount, this.rs.getInt("id")); + + assertEquals(row, testOffsetTime, this.rs.getObject("ot1", OffsetTime.class)); + assertEquals(row, testOffsetTime, this.rs.getObject("ot2", OffsetTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject("odt1", OffsetDateTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject("odt2", OffsetDateTime.class)); + } + assertEquals(2, rowCount); + + testConn.close(); + } + + /** + * Test for (Updatable)ResultSet.updateObject(), unsupported SQL types TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE and REF_CURSOR. + */ + public void testUpdResultSetUpdateObjectAndNewUnsupportedTypes() throws SQLException { + createTable("testUnsupportedTypes", "(id INT PRIMARY KEY, col VARCHAR(20))"); + + Statement testStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + assertEquals(1, testStmt.executeUpdate("INSERT INTO testUnsupportedTypes VALUES (1, 'dummy')")); + this.rs = testStmt.executeQuery("SELECT * FROM testUnsupportedTypes"); + + /* + * Unsupported SQL types TIME_WITH_TIMEZONE and TIMESTAMP_WITH_TIMEZONE. + */ + + assertTrue(this.rs.next()); + + final ResultSet rsTmp = this.rs; + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, LocalTime.now(), JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, LocalTime.now(), JDBCType.TIME_WITH_TIMEZONE, 8); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", LocalTime.now(), JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", LocalTime.now(), JDBCType.TIME_WITH_TIMEZONE, 8); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, LocalDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, LocalDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE, 20); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", LocalDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", LocalDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE, 20); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, new Object(), JDBCType.REF_CURSOR); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject(2, new Object(), JDBCType.REF_CURSOR, 32); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", new Object(), JDBCType.REF_CURSOR); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + rsTmp.updateObject("col", new Object(), JDBCType.REF_CURSOR, 32); + return null; + } + }); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/StatementsTest.java b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/StatementsTest.java new file mode 100644 index 0000000..2002ae3 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/simple/jdbc42/StatementsTest.java @@ -0,0 +1,1273 @@ +/* + Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + + The MySQL Connector/J is licensed under the terms of the GPLv2 + , like most MySQL Connectors. + There are special exceptions to the terms and conditions of the GPLv2 as it is applied to + this software, see the FOSS License Exception + . + + 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; version 2 + of the License. + + 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., 51 Franklin St, Fifth + Floor, Boston, MA 02110-1301 USA + + */ + +package testsuite.simple.jdbc42; + +import java.sql.BatchUpdateException; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.PreparedStatement; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.util.concurrent.Callable; + +import testsuite.BaseTestCase; + +public class StatementsTest extends BaseTestCase { + // Shared test data + private final String testDateString = "2015-08-04"; + private final String testTimeString = "12:34:56"; + private final String testDateTimeString = testDateString + " " + testTimeString + ".0"; + private final String testISODateTimeString = testDateString + "T" + testTimeString + ".0"; + + private final Date testSqlDate = Date.valueOf(testDateString); + private final Time testSqlTime = Time.valueOf(testTimeString); + private final Timestamp testSqlTimeStamp = Timestamp.valueOf(testDateTimeString); + + private final LocalDate testLocalDate = LocalDate.parse(testDateString); + private final LocalTime testLocalTime = LocalTime.parse(testTimeString); + private final LocalDateTime testLocalDateTime = LocalDateTime.parse(testISODateTimeString); + + private final OffsetDateTime testOffsetDateTime = OffsetDateTime.of(2015, 8, 04, 12, 34, 56, 7890, ZoneOffset.UTC); + private final OffsetTime testOffsetTime = OffsetTime.of(12, 34, 56, 7890, ZoneOffset.UTC); + + public StatementsTest(String name) { + super(name); + } + + /** + * Test shared test data validity. + */ + public void testSharedTestData() throws Exception { + assertEquals(testSqlDate, Date.valueOf(testLocalDate)); + assertEquals(testSqlTime, Time.valueOf(testLocalTime)); + assertEquals(testSqlTimeStamp, Timestamp.valueOf(testLocalDateTime)); + + assertEquals(testLocalDate, testSqlDate.toLocalDate()); + assertEquals(testLocalTime, testSqlTime.toLocalTime()); + assertEquals(testLocalDateTime, testSqlTimeStamp.toLocalDateTime()); + } + + /** + * Test for Statement.executeLargeBatch(). Validate update count returned and generated keys. + */ + public void testStmtExecuteLargeBatch() throws Exception { + /* + * Fully working batch + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (1)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (2)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (3)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (4)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (8)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + long[] counts = this.stmt.executeLargeBatch(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(1, counts[5]); + assertEquals(2, counts[6]); + + this.rs = this.stmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + long generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(10, generatedKey); + this.rs.close(); + + /* + * Batch with failing queries + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (1)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (2)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch VALUES (3)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (4)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES ('eight')"); + this.stmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + try { + this.stmt.executeLargeBatch(); + fail("BatchUpdateException expected"); + } catch (BatchUpdateException e) { + assertEquals("Incorrect integer value: 'eight' for column 'n' at row 1", e.getMessage()); + counts = e.getLargeUpdateCounts(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(Statement.EXECUTE_FAILED, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(Statement.EXECUTE_FAILED, counts[5]); + assertEquals(2, counts[6]); + } catch (Exception e) { + fail("BatchUpdateException expected"); + } + + this.rs = this.stmt.getGeneratedKeys(); + generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(8, generatedKey); + this.rs.close(); + } + + /** + * Test for Statement.executeLargeUpdate(String). + * Validate update count returned and generated keys. + * Case: without requesting generated keys. + */ + public void testStmtExecuteLargeUpdateNoGeneratedKeys() throws Exception { + createTable("testExecuteLargeUpdate", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + long count = this.stmt.executeLargeUpdate("INSERT INTO testExecuteLargeUpdate (n) VALUES (1), (2), (3), (4), (5)"); + assertEquals(5, count); + assertEquals(5, this.stmt.getLargeUpdateCount()); + + final Statement stmtTmp = this.stmt; + assertThrows(SQLException.class, "Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate\\(\\), " + + "Statement.executeLargeUpdate\\(\\) or Connection.prepareStatement\\(\\).", new Callable() { + public Void call() throws Exception { + stmtTmp.getGeneratedKeys(); + return null; + } + }); + } + + /** + * Test for Statement.executeLargeUpdate(String, _). + * Validate update count returned and generated keys. + * Case 1: explicitly requesting generated keys. + * Case 2: requesting generated keys by defining column indexes. + * Case 3: requesting generated keys by defining column names. + */ + public void testStmtExecuteLargeUpdate() throws Exception { + createTable("testExecuteLargeUpdate", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + for (int tst = 1; tst <= 3; tst++) { + this.stmt.execute("TRUNCATE TABLE testExecuteLargeUpdate"); + String tstCase = "Case " + tst; + long count = 0; + switch (tst) { + case 1: + count = this.stmt.executeLargeUpdate("INSERT INTO testExecuteLargeUpdate (n) VALUES (1), (2), (3), (4), (5)", + Statement.RETURN_GENERATED_KEYS); + break; + case 2: + count = this.stmt.executeLargeUpdate("INSERT INTO testExecuteLargeUpdate (n) VALUES (1), (2), (3), (4), (5)", new int[] { 1 }); + break; + case 3: + count = this.stmt.executeLargeUpdate("INSERT INTO testExecuteLargeUpdate (n) VALUES (1), (2), (3), (4), (5)", new String[] { "id" }); + break; + } + assertEquals(tstCase, 5, count); + assertEquals(tstCase, 5, this.stmt.getLargeUpdateCount()); + + this.rs = this.stmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(tstCase, 1, rsmd.getColumnCount()); + assertEquals(tstCase, JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(tstCase, 20, rsmd.getColumnDisplaySize(1)); + + long generatedKey = 0; + while (this.rs.next()) { + assertEquals(tstCase, ++generatedKey, this.rs.getLong(1)); + } + assertEquals(tstCase, 5, generatedKey); + this.rs.close(); + } + } + + /** + * Test for PreparedStatement.executeLargeBatch(). + * Validate update count returned and generated keys. + */ + public void testPrepStmtExecuteLargeBatch() throws Exception { + /* + * Fully working batch + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testExecuteLargeBatch (n) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 4); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.pstmt.setInt(1, 8); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + long[] counts = this.pstmt.executeLargeBatch(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(1, counts[5]); + assertEquals(2, counts[6]); + + this.rs = this.pstmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + long generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(10, generatedKey); + this.rs.close(); + + /* + * Batch with failing queries + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testExecuteLargeBatch (n) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 4); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.pstmt.setString(1, "eight"); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + try { + this.pstmt.executeLargeBatch(); + fail("BatchUpdateException expected"); + } catch (BatchUpdateException e) { + assertEquals("Incorrect integer value: 'eight' for column 'n' at row 1", e.getMessage()); + counts = e.getLargeUpdateCounts(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(Statement.EXECUTE_FAILED, counts[5]); + assertEquals(2, counts[6]); + } catch (Exception e) { + fail("BatchUpdateException expected"); + } + + this.rs = this.pstmt.getGeneratedKeys(); + generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(9, generatedKey); + this.rs.close(); + } + + /** + * Test for PreparedStatement.executeLargeUpdate(). + * Validate update count returned and generated keys. + * Case: without requesting generated keys. + */ + public void testPrepStmtExecuteLargeUpdateNoGeneratedKeys() throws Exception { + createTable("testExecuteLargeUpdate", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testExecuteLargeUpdate (n) VALUES (?), (?), (?), (?), (?)"); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.setInt(3, 3); + this.pstmt.setInt(4, 4); + this.pstmt.setInt(5, 5); + + long count = this.pstmt.executeLargeUpdate(); + assertEquals(5, count); + assertEquals(5, this.pstmt.getLargeUpdateCount()); + + final Statement stmtTmp = this.pstmt; + assertThrows(SQLException.class, "Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate\\(\\), " + + "Statement.executeLargeUpdate\\(\\) or Connection.prepareStatement\\(\\).", new Callable() { + public Void call() throws Exception { + stmtTmp.getGeneratedKeys(); + return null; + } + }); + } + + /** + * Test for PreparedStatement.executeLargeUpdate(). + * Validate update count returned and generated keys. + * Case: explicitly requesting generated keys. + */ + public void testPrepStmtExecuteLargeUpdateExplicitGeneratedKeys() throws Exception { + createTable("testExecuteLargeUpdate", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testExecuteLargeUpdate (n) VALUES (?), (?), (?), (?), (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setInt(1, 1); + this.pstmt.setInt(2, 2); + this.pstmt.setInt(3, 3); + this.pstmt.setInt(4, 4); + this.pstmt.setInt(5, 5); + + long count = this.pstmt.executeLargeUpdate(); + assertEquals(5, count); + assertEquals(5, this.pstmt.getLargeUpdateCount()); + + this.rs = this.pstmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + long generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(5, generatedKey); + this.rs.close(); + } + + /** + * Test for CallableStatement.executeLargeBatch(). + * Validate update count returned and generated keys. + */ + public void testCallStmtExecuteLargeBatch() throws Exception { + /* + * Fully working batch + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + createProcedure("testExecuteLargeBatchProc", "(IN n INT) BEGIN INSERT INTO testExecuteLargeBatch (n) VALUES (n); END"); + + CallableStatement testCstmt = this.conn.prepareCall("{CALL testExecuteLargeBatchProc(?)}"); + testCstmt.setInt(1, 1); + testCstmt.addBatch(); + testCstmt.setInt(1, 2); + testCstmt.addBatch(); + testCstmt.setInt(1, 3); + testCstmt.addBatch(); + testCstmt.setInt(1, 4); + testCstmt.addBatch(); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(5)}"); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(6)}"); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(7)}"); + testCstmt.setInt(1, 8); + testCstmt.addBatch(); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(9)}"); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(10)}"); + + long[] counts = testCstmt.executeLargeBatch(); + assertEquals(10, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(1, counts[4]); + assertEquals(1, counts[5]); + assertEquals(1, counts[6]); + assertEquals(1, counts[7]); + assertEquals(1, counts[8]); + assertEquals(1, counts[9]); + + this.rs = testCstmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + // We can't check the generated keys as they are not returned correctly in this case (last_insert_id is missing from OK_PACKET when executing inserts + // within a stored procedure - Bug#21792359). + // long generatedKey = 0; + // while (this.rs.next()) { + // assertEquals(++generatedKey, this.rs.getLong(1)); + // } + // assertEquals(10, generatedKey); + this.rs.close(); + + testCstmt.close(); + + /* + * Batch with failing queries + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + testCstmt = this.conn.prepareCall("{call testExecuteLargeBatchProc(?)}"); + testCstmt.setInt(1, 1); + testCstmt.addBatch(); + testCstmt.setInt(1, 2); + testCstmt.addBatch(); + testCstmt.setInt(1, 3); + testCstmt.addBatch(); + testCstmt.setInt(1, 4); + testCstmt.addBatch(); + testCstmt.addBatch("{call testExecuteLargeBatchProc(5)}"); + testCstmt.addBatch("{call testExecuteLargeBatchProc('six')}"); + testCstmt.addBatch("{call testExecuteLargeBatchProc(7)}"); + testCstmt.setString(1, "eight"); + testCstmt.addBatch(); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(9)}"); + testCstmt.addBatch("{CALL testExecuteLargeBatchProc(10)}"); + + try { + testCstmt.executeLargeBatch(); + fail("BatchUpdateException expected"); + } catch (BatchUpdateException e) { + assertEquals("Incorrect integer value: 'eight' for column 'n' at row 1", e.getMessage()); + counts = e.getLargeUpdateCounts(); + assertEquals(10, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(1, counts[4]); + assertEquals(Statement.EXECUTE_FAILED, counts[5]); + assertEquals(1, counts[6]); + assertEquals(Statement.EXECUTE_FAILED, counts[7]); + assertEquals(1, counts[8]); + assertEquals(1, counts[9]); + } catch (Exception e) { + fail("BatchUpdateException expected"); + } + + testCstmt.close(); + } + + /** + * Test for CallableStatement.executeLargeUpdate(). + * Validate update count returned and generated keys. + */ + public void testCallStmtExecuteLargeUpdate() throws Exception { + createTable("testExecuteLargeUpdate", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + createProcedure("testExecuteLargeUpdateProc", "(IN n1 INT, IN n2 INT, IN n3 INT, IN n4 INT, IN n5 INT) BEGIN " + + "INSERT INTO testExecuteLargeUpdate (n) VALUES (n1), (n2), (n3), (n4), (n5); END"); + + CallableStatement testCstmt = this.conn.prepareCall("{CALL testExecuteLargeUpdateProc(?, ?, ?, ?, ?)}"); + testCstmt.setInt(1, 1); + testCstmt.setInt(2, 2); + testCstmt.setInt(3, 3); + testCstmt.setInt(4, 4); + testCstmt.setInt(5, 5); + + long count = testCstmt.executeLargeUpdate(); + assertEquals(5, count); + assertEquals(5, testCstmt.getLargeUpdateCount()); + + this.rs = testCstmt.getGeneratedKeys(); + + // Although not requested, CallableStatements makes gerenated keys always available. + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + // We can't check the generated keys as they are not returned correctly in this case (last_insert_id is missing from OK_PACKET when executing inserts + // within a stored procedure - Bug#21792359). + // long generatedKey = 0; + // while (this.rs.next()) { + // assertEquals(++generatedKey, this.rs.getLong(1)); + // } + // assertEquals(5, generatedKey); + this.rs.close(); + } + + /** + * Test for (Server)PreparedStatement.executeLargeBatch(). + * Validate update count returned and generated keys. + */ + public void testServerPrepStmtExecuteLargeBatch() throws Exception { + /* + * Fully working batch + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + Connection testConn = getConnectionWithProps("useServerPrepStmts=true"); + + this.pstmt = testConn.prepareStatement("INSERT INTO testExecuteLargeBatch (n) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 4); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.pstmt.setInt(1, 8); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + long[] counts = this.pstmt.executeLargeBatch(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(1, counts[5]); + assertEquals(2, counts[6]); + + this.rs = this.pstmt.getGeneratedKeys(); + + ResultSetMetaData rsmd = this.rs.getMetaData(); + assertEquals(1, rsmd.getColumnCount()); + assertEquals(JDBCType.BIGINT.getVendorTypeNumber().intValue(), rsmd.getColumnType(1)); + assertEquals(20, rsmd.getColumnDisplaySize(1)); + + long generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(10, generatedKey); + this.rs.close(); + + /* + * Batch with failing queries + */ + createTable("testExecuteLargeBatch", "(id BIGINT AUTO_INCREMENT PRIMARY KEY, n INT)"); + + this.pstmt = testConn.prepareStatement("INSERT INTO testExecuteLargeBatch (n) VALUES (?)", Statement.RETURN_GENERATED_KEYS); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 4); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (5), (6), (7)"); + this.pstmt.setString(1, "eight"); + this.pstmt.addBatch(); + this.pstmt.addBatch("INSERT INTO testExecuteLargeBatch (n) VALUES (9), (10)"); + + try { + this.pstmt.executeLargeBatch(); + fail("BatchUpdateException expected"); + } catch (BatchUpdateException e) { + assertEquals("Incorrect integer value: 'eight' for column 'n' at row 1", e.getMessage()); + counts = e.getLargeUpdateCounts(); + assertEquals(7, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(1, counts[3]); + assertEquals(3, counts[4]); + assertEquals(Statement.EXECUTE_FAILED, counts[5]); + assertEquals(2, counts[6]); + } + + this.rs = this.pstmt.getGeneratedKeys(); + generatedKey = 0; + while (this.rs.next()) { + assertEquals(++generatedKey, this.rs.getLong(1)); + } + assertEquals(9, generatedKey); + this.rs.close(); + + testConn.close(); + } + + /** + * Test for Statement.[get/set]LargeMaxRows(). + */ + public void testStmtGetSetLargeMaxRows() throws Exception { + assertEquals(0, this.stmt.getMaxRows()); + assertEquals(0, this.stmt.getLargeMaxRows()); + + this.stmt.setMaxRows(50000000); + + assertEquals(50000000, this.stmt.getMaxRows()); + assertEquals(50000000, this.stmt.getLargeMaxRows()); + + final Statement stmtTmp = this.stmt; + assertThrows(SQLException.class, "setMaxRows\\(\\) out of range. 50000001 > 50000000.", new Callable() { + public Void call() throws Exception { + stmtTmp.setMaxRows(50000001); + return null; + } + }); + + this.stmt.setLargeMaxRows(0); + + assertEquals(0, this.stmt.getMaxRows()); + assertEquals(0, this.stmt.getLargeMaxRows()); + + this.stmt.setLargeMaxRows(50000000); + + assertEquals(50000000, this.stmt.getMaxRows()); + assertEquals(50000000, this.stmt.getLargeMaxRows()); + + assertThrows(SQLException.class, "setMaxRows\\(\\) out of range. 50000001 > 50000000.", new Callable() { + public Void call() throws Exception { + stmtTmp.setLargeMaxRows(50000001L); + return null; + } + }); + } + + /** + * Test for PreparedStatement.setObject(). + * Validate new methods as well as support for the types java.time.Local[Date][Time] and java.time.Offset[Date]Time. + */ + public void testPrepStmtSetObjectAndNewSupportedTypes() throws Exception { + /* + * Objects java.time.Local[Date][Time] are supported via conversion to/from java.sql.[Date|Time|Timestamp]. + */ + createTable("testSetObjectPS1", "(id INT, d DATE, t TIME, dt DATETIME, ts TIMESTAMP)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testSetObjectPS1 VALUES (?, ?, ?, ?, ?)"); + validateTestDataLocalDTTypes("testSetObjectPS1", insertTestDataLocalDTTypes(this.pstmt)); + + /* + * Objects java.time.Offset[Date]Time are supported via conversion to *CHAR or serialization. + */ + createTable("testSetObjectPS2", "(id INT, ot1 VARCHAR(100), ot2 BLOB, odt1 VARCHAR(100), odt2 BLOB)"); + + this.pstmt = this.conn.prepareStatement("INSERT INTO testSetObjectPS2 VALUES (?, ?, ?, ?, ?)"); + validateTestDataOffsetDTTypes("testSetObjectPS2", insertTestDataOffsetDTTypes(this.pstmt)); + } + + /** + * Test for PreparedStatement.setObject(), unsupported SQL types TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE and REF_CURSOR. + */ + public void testPrepStmtSetObjectAndNewUnsupportedTypes() throws Exception { + checkUnsupportedTypesBehavior(this.conn.prepareStatement("SELECT ?")); + } + + /** + * Test for CallableStatement.setObject(). + * Validate new methods as well as support for the types java.time.Local[Date][Time] and java.time.Offset[Date]Time. + */ + public void testCallStmtSetObjectAndNewSupportedTypes() throws Exception { + /* + * Objects java.time.Local[Date][Time] are supported via conversion to/from java.sql.[Date|Time|Timestamp]. + */ + createTable("testSetObjectCS1", "(id INT, d DATE, t TIME, dt DATETIME, ts TIMESTAMP)"); + createProcedure("testSetObjectCS1Proc", + "(IN id INT, IN d DATE, IN t TIME, IN dt DATETIME, IN ts TIMESTAMP) BEGIN " + "INSERT INTO testSetObjectCS1 VALUES (id, d, t, dt, ts); END"); + + CallableStatement testCstmt = this.conn.prepareCall("{CALL testSetObjectCS1Proc(?, ?, ?, ?, ?)}"); + validateTestDataLocalDTTypes("testSetObjectCS1", insertTestDataLocalDTTypes(testCstmt)); + + /* + * Objects java.time.Offset[Date]Time are supported via conversion to *CHAR or serialization. + */ + createTable("testSetObjectCS2", "(id INT, ot1 VARCHAR(100), ot2 BLOB, odt1 VARCHAR(100), odt2 BLOB)"); + createProcedure("testSetObjectCS2Proc", + "(id INT, ot1 VARCHAR(100), ot2 BLOB, odt1 VARCHAR(100), odt2 BLOB) BEGIN INSERT INTO testSetObjectCS2 VALUES (id, ot1, ot2, odt1, odt2); END"); + + testCstmt = this.conn.prepareCall("{CALL testSetObjectCS2Proc(?, ?, ?, ?, ?)}"); + validateTestDataOffsetDTTypes("testSetObjectCS2", insertTestDataOffsetDTTypes(testCstmt)); + } + + /** + * Test for CallableStatement.setObject(), unsupported SQL types TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE and REF_CURSOR. + */ + public void testCallStmtSetObjectAndNewUnsupportedTypes() throws Exception { + createProcedure("testUnsupportedTypesProc", "(OUT param VARCHAR(20)) BEGIN SELECT 1; END"); + checkUnsupportedTypesBehavior(this.conn.prepareCall("{CALL testUnsupportedTypesProc(?)}")); + } + + /** + * Test for (Server)PreparedStatement.setObject(). + * Validate new methods as well as support for the types java.time.Local[Date][Time] and java.time.Offset[Date]Time. + */ + public void testServPrepStmtSetObjectAndNewSupportedTypes() throws Exception { + /* + * Objects java.time.Local[Date][Time] are supported via conversion to/from java.sql.[Date|Time|Timestamp]. + */ + createTable("testSetObjectSPS1", "(id INT, d DATE, t TIME, dt DATETIME, ts TIMESTAMP)"); + + Connection testConn = getConnectionWithProps("useServerPrepStmts=true"); + + this.pstmt = testConn.prepareStatement("INSERT INTO testSetObjectSPS1 VALUES (?, ?, ?, ?, ?)"); + validateTestDataLocalDTTypes("testSetObjectSPS1", insertTestDataLocalDTTypes(this.pstmt)); + + /* + * Objects java.time.Offset[Date]Time are supported via conversion to *CHAR or serialization. + */ + createTable("testSetObjectSPS2", "(id INT, ot1 VARCHAR(100), ot2 BLOB, odt1 VARCHAR(100), odt2 BLOB)"); + + this.pstmt = testConn.prepareStatement("INSERT INTO testSetObjectSPS2 VALUES (?, ?, ?, ?, ?)"); + validateTestDataOffsetDTTypes("testSetObjectSPS2", insertTestDataOffsetDTTypes(this.pstmt)); + } + + /** + * Test for (Server)PreparedStatement.setObject(), unsupported SQL types TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE and REF_CURSOR. + */ + public void testServPrepStmtSetObjectAndNewUnsupportedTypes() throws Exception { + Connection testConn = getConnectionWithProps("useServerPrepStmts=true"); + checkUnsupportedTypesBehavior(testConn.prepareStatement("SELECT ?")); + testConn.close(); + } + + /** + * Helper method for *SetObject* tests. + * Insert data into the given PreparedStatement, or any of its subclasses, with the following structure: + * 1 - `id` INT + * 2 - `d` DATE (or any kind of *CHAR) + * 3 - `t` TIME (or any kind of *CHAR) + * 4 - `dt` DATETIME (or any kind of *CHAR) + * 5 - `ts` TIMESTAMP (or any kind of *CHAR) + * + * @param pstmt + * @return the row count of inserted records. + * @throws Exception + */ + private int insertTestDataLocalDTTypes(PreparedStatement pstmt) throws Exception { + pstmt.setInt(1, 1); + pstmt.setDate(2, testSqlDate); + pstmt.setTime(3, testSqlTime); + pstmt.setTimestamp(4, testSqlTimeStamp); + pstmt.setTimestamp(5, testSqlTimeStamp); + assertEquals(1, pstmt.executeUpdate()); + + pstmt.setInt(1, 2); + pstmt.setObject(2, testLocalDate); + pstmt.setObject(3, testLocalTime); + pstmt.setObject(4, testLocalDateTime); + pstmt.setObject(5, testLocalDateTime); + assertEquals(1, pstmt.executeUpdate()); + + pstmt.setInt(1, 3); + pstmt.setObject(2, testLocalDate, JDBCType.DATE); + pstmt.setObject(3, testLocalTime, JDBCType.TIME); + pstmt.setObject(4, testLocalDateTime, JDBCType.TIMESTAMP); + pstmt.setObject(5, testLocalDateTime, JDBCType.TIMESTAMP); + assertEquals(1, pstmt.executeUpdate()); + + pstmt.setInt(1, 4); + pstmt.setObject(2, testLocalDate, JDBCType.DATE, 10); + pstmt.setObject(3, testLocalTime, JDBCType.TIME, 8); + pstmt.setObject(4, testLocalDateTime, JDBCType.TIMESTAMP, 20); + pstmt.setObject(5, testLocalDateTime, JDBCType.TIMESTAMP, 20); + assertEquals(1, pstmt.executeUpdate()); + + pstmt.setInt(1, 5); + pstmt.setObject(2, testLocalDate, JDBCType.VARCHAR); + pstmt.setObject(3, testLocalTime, JDBCType.VARCHAR); + pstmt.setObject(4, testLocalDateTime, JDBCType.VARCHAR); + pstmt.setObject(5, testLocalDateTime, JDBCType.VARCHAR); + assertEquals(1, pstmt.executeUpdate()); + + pstmt.setInt(1, 6); + pstmt.setObject(2, testLocalDate, JDBCType.VARCHAR, 10); + pstmt.setObject(3, testLocalTime, JDBCType.VARCHAR, 8); + pstmt.setObject(4, testLocalDateTime, JDBCType.VARCHAR, 20); + pstmt.setObject(5, testLocalDateTime, JDBCType.VARCHAR, 20); + assertEquals(1, pstmt.executeUpdate()); + + if (pstmt instanceof CallableStatement) { + CallableStatement cstmt = (CallableStatement) pstmt; + + cstmt.setInt("id", 7); + cstmt.setDate("d", testSqlDate); + cstmt.setTime("t", testSqlTime); + cstmt.setTimestamp("dt", testSqlTimeStamp); + cstmt.setTimestamp("ts", testSqlTimeStamp); + assertEquals(1, cstmt.executeUpdate()); + + cstmt.setInt("id", 8); + cstmt.setObject("d", testLocalDate); + cstmt.setObject("t", testLocalTime); + cstmt.setObject("dt", testLocalDateTime); + cstmt.setObject("ts", testLocalDateTime); + assertEquals(1, cstmt.executeUpdate()); + + cstmt.setInt("id", 9); + cstmt.setObject("d", testLocalDate, JDBCType.DATE); + cstmt.setObject("t", testLocalTime, JDBCType.TIME); + cstmt.setObject("dt", testLocalDateTime, JDBCType.TIMESTAMP); + cstmt.setObject("ts", testLocalDateTime, JDBCType.TIMESTAMP); + assertEquals(1, cstmt.executeUpdate()); + + cstmt.setInt("id", 10); + cstmt.setObject("d", testLocalDate, JDBCType.DATE, 10); + cstmt.setObject("t", testLocalTime, JDBCType.TIME, 8); + cstmt.setObject("dt", testLocalDateTime, JDBCType.TIMESTAMP, 20); + cstmt.setObject("ts", testLocalDateTime, JDBCType.TIMESTAMP, 20); + assertEquals(1, cstmt.executeUpdate()); + + cstmt.setInt("id", 11); + cstmt.setObject("d", testLocalDate, JDBCType.VARCHAR); + cstmt.setObject("t", testLocalTime, JDBCType.VARCHAR); + cstmt.setObject("dt", testLocalDateTime, JDBCType.VARCHAR); + cstmt.setObject("ts", testLocalDateTime, JDBCType.VARCHAR); + assertEquals(1, cstmt.executeUpdate()); + + cstmt.setInt("id", 12); + cstmt.setObject("d", testLocalDate, JDBCType.VARCHAR, 10); + cstmt.setObject("t", testLocalTime, JDBCType.VARCHAR, 8); + cstmt.setObject("dt", testLocalDateTime, JDBCType.VARCHAR, 20); + cstmt.setObject("ts", testLocalDateTime, JDBCType.VARCHAR, 20); + assertEquals(1, cstmt.executeUpdate()); + + return 12; + } + + return 6; + } + + /** + * Helper method for *SetObject* tests. + * Validate the test data contained in the given ResultSet with following structure: + * 1 - `id` INT + * 2 - `d` DATE (or any kind of *CHAR) + * 3 - `t` TIME (or any kind of *CHAR) + * 4 - `dt` DATETIME (or any kind of *CHAR) + * 5 - `ts` TIMESTAMP (or any kind of *CHAR) + * + * Additionally validate support for the types java.time.Local[Date][Time] in ResultSet.getObject(). + * + * @param tableName + * @param expectedRowCount + * @throws Exception + */ + private void validateTestDataLocalDTTypes(String tableName, int expectedRowCount) throws Exception { + this.rs = this.stmt.executeQuery("SELECT * FROM " + tableName); + + int rowCount = 0; + while (rs.next()) { + String row = "Row " + this.rs.getInt(1); + assertEquals(row, ++rowCount, this.rs.getInt(1)); + + assertEquals(row, testDateString, this.rs.getString(2)); + assertEquals(row, testTimeString, this.rs.getString(3)); + assertEquals(row, testDateTimeString, this.rs.getString(4)); + assertEquals(row, testDateTimeString, this.rs.getString(5)); + + assertEquals(row, testSqlDate, this.rs.getDate(2)); + assertEquals(row, testSqlTime, this.rs.getTime(3)); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp(4)); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp(5)); + + assertEquals(row, testLocalDate, this.rs.getObject(2, LocalDate.class)); + assertEquals(row, testLocalTime, this.rs.getObject(3, LocalTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject(4, LocalDateTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject(5, LocalDateTime.class)); + + assertEquals(row, rowCount, this.rs.getInt("id")); + + assertEquals(row, testDateString, this.rs.getString("d")); + assertEquals(row, testTimeString, this.rs.getString("t")); + assertEquals(row, testDateTimeString, this.rs.getString("dt")); + assertEquals(row, testDateTimeString, this.rs.getString("ts")); + + assertEquals(row, testSqlDate, this.rs.getDate("d")); + assertEquals(row, testSqlTime, this.rs.getTime("t")); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp("dt")); + assertEquals(row, testSqlTimeStamp, this.rs.getTimestamp("ts")); + + assertEquals(row, testLocalDate, this.rs.getObject("d", LocalDate.class)); + assertEquals(row, testLocalTime, this.rs.getObject("t", LocalTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject("dt", LocalDateTime.class)); + assertEquals(row, testLocalDateTime, this.rs.getObject("ts", LocalDateTime.class)); + } + assertEquals(expectedRowCount, rowCount); + } + + /** + * Helper method for *SetObject* tests. + * Insert data into the given PreparedStatement, or any of its subclasses, with the following structure: + * 1 - `id` INT + * 2 - `ot1` VARCHAR + * 3 - `ot2` BLOB + * 4 - `odt1` VARCHAR + * 5 - `odt2` BLOB + * + * @param pstmt + * @return the row count of inserted records. + * @throws Exception + */ + private int insertTestDataOffsetDTTypes(PreparedStatement pstmt) throws Exception { + pstmt.setInt(1, 1); + pstmt.setObject(2, testOffsetTime, JDBCType.VARCHAR); + pstmt.setObject(3, testOffsetTime); + pstmt.setObject(4, testOffsetDateTime, JDBCType.VARCHAR); + pstmt.setObject(5, testOffsetDateTime); + assertEquals(1, pstmt.executeUpdate()); + + if (pstmt instanceof CallableStatement) { + CallableStatement cstmt = (CallableStatement) pstmt; + + cstmt.setInt("id", 2); + cstmt.setObject("ot1", testOffsetTime, JDBCType.VARCHAR); + cstmt.setObject("ot2", testOffsetTime); + cstmt.setObject("odt1", testOffsetDateTime, JDBCType.VARCHAR); + cstmt.setObject("odt2", testOffsetDateTime); + assertEquals(1, cstmt.executeUpdate()); + + return 2; + } + + return 1; + } + + /** + * Helper method for *SetObject* tests. + * Validate the test data contained in the given ResultSet with following structure: + * 1 - `id` INT + * 2 - `ot1` VARCHAR + * 3 - `ot2` BLOB + * 4 - `odt1` VARCHAR + * 5 - `odt2` BLOB + * + * Additionally validate support for the types java.time.Offset[Date]Time in ResultSet.getObject(). + * + * @param tableName + * @param expectedRowCount + * @throws Exception + */ + private void validateTestDataOffsetDTTypes(String tableName, int expectedRowCount) throws Exception { + Connection testConn = getConnectionWithProps("autoDeserialize=true"); // Offset[Date]Time are supported via object serialization too. + Statement testStmt = testConn.createStatement(); + this.rs = testStmt.executeQuery("SELECT * FROM " + tableName); + + int rowCount = 0; + while (rs.next()) { + String row = "Row " + rs.getInt(1); + assertEquals(++rowCount, rs.getInt(1)); + + assertEquals(row, testOffsetTime, this.rs.getObject(2, OffsetTime.class)); + assertEquals(row, testOffsetTime, this.rs.getObject(3, OffsetTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject(4, OffsetDateTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject(5, OffsetDateTime.class)); + + assertEquals(row, rowCount, this.rs.getInt("id")); + + assertEquals(row, testOffsetTime, this.rs.getObject("ot1", OffsetTime.class)); + assertEquals(row, testOffsetTime, this.rs.getObject("ot2", OffsetTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject("odt1", OffsetDateTime.class)); + assertEquals(row, testOffsetDateTime, this.rs.getObject("odt2", OffsetDateTime.class)); + } + assertEquals(expectedRowCount, rowCount); + testConn.close(); + } + + /** + * Helper method for *SetObject* tests. + * Check unsupported types behavior for the given PreparedStatement with a single placeholder. If this is a CallableStatement then the placeholder must + * coincide with a parameter named `param`. + * + * @param pstmt + */ + private void checkUnsupportedTypesBehavior(final PreparedStatement pstmt) { + final CallableStatement cstmt = pstmt instanceof CallableStatement ? (CallableStatement) pstmt : null; + + /* + * Unsupported SQL types TIME_WITH_TIMEZONE and TIMESTAMP_WITH_TIMEZONE. + */ + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + pstmt.setObject(1, OffsetTime.now(), JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + pstmt.setObject(1, OffsetDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + if (cstmt != null) { + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + cstmt.setObject("param", OffsetTime.now(), JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + cstmt.setObject("param", OffsetDateTime.now(), JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + } + /* + * Unsupported SQL type REF_CURSOR. + */ + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + pstmt.setObject(1, new Object(), JDBCType.REF_CURSOR); + return null; + } + }); + if (cstmt != null) { + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + cstmt.setObject("param", new Object(), JDBCType.REF_CURSOR); + return null; + } + }); + } + } + + /** + * Test for CallableStatement.registerOutParameter(). + */ + public void testCallStmtRegisterOutParameter() throws Exception { + createProcedure("testRegisterOutParameterProc", "(OUT b BIT, OUT i INT, OUT c CHAR(10)) BEGIN SELECT 1, 1234, 'MySQL' INTO b, i, c; END"); + final CallableStatement testCstmt = this.conn.prepareCall("{CALL testRegisterOutParameterProc(?, ?, ?)}"); + + // registerOutParameter by parameter index + testCstmt.registerOutParameter(1, JDBCType.BOOLEAN); + testCstmt.registerOutParameter(2, JDBCType.INTEGER); + testCstmt.registerOutParameter(3, JDBCType.CHAR); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + + testCstmt.registerOutParameter(1, JDBCType.BOOLEAN, 1); + testCstmt.registerOutParameter(2, JDBCType.INTEGER, 1); + testCstmt.registerOutParameter(3, JDBCType.CHAR, 1); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + + testCstmt.registerOutParameter(1, JDBCType.BOOLEAN, "dummy"); + testCstmt.registerOutParameter(2, JDBCType.INTEGER, "dummy"); + testCstmt.registerOutParameter(3, JDBCType.CHAR, "dummy"); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + + // registerOutParameter by parameter name + testCstmt.registerOutParameter("b", JDBCType.BOOLEAN); + testCstmt.registerOutParameter("i", JDBCType.INTEGER); + testCstmt.registerOutParameter("c", JDBCType.CHAR); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + + testCstmt.registerOutParameter("b", JDBCType.BOOLEAN, 1); + testCstmt.registerOutParameter("i", JDBCType.INTEGER, 1); + testCstmt.registerOutParameter("c", JDBCType.CHAR, 1); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + + testCstmt.registerOutParameter("b", JDBCType.BOOLEAN, "dummy"); + testCstmt.registerOutParameter("i", JDBCType.INTEGER, "dummy"); + testCstmt.registerOutParameter("c", JDBCType.CHAR, "dummy"); + testCstmt.execute(); + + assertEquals(Boolean.TRUE, testCstmt.getObject(1)); + assertEquals(Integer.valueOf(1234), testCstmt.getObject(2)); + assertEquals("MySQL", testCstmt.getObject(3)); + } + + /** + * Test for CallableStatement.registerOutParameter(), unsupported SQL types TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE and REF_CURSOR. + */ + public void testCallStmtRegisterOutParameterNewUnsupportedTypes() throws Exception { + createProcedure("testUnsupportedTypesProc", "(OUT param VARCHAR(20)) BEGIN SELECT 1; END"); + final CallableStatement testCstmt = this.conn.prepareCall("{CALL testUnsupportedTypesProc(?)}"); + + /* + * Unsupported SQL types TIME_WITH_TIMEZONE and TIMESTAMP_WITH_TIMEZONE. + */ + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIME_WITH_TIMEZONE, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIME_WITH_TIMEZONE, "dummy"); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIME_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIME_WITH_TIMEZONE, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIME_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIME_WITH_TIMEZONE, "dummy"); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIMESTAMP_WITH_TIMEZONE, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.TIMESTAMP_WITH_TIMEZONE, "dummy"); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIMESTAMP_WITH_TIMEZONE); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIMESTAMP_WITH_TIMEZONE, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: TIMESTAMP_WITH_TIMEZONE", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.TIMESTAMP_WITH_TIMEZONE, "dummy"); + return null; + } + }); + + /* + * Unsupported SQL type REF_CURSOR. + */ + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.REF_CURSOR); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.REF_CURSOR, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter(1, JDBCType.REF_CURSOR, "dummy"); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.REF_CURSOR); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.REF_CURSOR, 1); + return null; + } + }); + assertThrows(SQLFeatureNotSupportedException.class, "Unsupported SQL type: REF_CURSOR", new Callable() { + @Override + public Void call() throws Exception { + testCstmt.registerOutParameter("param", JDBCType.REF_CURSOR, "dummy"); + return null; + } + }); + } +} \ No newline at end of file diff --git a/mysql-connector-java-5.1.40/src/testsuite/simple/tb2-data.txt.gz b/mysql-connector-java-5.1.40/src/testsuite/simple/tb2-data.txt.gz new file mode 100644 index 0000000..9350a9d Binary files /dev/null and b/mysql-connector-java-5.1.40/src/testsuite/simple/tb2-data.txt.gz differ diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-cert.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-cert.pem new file mode 100644 index 0000000..7e39d72 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIJAJ8S2uoC12H6MA0GCSqGSIb3DQEBCwUAMIGXMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UEBwwHUmVkd29vZDEP +MA0GA1UECgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDESMBAGA1UEAwwJbG9jYWxo +b3N0MSwwKgYJKoZIhvcNAQkBFh1hbGV4YW5kZXIuc29rbGFrb3ZAb3JhY2xlLmNv +bTAeFw0xNjA2MjIxMDAwNThaFw0yNjA2MjAxMDAwNThaMIGXMQswCQYDVQQGEwJV +UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEQMA4GA1UEBwwHUmVkd29vZDEPMA0GA1UE +CgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDESMBAGA1UEAwwJbG9jYWxob3N0MSww +KgYJKoZIhvcNAQkBFh1hbGV4YW5kZXIuc29rbGFrb3ZAb3JhY2xlLmNvbTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANPL0Q3YF6ouvKJdqhP1JjzuN0ay +62kZegjiV6WYH0+j+7otxukmuIymY/uzTjEtv1IaNW5/Z/PPZnux2a1N+Hrrd4Cu +UYcj4U0RYDId2JJZAEhdYqr94YuOAb1r8fPYD+9eKXRzwV6QJYCtcpbdwbtRHU4O +nFnW1kUX9Ic7NlMTgcCsHZNE5g6OzfelT0iS1NxBzzz6h3RSoFxe1pRfw4+2SHG1 +Hm0LLUb/LqDtfpqrN71OUqUTfnefioQFVwl0shcJeUqlNNTh89dwAFeZ0kgVUWBE +YCCpAweCi4+DHosBG07JWaih76p4XfcR0TMa9G49pb9hN4K488R9T3oCMacCAwEA +AaNQME4wHQYDVR0OBBYEFLjtdDzKgsO9QkFrAgc0oiDFcZfgMB8GA1UdIwQYMBaA +FLjtdDzKgsO9QkFrAgc0oiDFcZfgMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAKdVDtwoIijJ50ugR7hD+GLlTvLPvKDAUHNZrA04muqHEC1E99ZHf260 +b7ZFd96xAwCGXBzbOL8eara3DOQeXre0gWaID/24QKbo2r+Fv4FbrppsxNngxVlZ +f6yRJyBaK1PEJLTyHMwScmiDd3w9VrnpqKpsfi5HYnUps7j6+BEA1Y3FV+ZWlnkr +D6dLjVh/fjCP2dkxEe9iYbOyf6u8yOt1u40yoQNW69Ldf3W+C8ZoXnYPphAwxLGm +I6yensCMLApPy7z2QTheuTMi4LdUFtP8PxcrqwlP8Z2hkPnxY83+rhG3WiMsc8N1 +DrEq+HYZznCbWVLeFq2B1Fy/aPcJ8tk= +-----END CERTIFICATE----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-key.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-key.pem new file mode 100644 index 0000000..0ca150b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA08vRDdgXqi68ol2qE/UmPO43RrLraRl6COJXpZgfT6P7ui3G +6Sa4jKZj+7NOMS2/Uho1bn9n889me7HZrU34eut3gK5RhyPhTRFgMh3YklkASF1i +qv3hi44BvWvx89gP714pdHPBXpAlgK1ylt3Bu1EdTg6cWdbWRRf0hzs2UxOBwKwd +k0TmDo7N96VPSJLU3EHPPPqHdFKgXF7WlF/Dj7ZIcbUebQstRv8uoO1+mqs3vU5S +pRN+d5+KhAVXCXSyFwl5SqU01OHz13AAV5nSSBVRYERgIKkDB4KLj4MeiwEbTslZ +qKHvqnhd9xHRMxr0bj2lv2E3grjzxH1PegIxpwIDAQABAoIBACHQNWUPbd/GrGkD +qSacielKYtrPr9PMtHX8pN+UJNOTK5oyusH4Y5hY7DeADei+FABonMUyZAdBwmvX +HhiUDSqasy6BJhT8PnkOYBxANZZPzULAXgNMmxirqE10kWFLymd90gJ8hI+zVblw +d43sg4SDU7lljcKuEPFg/LImWuryNOP/hRE0fIT2lYjjQHkPMpsYPfhvqPP+Rn1G +0rlzh2XhUR8vJz9JfNVZCdzHGQIsnhrKn30s6zqrA9MGg8yot0sEpkwHY6tOOLXZ +6B7uG9L4ps/pLT7Yplg3xFgELEidHZYHQGLLzc43vKHEo3lczGhbIpNXQw8zJDQP +TMcNvgECgYEA88/EiJipttD4iL976Y26Y8EUAxNQlRMcrLSauRdLBDmMyRsX/r4I +kAcmLL65V02iHShbx0Y/B77Wct3MxNHUeGqPS2lLPA7oB011IiYKw2QQYAwaLy1r +bd8ryA92KO3UeMiNi3z1YRP4TmB96EmCvaywzfSYazxfkGTcFXWCWwECgYEA3mJT +CkgBu7ISmbFm2Z+MVShSTQoJbsE9IiEj2u5h3yJGMoYMgyHEicDK9UNW4K6YZrax +v5zeYzUGGfkCdD2h118D/r1qtvbQvs7dSa6OVIW2ENpqnASXILi3isQhTwvzseLe +y6Y5t8P+84lVnEgBuNHbQwtn+LhpA0k64mqc1KcCgYEAibFI+SvejRSvh+s8e8ZD +AZtFRgdedxJ4G0FUMDvrbdNioyDeCEwiYdTnxlVgLuH1MCpeysftSN6KFg0VXAJ0 +0By+GIS6rIxuldE02bhKU9brOJMdJo+sIDztaOryfGL0n9IDvkcv/Udd8EWmVc1O +PPiOUYJVohpSjiWn9aaLnAECgYEAwoWjOYUO7w2us324B3hGGl8wzm6PHbAuN2Jh +qKmINtQWLy+OVk39Srm3Tp5eqh5O9Nmt1Xv0EzdYZPOpr+ARZwCX0AYECJMQuTbd +3guwOELVpRxI0Lw7LcYl1l4E/M3plppqZBy8s//fTUu99PwPdYkrkBO0GJ3GTbeK +qG61TFUCgYBBmRhW6M+A5x67DUE7+MsdWH/v/iGslFZlAjj9mrxtwxfiBStjRp3O +aTkclm4LTCOGW9G1Gc+O1G19JMoGD7U7X6/A8YmwDJ1izJ+UI4bCMI3evVq3oT+e +Nq2REQlmJ0JFg3Y3RRjpVUQfSfKKSLofwtGxYQ4YUyfClYMKlKMHdw== +-----END RSA PRIVATE KEY----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/certs_howto.txt b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/certs_howto.txt new file mode 100644 index 0000000..f9c614f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/certs_howto.txt @@ -0,0 +1,55 @@ + +=================================== +STEPS TO GENERATE TEST CERTIFICATES +=================================== + +1. Create CA cert. +================== + +$ openssl genrsa 2048 > ca-key.pem + +$ openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem +Country Name (2 letter code) [XX]:US +State or Province Name (full name) []:California +Locality Name (eg, city) [Default City]:Redwood +Organization Name (eg, company) [Default Company Ltd]:Oracle +Organizational Unit Name (eg, section) []:MySQL +Common Name (eg, your name or your server's hostname) []:localhost +Email Address []:alexander.soklakov@oracle.com + + +2. Create server cert. +====================== + +$ openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem +Country Name (2 letter code) [XX]:US +State or Province Name (full name) []:California +Locality Name (eg, city) [Default City]:Redwood +Organization Name (eg, company) [Default Company Ltd]:Oracle +Organizational Unit Name (eg, section) []:MySQL +Common Name (eg, your name or your server's hostname) []:localhost +Email Address []:alexander.soklakov@oracle.com +A challenge password []:password +An optional company name []:password + +$ openssl rsa -in server-key.pem -out server-key.pem +$ openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem + +3. Create truststore +==================== + +$ keytool -importcert -alias mysqlservercacert -file ca-cert.pem -keystore test-cert-store +Enter keystore password: password +Re-enter new password: password +Trust this certificate? [no]: yes + +========================== +RUN SERVER WITH TEST CERTS +========================== +Add to my.conf: + +[mysqld] +ssl-key = "/path/server-key.pem" +ssl-cert = "/path/server-cert.pem" +ssl-ca = "/path/ca-cert.pem" + diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pem new file mode 100644 index 0000000..b14371e --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDBzoqQuwZ9/eKMOU0OXWJ4ISj+2YEk8l4OmBoC9Y2wNEe4Pcjz +mCF/f9aZDyH6znh0G6gmb/yrTvuNLYkTUgiFNm0yJ2rSzlgmJZHkWykRkjKr4V04 +iAaHdU4ORre7Ms9eln7k8CeVQFpCjM51HOLkp8IhnAVrkOhSbHI4vxprbQIDAQAB +AoGBAJbMD3L3ouCLmCfFOhiwPrr8CjrpoBvQXbD7VlnxGDL/KsLHiEFJoN7k6GQl +qynMV1r4XgiyYCYnCYHa6WpYDPnHI59pq4P7UUkO8hC54FVSbnZvNLnveRa4hmYv +d/7uRGEygJ0ifrHaaAztLHx5MgpMveJ4FTeQK0bjnOvTk0FBAkEA4v2QFc6em3bF +5CIvtmo44dpeX/89dzShGLheIAMlgrq2a7ZYi5L7G4NQH+nUKErObtqjGnhtZNjp +6El2B+LVfQJBANqTTz9bAIVJhnLpJz39YNXgtHQFuSNeqwI1b3XW5X037+2G1Fu5 +vEqc4rSn+auzqnPhvWaHL6e9s+BgxRUxELECQQCFLGLGiIzBhTOhHvWDPlrVFzrb +c3JqZ7REPpbqjS4KvuSb2UWlzbwJbkcqEhPo98qZeyjKNPFpg2HoaIEZK1o5AkA6 +fDA4Q6FgCMxw8vINJD9E+1tNsDpZvHsFnEa2wUmmt9D7PZiU8p3vWQtQS6ICz5BM +cZq8VG/r8lavMzWI9hgBAkBz92TmpDXjqsa9pJ2reqjc9EwvBwH0d4Up8jKrUotc +qFNXFtt+UCBRM358LYNHJlEmorc/EM9Tx89YeNGwSgXU +-----END RSA PRIVATE KEY----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pub b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pub new file mode 100644 index 0000000..d4eb315 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/mykey.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBzoqQuwZ9/eKMOU0OXWJ4ISj+ +2YEk8l4OmBoC9Y2wNEe4PcjzmCF/f9aZDyH6znh0G6gmb/yrTvuNLYkTUgiFNm0y +J2rSzlgmJZHkWykRkjKr4V04iAaHdU4ORre7Ms9eln7k8CeVQFpCjM51HOLkp8Ih +nAVrkOhSbHI4vxprbQIDAQAB +-----END PUBLIC KEY----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-cert.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-cert.pem new file mode 100644 index 0000000..5e5c591 --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDpDCCAowCAQEwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYD +VQQIDApDYWxpZm9ybmlhMRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFj +bGUxDjAMBgNVBAsMBU15U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG +9w0BCQEWHWFsZXhhbmRlci5zb2tsYWtvdkBvcmFjbGUuY29tMB4XDTE2MDYyMjEw +MDU1MVoXDTI2MDYyMDEwMDU1MVowgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD +YWxpZm9ybmlhMRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFjbGUxDjAM +BgNVBAsMBU15U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEW +HWFsZXhhbmRlci5zb2tsYWtvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWf +UyFZbRO/yHYLaZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkW +kvjtL/LCu4PHULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2 +LYPPbl0HjvpqsZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6 +H9VruJdCxjympVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOO +BkMfHnZcQQEKH/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABMA0GCSqGSIb3DQEB +CwUAA4IBAQBsdzkgdw95IBaynNIIRwqw8/tnzWW2UUlYiIqYCIKxdc+eWtbymNJT +4DXzckRSq9R2XUSHmdmFidLeYNdayzRPiecacG7wMo436et1ZLWXQ8DLe//lMJY1 +muj3As2W6/zY6jfGlMWQJ5zVujS5eqFdc4RQxxQfEEQG0NYmz7pXAnrpooE4yguf +nI3uzhQ3eoeTk13DngavdsxFVPSiYObHmcA7KahWYTNmzy70IR81KqMqn0/iOasH +d5Cdsq1A0slICSZ0VOndXIXAhByT5tuFlx/QT5fktobpp41CHDmV6+votEU4gpFC +qiSns89VrtSqY5YRiRs3Va5Oph7rk7r3 +-----END CERTIFICATE----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-key.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-key.pem new file mode 100644 index 0000000..2fa963f --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWfUyFZ +bRO/yHYLaZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkWkvjt +L/LCu4PHULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2LYPP +bl0HjvpqsZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6H9Vr +uJdCxjympVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOOBkMf +HnZcQQEKH/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABAoIBAQCpZM8UpJ1PvaaQ +057O9Cz8j5JCc4bJGIkdkd8tdBoTEsxPRLk0CUxbkBVlEkVxlpww8kfBtuYGcSQJ ++rZVpD2y4vaEgyWEYHbMi2Lq2e25mp8xWTxDFMJ+JzFsVvyIA8d2CpnJK/uo+Gku +QZ2xrJhlE0c1oPacJ1kO5h0B21uhLrB5SYq34/eK4kw7yd9n6ZjpvuRq3DBkR0Xp +I3B8k7BgMYrvQPGeiVddNhTZcVU16TYwPtALS0VaUXaHKtdIpxs7szsJPi9BqKRo +wKxT60zWJkB1FXg5WDaXRuofgV6RV/yTiZtBOt5OJubZbGTbs6Dq6R9UnvtvxhQm +UVNfuUcdAoGBAPlT0AHxb2bHDn6I1JC77a8P/Gnd28bY90Tb8zB1LFytkz+ASu/l +SAp9RAJi685uk10xI3ROpQPi/YSmt+gJSAKvbuPiD+iZomHhBZP32e8uedPtH2fU +W0Qdet3foYaU3rbn2NRU7c2JiZF0dKhKrLsNMd3aowIIfPV9uDI0Y6bzAoGBAMm2 +0ulyXqDx2GILgsPFjHBRKBiVB8yrWbJsCJhM5SmPLcpfYrtCEBudu5vS0wkxmtFM +OzWJZfxy6Smzv86ryEJ1OGlxr1yFwUE8UUuhmM/zbIfb37fwjfTXsi/EidmZhHRF +V8OsWC5YMvcDPaGipOp3QHGEHedrwjtY1xpIhCNXAoGBAI6qAo/aEqCAri6BJQBc +SDivsQLyy9sQMwzXvmOv6F5vg54GTtrOG1bFLrEe4UcRxojAoUTf61Q6Ak5xBzJi +fS3sLEuBAxFZN61CEOsGG1HdCiDVwe6reD2VkMR8PfTAImAOceetYRUG6gys0kOp +1wSBHFOR00xKhxYBhfBUQeE5AoGAOduU150Ug3mbJVRD57+MLtM4ewSUPiKQEdtn +A7haqvcEnV2KxkeGs0UlAOcR3Ts7OvlwqkSE2kpjkrCaPb+MK3PQAH65XAKJkhqo +/taVLGCYKsfofawkK6yK0aTAHYgpM+iH6fpPOMvXon5teSVbxFz8QRMyOKTyC2/K +EyHltPUCgYEAkB10UHWcF8r+QwlZvCfg0eFVcOyeU87ZMAfD4AQC0KC5p1Jb1cuK +iCilqEmZ2YAm24GyxPhX6QZy84S61QBXJdaW2gQy9yL8m3wHZEAlbgEf5UvNtX1X +WG51C7gvTuMvsONOe413734xOSSQ7NCQwXfi8b9ljnxEzL9KxIXBJhU= +-----END RSA PRIVATE KEY----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-req.pem b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-req.pem new file mode 100644 index 0000000..8724c3b --- /dev/null +++ b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/server-req.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIDDzCCAfcCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh +MRAwDgYDVQQHDAdSZWR3b29kMQ8wDQYDVQQKDAZPcmFjbGUxDjAMBgNVBAsMBU15 +U1FMMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEWHWFsZXhhbmRl +ci5zb2tsYWtvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAxHTdUYU/gt6nefynr3KQtWl0UsjsGfbeM0TQObZPDJWfUyFZbRO/yHYL +aZN1HrX4XCgRmHEAXSyoKLGyLlHlGGzzKdXZxYX/IC3hRJG2sHkWkvjtL/LCu4PH +ULxH/YAuXJ3jNrhJDNNRnZH/Bv3SPjLpieEF8bhvwy6hJBN3/Hc2LYPPbl0Hjvpq +sZIf5R2LzIuBbajGU2R/KK0yPGryYOC/EubCGyq1wNEaBitq0bh6H9VruJdCxjym +pVBHp6kJ6e4SjJyjdJEI/iF9Q4/hKmvwttNN6wMuN/2NBlntzNOOBkMfHnZcQQEK +H/XMxAKAVwyOoSCw1qMeZIo92vz1lQIDAQABoDIwFwYJKoZIhvcNAQkCMQoMCHBh +c3N3b3JkMBcGCSqGSIb3DQEJBzEKDAhwYXNzd29yZDANBgkqhkiG9w0BAQsFAAOC +AQEAkDQz/89WlMiJGEvRmauLXay8dLx+N9aTv8RlN0yHicmV/FkJXhJ6G97fdGZs +GrHN0vRC4mKf9svEl5hp2jmDicOf5AEfaLyJHWiRf99B/wNvKyeWPYsjHlYhPpRd +RjHB7h2uXsE6JaLL4XFjso2axbuCDJ9GsHiuAjgiMLfBZhaDxMe/OTflnYDJdVWo +EfMVbuZ3M0nq5ro/wE8KNQVw5aL6ZKadyxPSuoGT38dQXLj5d0mIvMtwmXdXHsmz +pylIIO7a4+9HhwQf7z80aF8sgNgO9/LMD9uEwp81GedQYHyPw+8z4aezVBkx9GFl +qyJgIiR6oeORktXuRfXtCm87kA== +-----END CERTIFICATE REQUEST----- diff --git a/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/test-cert-store b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/test-cert-store new file mode 100644 index 0000000..b86e928 Binary files /dev/null and b/mysql-connector-java-5.1.40/src/testsuite/ssl-test-certs/test-cert-store differ diff --git a/toto/.classpath b/toto/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/toto/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/toto/.project b/toto/.project new file mode 100644 index 0000000..fe9088d --- /dev/null +++ b/toto/.project @@ -0,0 +1,17 @@ + + + toto + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/toto/.settings/org.eclipse.jdt.core.prefs b/toto/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/toto/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/toto/bin/titi.class b/toto/bin/titi.class new file mode 100644 index 0000000..fadec65 Binary files /dev/null and b/toto/bin/titi.class differ diff --git a/toto/src/titi.java b/toto/src/titi.java new file mode 100644 index 0000000..75b24b8 --- /dev/null +++ b/toto/src/titi.java @@ -0,0 +1,4 @@ + +public class titi { + +}